AUGUST 2003
VOLUME II - ISSUE 8
The Magazine For PHP Professionals
Maintenance from the Outset Building a monitoring system into your scripts
An Introduction to Ming Create amazing Flash applications on-the-fly
An Introduction to cURL Discover the ins and outs of this indispensable tool
Transforming XML to PDF with the help of LaTeX Embedding Assembler in PHP
Case studies in Client/Server Applications
www.phparch.com
Take your PHP to new lows
Get Ready For php | Cruise See inside for details
March 1st - March 5th 2004
Plus: Tips & Tricks, Product Reviews and much more...
This copy is registered to: Liwei Cui
[email protected] ! W E N
Existing subscribers can upgrade to the Print edition and save! Login to your account for more details.
Buy now and save $10 off the price of any subscription† Visit: http://www.phparch.com/print for more information or to subscribe online.
php|architect Subscription Dept. P.O. Box 3342 Markham, ON L3R 9Z4 Canada Name: Address: City: State/Province: ZIP/Postal Code: Country:
php|architect The Magazine For PHP Professionals
Your charge will appear under the name “Marco Tabini & Associates, Inc.” The first issue of your subscription will be mailed to you in September, 2003. Please allow up to 6 weeks for your subscription to be established. *US Pricing is approximate and for illustration purposes only.
Choose a Subscription type: Canada/USA $ 81.59 $67.99 CAD ($59.99 $49.99 US*) International Surface $108.99 $94.99 CAD ($79.99 $69.99 US*) International Air $122.99 $108.99 CAD ($89.99 $79.99 US*)
Payment type: VISA
Mastercard
Credit Card Number: Expiration Date: E-mail address: Phone Number:
American Express
Signature:
Date:
*By signing this order form, you agree that we will charge your account in Canadian dollars for the “CAD” amounts indicated above. Because of fluctuations in the exchange rates, the actual amount charged in your currency on your credit card statement may vary slightly. †Limited time offer expires August 31st, 2003.
To subscribe via snail mail - please detach this form, fill it out and mail to the address above or fax to +1-416-630-5057
We’ve got you covered, from port to sockets.
php | Cruise
Port Canaveral • Coco Cay • Nassau
March 1st - March 5th 2004 Signup now and save $100.00! Hurry, space is limited.
Visit us at www.phparch.com/cruise for more details. Andrei Zmievski - Andrei's Regex Clinic, James Cox - XML for the Masses, Wez Furlong - Extending PHP, Stuart Herbert - Safe and Advanced Error Handling in PHP5, Peter James - mod_rewrite: From Zero to Hero, George Schlossnagle - Profiling PHP, Ilia Alshanetsky - Programming Web Services, John Coggeshall - Mastering PDFLib, Jason Sweat - Data Caching Techniques Plus: Stream socket programming, debugging techniques, writing high-performance code, data mining, PHP 101, safe and advanced error handling in PHP5, programming smarty, and much, much more!
TABLE OF CONTENTS
php|architect Departments
Features
5
10
Editorial
Transforming XML to PDF with LaTeX Stephan Schmidt
I N D E X
7
What’s New! 16
26
Ming & PHP Seth Wilson
Granted! Announcing the winners of the 2003 php|architect Grant Program
33 29
Sockets: Part 2 Eugene Otto
Datanamic DeZign for Databases 3 Peter James
65
Tips & Tricks
41
Peter James
John W. Holmes
69
Bits & Pieces
Grokking cURL
48
Maintenance from The Outset Graeme Foster
71
exit(0); Expect More From php|a Marco Tabini
57
Embedding Assembler in PHP Igor Gorelik
August 2003 · PHP Architect · www.phparch.com
4
EDITORIAL
E D I T O R I A L
R A N T S
I
could not be happier right now. As I sit here at my laptop, writing this editorial, by my side sits a glass of beer that I made from scratch, not quite a month ago. I’m inspired by the similarities that exist between the evolution of php|architect as a powerhouse publication and de facto standard source of PHP knowledge, and the creation of what is now a simple glass of lovely ale. I brew with real grain and real hops. None of this ‘just add water’ stuff. I’ve never done it, and I won’t. I suppose it’s fine if you just want a ‘quick and dirty’ beer. However, I have an overwhelming feeling that my integrity as a brewer would be sacrificed for ‘ease’ (I call it ‘laziness’), and it doesn’t sit well with me. I want honest beer. I want to know how the wort was produced, and where it came from. Indeed, I want to be intimately associated with the very essence of the finished product. For better, or for worse. I found this same spirit in the publisher of php|architect, who I’ve now come to know as both a good friend and close business associate, Marco Tabini. Marco understood my leanings toward things that were, for lack of a better term, ‘organic’ or ‘untainted’. I wanted material written by doers. I shunned several articles by professional writers whose writing lacked the spirit that we’ve by now become accustomed to at php|architect; the spirit of someone who has fought and lost battles on their way to great discoveries using PHP. Other writers, in comparison, appear to have ‘just added water’. We’ve made it a goal to avoid such articles. Of course, it is the ‘road less traveled’, but we feel it has made all the difference, and you’ve let us know that we’re right. Of course, brewing from scratch comes with its headaches. Brewing from scratch means more steps are involved, the materials are handled and transferred more often, and more opportunities exist for molds and bacterias to be introduced into the beer. While I’ve so far been able to avoid that, and produce very drinkable beer, it is not without its flaws. The one I’m drinking now took on a bit of a ‘chill haze’ after being bottled, for example; a minor problem that doesn’t affect anything but the aesthetic qualities of the beer. As I continue to brew and master the craft, I’ll inevitably fix the ‘chill haze’ problem, while introducing others. Eventually, it all works out, and you achieve ‘excellent beer’.
August 2003 · PHP Architect · www.phparch.com
php|architect Volume II - Issue 8 August, 2003
Publisher Marco Tabini
Editor-in-Chief Brian K. Jones
[email protected] Editorial Team Arbi Arzoumani Brian Jones Eddie Peloke Peter James Marco Tabini
Graphics & Layout Arbi Arzoumani, William Martin
Managing Editor Emanuela Corso
Authors Greame Foster, Igor Gorelik, Peter James, Eugene Otto, Stephan Schmidt, Marco Tabini, Seth Wilson php|architect (ISSN 1705-1142) is published twelve times a year by Marco Tabini & Associates, Inc., P.O. Box. 3342, Markham, ON L3R 6G6, Canada. Although all possible care has been placed in assuring the accuracy of the contents of this magazine, including all associated source code, listings and figures, the publisher assumes no responsibilities with regards of use of the information contained herein or in all associated material.
Contact Information: General mailbox: Editorial: Subscriptions: Sales & advertising: Technical support:
[email protected] [email protected] [email protected] [email protected] [email protected] Copyright © 2002-2003 Marco Tabini & Associates, Inc. — All Rights Reserved
5
EDITORIAL Such is the case with this magazine as well. We’re not perfect. We haven’t ever claimed to be perfect, and as a team, I don’t think any of us could ever foresee a day when there isn’t at least the slightest bit of ‘haze’. However, it is the attitude of this team that as long as there is not perfection, there is work to be done, and so we’re constantly busy, if not with fingers to the keyboard, then with developing ideas or analyzing weaknesses. It is this attitude, and the fact that it permeates every digital thread of the publication, that will earn us whatever critical acclaim may await us. Finally, beer and brewing is a learning process. I was fortunate enough that my buddy Matt was an already experienced brewer without a convenient location to brew, and I was a homeowner with a large back yard and a willingness to learn. Matt and I have known each other since childhood, and we work well together. We understand each other and relate to each other better than most other people we know. This makes for a great working relationship, partially because we grew out of the ‘polite friendship’ stage somewhere around age 14. We constantly question and debate. Our beer thus far has been better for it. This type of working relationship is invaluable in creating anything of any quality. In striving for quality of material in php|architect, anyone who has written for our editorial team will note that I take a decidedly different approach. The approach is largely the brainchild of Marco and I, who have somehow been able to achieve a vigorous and successful working relationship. Constantly questioning and debating each other, we’re always playing ‘devil’s advocate’ to insure that, in the end, we’ve done right by all parties involved. In the process we’ve learned what makes this monster we’ve created tick, and how to channel our efforts to add even more value to our readership. Our ability to come to a ‘meeting of the minds’ on all aspects of the publication has brought us not only to where we are now, but will influence the direction of the publication in the future – for better, or for worse. In reality, at the end of the day, it’s our ability to tell each other that our ideas completely suck that has made all the difference! But there is another ingredient in our recipe at php|architect. It’s the ‘recognition’ phase. Recognition of a person’s ‘highest and best use’.
August 2003 · PHP Architect · www.phparch.com
We seek people to join our team who we feel we can work with, and we eventually move everyone into a position where their interests and skills are put to work to produce the best ‘stuff’ that they can. As I’ve mentioned from the very beginning, editorial work is not my highest and best use. No matter how much I love editing and the editorial process, the reality is that a) I’m not a hardcore coder, and b) I’m not a hardcore editor! It is with these words that I am proud to pass the Editor in Chief torch to the very capable Peter James. Peter has accomplished much in his relatively short time as Senior Editor at php|architect, affecting both the outward appearance of the magazine, as well as the backend procedures for coordinating the editorial process. All by his own initiative, and all without a thought for anything but the good of the publication. Furthermore, as if this weren’t enough, he still constantly asks for more responsibility, and a greater role in the magazine’s creation. Finally, I came to the conclusion that the only way for Pete to do anything more was to take my job, which would allow me to be applied to my highest and best use as well. So from now on, it will be Pete’s job to try hard to give you all a reason to read the editorial column that I’m still convinced is a pretentious waste of paper (be it digital or ‘dead tree’ in form). I’ll not be far, mind you! I’m taking on a new role that will also produce some really cool and exciting new (but as yet unmentionable) ‘stuff’ in the not too distant future. I’m excited at the opportunity, though I’ll always make myself available on those sleepless nights when Pete can’t get some ‘damned Linux code’ to validate or something. I wish Pete a lot of luck, and thank him for the hard work he’s done, along with the rest of the production crew who have helped php|architect become what we are today, and what we will be tomorrow. I also thank all of the readers for the feedback and guidance along the way. ...until we meet again... brian.
6
3 php|Cruise ‘04 10 Transforming XML to PDF with LaTeX 16 Ming & PHP 33 Sockets: Part 2 41 Grokking cURL 48 Maintenance from The Outset 57 Embedding Assembler in PHP 7 What’s New! 26 Granted! 29 Datanamic DeZign for Databases 3 65 Tips & Tricks 69 Bits & Pieces 71 e x i t ( 0 ) ;
What’s New! S T U F F
PHP 4.3.3 RC3 PHP.net announced the release of PHP 4.3.2 release candidate 3. “This should be the last release candidate prior to the final 4.3.3 release. Please test this release as much as possible, so that any remaining issues can be uncovered and resolved.” Bug fixes include: • escapeshellcmd() can now handle quoted arguments • exit code lost when exit() called from register_shutdown_function() • methods misidentified as constructors • and much more.....
N E W
Visit php.net to download or view the change log.
Apache 2.0.47 The Apache Software Foundation and the Apache HTTP Server Project announce the tenth public release of the Apache 2.0 HTTP Server. This is a security, bug fix and minor upgrade release. Due to security issues, any sites using Apache 2 versions prior to Apache 2.0.47 should upgrade to Apache 2.0.47. Security issues: a. Fixed a bug in the handling of accept errors by the prefork MPM when handling accept errors, which could allow a denial of service attack if multiple listeners are configured. b. Fixed a bug in the optional renegotiation code in mod_ssl which could cause cipher suite restrictions to be ignored if optional renegotiation is enabled.
August 2003 · PHP Architect · www.phparch.com
c. Fix of denial of service attack in mod_proxy’s handling of DNS results. New features: Added support for a “prefer-language” environment variable to mod_negotiation” To download, visit Apache.org
ionCube and the Cerberus Encoder ionCube.com announced the release of the ionCube 3.0. The ionCube standalone PHP encoder is a high performance encoding solution for PHP, offering encoding of compiled code to deliver the maximum security and run-time performance for encoded file, and features to allow easy integration into build and release systems, and also websites for just-in-time software delivery. New Version 3.0 advantages: a. Customizing of Loader event messages b. Restricting encoded files to only cooperate with other encoded files that have certain ‘properties’. c. Improved encoding performance. In addition to Version 3.0, Ioncube has released the Cerberus Encoder. This is the same as the full featured encoder, but adds the ability to restrict files to a MAC address. For more information, visit ionCube.com.
7
NEW STUFF
Databases MySQL and PostgreSQL both announced new releases this month.
PHPNuke 6.7 PHPNuke.org announced the release of version 6.7. PHPNuke is a PHP based content management system. This newest version fixes XSS and other vulnerabilities and security bugs like path disclosure, and adds associated topics to the News module. There are many cosmetic changes and minor bugs fixed, and the Update folder was reorganized. You can get more information or download from PHPNuke.org.
PostgreSQL 7.3.4 PostgreSQL.org announced the release of their latest version 7.3.4. This version addressed a potentially serious (although rare) server startup failure that was recently reported. This release is critical for users of PostgreSQL version 7.3.3, and highly recommended for all other PostgreSQL users. The latest version can be downloaded from the PostgreSQL FTP site.
PHPEdit 0.7.1.131 PHPEdit.net announced the release of PHPEdit 0.7.1.131. It’s still a development version but takes one step further to the stable release. It contains proposal #2, #3 and #7 of the PHPEdit Community. PHPEdit invites all users to report problems or tweaks in 0.7.1.131 to the PHPEdit Community. PHPEdit also asks all users to vote for requests they want to be in the next release to the Community. Visit PHPEdit.net to download.
patUser 2.1.2 and 2.20 Beta PHP Application Tools announced the release of patUser 2.1.2 and 2.20 Beta New in this version: bugs, notices and warnings removed. Warning: this is the last version of patUser supporting patDbc, as we are switching to PEAR::DB. Version 2.2.0 is the new branch supporting PEAR:DB - but keep in mind that it is still BETA! If you are willing to test it, feel free to send your bug reports to gERD directly. “ For more information or to download, visit PHP-Tools.de.
MySQL 4.0.14 MySQL.com announces the release of version 4.0.14. This release is a maintenance release for the current production version and includes functionality such as: a. Enabled `INSERT’ from `SELECT’ when the table into which the records are inserted is also a table listed in the `SELECT’. b. Changed optimiser slightly to prefer index lookups over full table scans. c. `FLUSH LOGS’ now rotates relay logs in addition to the other types of logs it already rotated. For the full list of additions and bug fixes, or to download, visit MySQL.com.
php|a August 2003 · PHP Architect · www.phparch.com
8
Transforming XML to PDF with LaTeX
F E A T U R E
By Stephan Schmidt
Types of documents XML is commonly used to store several types of information. Not only do developers use it in their daily work to store configurations or define protocols, it is also a great way to structure your everyday documents, like letters, books, or articles. Even if they can be read with any text editor (XML is an ASCII format), XML documents are not the first choice when it comes to documents that you'd like to read in your free time. The mixture of tags, attributes and plain text may confuse you while you are trying to get to the actual information. XML was designed to be read by machines, not humans. XML does not contain any layout but only the structure of the raw data or content. An XML document consists of several tags (like HTML) which describe the information contained within the tags. Any program may use these tags to decide what to do with the text information between these tags. This could mean a program is able to prioritize parts of a document, if it had to create a search index or something similar. Humans tend to decide what to do with the information they read according to how it has been laid out. If you are reading an article, you will surely recognize that the headline has some significance in the context of the rest of the article, as it is printed in bold and large letters.
August 2003 · PHP Architect · www.phparch.com
So if information has to be accessed by computers and humans, the best solution would be to present both species the same information in different formats. The first choice of delivering information to a machine is nowadays XML, while PDF can be read with almost every client and operating system, and can easily be printed, which enables you to take the information to wherever you'd like without the need for a computer. As we're living in the age of automation, you will not want to create both versions of the information on your own but use your friendly neighbourhood webserver and scripting language (PHP) for it. This article will explain one way to achieve this without a single drop of sweat
Transforming documents To automate the generation of one version of the two required documents, you have to define which version should be created manually and which one should be generated by an application. This problem is solved in the blink of an eye, as the previous section explained REQUIREMENTS PHP Version: 4.x and above O/S: Linux Additional Software: LaTex Code Directory: N/A
10
FEATURES
Transforming XML to PDF with LaTeX
that XML can be read by machines, and if your webserver does not differ too much from the one we used, it definitely is a machine. So once we have made the decision to transform the XML document into a PDF document, we have to stop for a moment and think about how this transformation can be achieved. After choosing PHP as a programming language (as this is a article about PHP, after all) we realize that the task of distributing and accessing the document is now simpler, as both versions can later be accessed from anywhere in the world from our web server over the internet. If you are familiar with transforming XML documents you will probably be sitting at your desk yelling "Why the $@#! aren't you using XSLT? It was designed specifically for the task of transforming XML documents!". The answer is quite simple: XSLT was designed to transform an XML document to another XML document with a different structure. A PDF is not an XML application, and it is not even based on the ASCII standard. This means you will have a hard time transforming XML to PDF using XSLT. Some of you may still be sitting there wondering why we are not using XSL-FO, which is able to create PDF files and uses XML as its input format. We do not want to use XSL-FO as it is not very easy to use. Furthermore, it has already been discussed in other articles, and we are here to teach you something new. Call it an 'alternative method'. A better way to transform XML to PDF is to take a detour and use LaTeX. It is based on the ASCII character set, available on nearly every operating system for free and can easily be converted to PDF documents (or other printable formats).
LaTeX is based on Donald E.Knuth's TeX typesetting language. Development started in 1985 by Leslie Lamport and is currently maintained by the LaTeX3 Project . A typical LaTeX document looks like this
A short introduction to LaTeX
As there are a lot of tutorials on LaTeX available on the web (see the end of this article for useful links), we will only list the most important features and show some simple examples. LaTeX provides features for:
LaTeX is a document preparation system for high-quality typesetting. It is most often used for medium-tolarge technical or scientific documents, but it can be used for almost any form of publishing. LaTeX is not a word processor! Instead, LaTeX encourages authors not to worry too much about the appearance of their documents, but to concentrate on getting the right content. This means LaTeX is not edited in a WYSIWYG (What You See Is What You Get) editor. LaTeX documents can be created using your favourite editor, whether it is vi, emacs, Homesite or even Notepad (but not FrontPage). This also means that LaTeX documents can be created by any application that is able to create text files (and of course PHP is able to do this). If you want to view a LaTeX document, it has to be converted first. You cannot view it directly in your editor, or you will get the plain source code of your document, not the version including layout. This would be similar to trying to view an HTML document in vi or Notepad.
August 2003 · PHP Architect · www.phparch.com
\documentclass{article} \title{Dynamic transformations of XML to PDF with LaTex} \author{Stephan Schmidt} \date{April 2003} \begin{document} \maketitle We love XML, but everyone wants PDF. \end{document}
If you are familiar with markup or programming languages, you may already have guessed that this means: 1. The document is an article. 2. The title of the document is "Dynamic transformations of XML to PDF with LaTeX". 3. The article has been written by Stephan Schmidt ("Hey, that's me! Look mum, I'm on TV!") 4. It has been written in April 2003. 5. The document consists of a title, followed by the text "We love XML, but everyone wants PDF."
• Typesetting articles, technical reports, letters, books and slide presentations • Control over large (and we really mean large) documents • Control over sectioning, cross references, footnote, tables and figures • Automatic creation of bibliographies and indexes • Inclusion of images • Using PostScript or Metafont fonts
Basic usage of LaTeX LaTeX documents consist of commands, macros and environments as well as plain text. Commands always start with a backslash ("\"). If a command needs parameters, they have to be enclosed in curly braces ("{" and "}"), and if those parameters are optional, they have to
11
FEATURES be enclosed in brackets ("[" and "]") and separated with commas (","). Typical LaTeX commands look like this: \maketitle \footnote{I am a footnote} \documentclass[a4paper,twoside]{book}
It is possible to include comments in your documents, they have to start with "%" and end at the end of the line (like "//" in PHP). \documentclass{article} % This will be an article % This line is a comment and will be ignored later
Transforming XML to PDF with LaTeX • \label, \bibitem, \ref and \href to create cross-references • \includegraphics to include images • \begin{table}, \begin{itemize} to create commonly used environments • \tableofcontents, \listoftables and \listoffigures to create indexes • ... and many more So your first LaTeX document could look like this: \documentclass[a4paper,twocolumn]{article} \usepackage{german} % support for German umlaut \usepackage{hyperref} \title{Me and the superheroes} \author{Me, of course} \date{\today}
Environments are used to split the document into logical parts (like tags split XML documents). Environments always start with the "\begin" command and end with the "\end" command. \begin{document} % Place anything that is part of the document here \end{document}
Now you know enough to start creating your very own LaTeX document, and we will guide you through the needed steps. Each document has to start with the "\documentclass" command, which is being used to define what kind of document you are creating. The documentclass is responsible for the command set that is available in your document. E.g. if you are writing an article, there is no use for the "\chapter" command. Furthermore the "\documentclass" command is used to define the basic layout style, like two column layout or paper format. After this command you may load packages containing macro definitions using the "\usepackage" command. Packages include features for localization (different character sets), hyphenation, graphics or cross references. Usually you will now include meta information about the document, like the title, author or the date it has been created. Finally the "\begin{document}" command is used to indicate the start of the actual document. Within the "document" environment you may use any LaTeX command that is used to structure the document or include graphics. Common commands include: • \section, \subsection and \subsubsection to structure the document • \em to emphasize parts of the document • \item to create lists • \footnote (for footnotes, of course)
August 2003 · PHP Architect · www.phparch.com
\begin{document} \maketitle \tableofcontents \section{My relationship to Superman} \subsection{How it started} When I was twelve, Superman was my greatest hero. \subsection{Our relationship grew stronger} I first met him in person at the age of 16. \subsection{Everything has to end} When he died at the hands of {\em Doomsday}, I was really sad and devoted my life to Batman. \section{My relationship to Batman} My relationship to Batman started last week so there's not much to tell, yet. But I already know some of his friends: \begin{itemize} \item{\em Robin}, the Boy Wonder \item{\em Oracle}, the former Batgirl \end{itemize} \end{document}
OK, you have created your first LaTeX document, containing the tragedy of your life, but nobody is able to read it. What you probably want to do is to create a PDF document from it, print it and distribute it through your local comic book store. The next section will show you how.
Converting LaTeX to PDF Converting LaTeX documents to PDF is not something you do every day, but nevertheless it is quite easy, assuming you have the right software installed. Otherwise you have to install it first. If you are using Linux, there is no problem at all. LaTeX is included in most distributions, and, if not, it is available as an installable package for your favourite package manager. The package is most often called "tetex". You will find more information on the teTeX homepage. If you are a Windows user, you should download and install MikTeX. This should pose no problem, as MikTeX is distributed as a Windows installer. Now, as you are proud to have LaTeX installed on your system, there is nothing to hold you back from generating the PDF version of your document. All you
12
FEATURES
Transforming XML to PDF with LaTeX
need to do is to call pdflatex with the filename of your LaTeX document: pdflatex superheroes.tex
This creates several files in the folder where you have saved your LaTeX document: • superheroes.pdf is the PDF file you wanted to create • superheroes.log is a log file containing all log messages • superheroes.toc contains the table of contents • superheroes.out contains bookmarks for the PDF reader • superheroes.aux contains all data needed for cross references If you execute pdflatex, the application parses the LaTeX document from top to bottom and generates the table of contents, anchor files for hyperlinks, PDF bookmarks and other meta information. As this data should also be included in the PDF file, you have to call "pdflatex superheroes.tex" twice to achive the desired result. If you have done this, open the file superheroes.pdf with Acrobat Reader and you should see your lifestory rendered as a PDF document. If everything went like it should, you will probably see something like what’s shown in Figure 1. To get acquainted with LaTeX, visit a tutorial on the web and try fooling around with various LaTeX commands and how they are rendered as PDF.
ments consist of plain text, you may use any PHP function to modfiy them. The easiest way is to dynamically create a LaTeX document using "echo" statements like it has been done with HTML for ages, capture the result and transform it to PDF. The following snippet shows how it is done: \documentclass{article} \begin{document} Hi, my name is . \end{document}
Now open your favourite webbrowser (Mozilla, I hope), open the script and append your name as GET parameter "name". The URL will probably look like http://localhost/latex.php?name=Aquaman. If you view the sourcecode of the resulting page, you should see the complete LaTeX source including the name you entered in the URL. Save it to disk and transform it with pdflatex.
Figure 1: Your first PDF
Dynamic creation of LaTeX and PDF documents You may wonder why this article has been published in a magazine about PHP, and there has not been any PHP code so far. This will change with this section, where you will learn to generate LaTeX documents dynamically. As LaTeX docuAugust 2003 · PHP Architect · www.phparch.com
13
FEATURES Of course this is not what we initially wanted to do, as you still have to save the LaTeX document and transform it manually. To automate the process of saving the file to disk you should use PHP's output control and file system functions. To automate the "pdflatex" call you can use "system" or "exec". These functions are used to execute any command that is available on your server, just like you would execute it in the shell. Using "echo" to dynamically create LaTeX documents is the easiest - but also the ugliest - way to complete the task. If you are using LaTeX to create a printable invoice for customers in your online shop, you have to mix LaTeX (which contains content like your address) with PHP code (which is responsible for the logic of the invoice). The resulting files will be very hard to maintain, if changes to either the logic our the layout have to be made. This means you should use the same tools that are nowadays commonly used for creating dynamic HTML pages. Correct, we are talking of template engines. Just use your favourite template engine (of course, we recommend ours, patTemplate), create your LaTeX templates and let PHP fill them with any content you can to find. You may retrieve content from the user by supplying forms, get it from databases, or even XML files, which is the topic of the next section.
Transforming XML documents using patXMLRenderer As the title of this article is "Transforming XML to PDF with the help of LaTeX" we are now going back to where we came from and take a look at XML documents and how they can be transformed to PDF. In order to know how to transform LaTeX to XML we have to take a look at the gap between XML and LaTeX documents. Both languages split the document into logical expressions that can be nested as deep as you like. So the easiest way to transform XML to LaTeX is to define a LaTeX representation for each XML tag that is used in your documents and parse the XML document recursively. The result will be a LaTeX document with the same structure and content as the source XML document. As we do not wish to reinvent the wheel, we are relying on an existing software package called patXMLRenderer. There is online documentation for this available at the official website. This application makes use of a templateing engine (patTemplate) and allows you to define a representation for each tag by creating a template with the same name as the XML tag. In this template, you may access the content of the tag and all of its attributes. As the document is parsed recursively using a SAX based parser, all children of a tag are transformed before the tag itself is transformed. patXMLRenderer also allows you to include dynamic content by overloading namespaces with methods of PHP classes. This enables you to
August 2003 · PHP Architect · www.phparch.com
Transforming XML to PDF with LaTeX include content (SOAP requests, database content, text files,...) while the XML file is being transformed to the LaTeX document. A sample XML file, that can be transformed to LaTeX could look like this: <article title="Me and the superheroes, part 2"> <paragraph title="I lied to you"> When I was talking about Superman, I lied. He came back from the dead and rose to the glory he once had.
To transform this document to LaTeX (and later to PDF) you have to define representations for all tags used in the document: article, paragraph and imp. The LaTeX templates could look like this: <patTemplate:tmpl name="article"> \documentclass[a4paper,twocolumn]{article} \usepackage{german} % support for German umlaut \usepackage{hyperref} \title{} \author{Me, of course} \date{\today} \begin{document} \tableofcontents \end{document} <patTemplate name="paragraph"> \section{} \\ <patTemplate:tmpl name="imp" whitespace="trim"> {\em }
The resulting LaTeX template will be: \documentclass[a4paper,twocolumn]{article} \usepackage{german} % support for German umlaut \usepackage{hyperref} \title{Me and the superheroes, part 2} \author{Me, of course} \date{\today} \begin{document} \tableofcontents \section{I lied to you} When I was talking about {\em Superman}, I lied. He came back from the dead and rose to the glory he once had.\\ \end{document}
Now all you have to do is to call "pdflatex" and the XML file has been transformed to PDF and, as prom-
14
FEATURES ised, you did not lose one drop of sweat. We are currently developing an application that automates the process of transforming XML to PDF and allows you to combine several XML files through a graphical user interface. It will hopefully be available as a download together with patXMLRenderer from our website .
Common pitfalls When transforming XML to PDF you have to be cautious and avoid some common pitfalls. First of all, you have to tell patTemplate that you want to create LaTeX files instead of HTML files. This is done when creating a new instance of the class, by passing "tex" as first parameter to the constructor. When creating LaTeX output, variables have to be enclosed in "" instead of just "{" and "}" so they cannot be mistaken for LaTeX commands. Furthermore, you have to replace some characters in your XML files (quite similar to HTML entities). The application that we are planning to release will do this for you, so we'll just give a small overview of what has to be done in the background: • Some specialchars like "$", "{", "}", "_", etc. have to be quoted by adding a preceding
Transforming XML to PDF with LaTeX backslash • "..." should be replaced by "\dots" • \\ is used to mark the end of a paragraph, remember to include it in your templates. • ~ is used to explicitly create a space, similar to in HTML A list of all pitfalls would be nearly endless, given the possible level of complexity of LaTeX and XML documents. If you experience problems with any special characters you should take a look the LaTeX documentation to see if the characters you used have any relevance in LaTeX documents.
About The Author
?>
Stephan Schmidt is a web-application developer from Karlsruhe in Germany. He started coding PHP about three years ago and decided to join the Open Source community in 2001. He is a founding member of PHP Application Tools (http://www.php-tools.net) and author of patTemplate, patXMLRenderer, patUser and other classes.
Click HERE To Discuss This Article http://www.phparch.com/discuss/viewforum.php?f=37
Useful Links LaTeX Project: http://www.latex-project.org/ patXMLRenderer: http://www.php-tools.de/site.php?file?patXMLRenderer/overview.xml patTemplate: http://www.php-tools.de/site.php?file?patTemplate/overview.xml teTeX: http://www.tug.org/tetex/ MikTeX: http://www.miktex.org
Nobody...
As the publishers of Ian's Loaded Snapshot we know OSCommerce!
Hosts OSCommerce Better!
100's of OSCommerce powered sites rely on our years of experience with OSCommerce, direct
We Guarantee It! PHP, mySQL and Curl Optimized for OSCommerce Free Shared Certificate & Integrated SSL Server 20+ Contributions Pre-Installed on MS1 Release Web Mail and Web Based File Manager Full FTP and phpMyAdmin Access Free Ongoing Hands-On Support Web Stats by Urchin Reports Free Installation and Configuration
USE PROMO CODE: phpa Get an Extended Free Trial and Free Setup! August 2003 · PHP Architect · www.phparch.com
866-994-7377 or
[email protected] www.chainreactionweb.com www.chainreactionweb.com/reseller.
15
Ming & PHP
F E A T U R E
By Seth Wilson
T
he other day, while I was perusing through some books at a local bookstore (computer books of course), I noticed a book in the computer section about Ming. "Ming", I said to myself, "never heard of that". So I began to leaf through the book and discovered that Ming is a library of code with a set of wrappers that one can use to dynamically create .SWF format files (compiled Flash Movies) using PHP. "Oh, this is too cool", I can remember saying; however, since I had already blown my book budget on some others, the Ming book went back on the shelf. I did promise myself, however, to look into this Ming library a little bit more once I returned home. So I turned to the Internet (a fantastic invention, this Internet thing), and found plenty of documentation and examples to get my cranial juices flowing. I've finished my journey into cyberspace to seek out Ming, and I'd like to share with you my findings. Don't worry there won't be a three hour slideshow. Let's take a little journey into the world of Ming with PHP The first stop is to investigate the website for the Ming library, and this site is located at http://ming.sourceforge.net. Take the time to visit the site and scour through the function reference. There is a nice list here, and using these classes and their methods we can do just about everything you can do within the Flash authoring environment. You can even add Actionscript, which is the built-in scripting language inside Flash. I think this library is great, and a lot of nice work has
August 2003 · PHP Architect · www.phparch.com
been done here. My one beef is they could have named the objects better to represent the Flash "lingo". For instance, in Flash you would refer to an animated object as a "movie clip", but the Ming project leaders decided to call the class SWFSprite. A small thing, but nonetheless for a PHP developer beginning to learn Flash and Actionscript, this could be confusing when trying to get references and examples from the Flash community. Also check out the mailing lists archive located at http://www.opaque.net/pipermail/ming-fun/. If you are having a problem, chances are good that someone else has had the same problem, and there may be a work around or a solution there. Did you ever have to make up your mind? There are some cases where it makes sense to use the Flash authoring environment, and there are some cases where it would be nice to dynamically build a Flash movie. As we work through some examples you will begin to see that if you have an elaborate idea it may be best to draw it in Flash, especially if your idea contains plenty of animations. Even if you were to get REQUIREMENTS PHP Version: 4.1.1+ O/S: Any Additional Software: A browser with Flash Player installed, Ming 0.2a library, MySQL Code Directory: ming
16
FEATURES fancy and really optimize the PHP code, coding a Flash movie is a lot of work. Even still, I can think of a few applications where a dynamically generated .SWF file would be far easier to code than to draw. A couple of cool applications I can think of would work great for a website that offers advertising space. For a basic advertising package you could create a PHP web application where companies wishing to create a web banner can pick from a set of pre-coded Flash movies, and they include their own text and graphics. That way they can advertise without going through a lengthy design and development phase just for a simple web banner. Heck, they don't even have to know Flash. You could also have a dynamically created photo gallery or slideshow based on a user's query, and add some neat fade in/fade out or other effects. Or you could create custom e-cards where the content is delivered all in Flash. Actually, that's a great idea. Nobody else use that idea, ok? Make sure you check out a method in the SWFMovie class called streamMP3(). This one caught my attention immediately. I figured what's a better example than to make a Ming MP3 player, but first we need to learn some fundamentals. Ming compiles your PHP code into Flash version 4 movies, and at the same time any Actionscript you embed is compiled to Flash version 5. Why that is I don't know, so if you are looking to add Actionscript, please remember it must conform to Flash 5 Actionscript syntax, which is slightly different than the newest version, Flash MX. The very latest CVS Ming version 0.3a has support for Flash MX. However, this version is not released yet, so this article will focus on Ming 0.2a. If you do not presently have a browser with Flash player 6 you will need to download and install it. Flash player 6 is avail- Figure 1 able from Macromedia's website (http://www.macromedia.com) and is backwards compatible, so even though Ming creates Flash 4 movies, they will still be viewable with Flash player 6.
Ming and PHP should be a line just below this that begins with "extension_dir =". We want to change this line to reflect the location of our PHP extension directory where a file called php_ming.dll resides. If you have the default install of PHP (c:\php) on a Windows box it will probably be extension_dir = c:\php\extensions\
The second modification to the php.ini file is to uncomment the line that reads "extension=php_ming.dll", under the section labeled "Dynamic Extensions". Installing Ming on a Linux system is a whole different story. There are pre-compiled PHP modules on the Ming website, but these are compiled for older versions of PHP. That means you must build Ming support into PHP yourself, unless you can find a nice soul who has done the compiling for you. There is some documentation on how to do this located at http://ming.sourceforge.net. After installation, restart your Apache server and badda bing - you are ready to roll. Of course, if you do not want to cause yourself grief, make sure that Ming is working properly by writing a little script as in Listing 1 and running it. If Ming is installed properly you should be able to scroll down and see something similar to Figure 1. If you still have problems check out your Apache log file, it sometimes sheds some light on things. Listing 1 1
Installation The installation on a Windows machine is fairly straightforward as long as you are using a recent build for Windows. I am running PHP 4.3.1 on a Windows 2000 machine, and the Ming extension has already been compiled into PHP. All we need to do is enable Ming by modifying the php.ini file. Locate and open the php.ini file on your system and scroll down until you find a section labeled "Paths and Directories". There August 2003 · PHP Architect · www.phparch.com
17
FEATURES Flash conventions Before we get into using Ming and draw up some movies, we should go over some conventions with Flash. Flash movies have the origin of the movie at 0,0. This point is in the uppermost left corner. From this point the x value increases from the left to the right, and the y increases as we go from top of the screen downward. The other hot tip to know is that an object's depth in the movie corresponds to the order you added it to your movie. Have a look at Figure 2 to see what I mean. SWFMovie The first object we should look at in the Ming library is the SWFMovie object. This is the object that handles the creation of our movie, setting the frame rate, the overall dimensions, the background colour, and also handles the addition and removal of "items". These items can be shapes, other movies, movie clips (or sprites), buttons or text. If you want to see it (or hear it), you must add it. Listing 2 shows the setup code necessary for our movie. Don't bother viewing the movie at this point because all you will see is a blank white screen. As you can see from Listing 2, I like to add the code to instantiate the movie, and set all the settings right at the beginning. Then I like to jump down to the bottom and include two more lines to spit out the Flash movie to the browser for viewing. Listing 2 1 2 3 4 5 6 7 8 9 10 11 12 13
Figure 2
Ming and PHP
header('Content-type: application/x-shockwave-flash'); $m->output();
The first line will tell the browser what type of file this is, and the next line outputs the finished .SWF file to the browser. Alternatively, you can output the Flash movie to a SWF file by using $m->save($filename);
SWF_Shape Shapes are drawn using the SWFShape object, and shapes are the foundation to a Flash movie. You can take a shape that is drawn and use it for background fills, add it to a movie clip or sprite, or create a button with it. At the same time we can fill this shape with a colour, gradient or bitmap, as well. Enough talking already! Let's draw our first shape (see Listing 3). In this first example we are using a few methods of the shape object. First off, we create a new SWFShape Listing 3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
August 2003 · PHP Architect · www.phparch.com
//Starting X and Y values $x = 100; $y = 100; //Height of trangle $height = 50; // create a new Shape $s = new SWFShape(); //Set the line width and colour $s->setLine(2, 0, 200, 0); // create a new Fill red, green, blue and alpha $f=$s->addFill(0,255,0,255); // set the fill to the new fill just created $s->setRightFill($f); // Start drawing the Shape $s->movePenTo($x, $y); $s->drawLineTo($x-(0.5*$height),$y+$height); $s->drawLineTo($x+(0.5*$height),$y+$height); $s->drawLineTo($x, $y); // add it to the movie $m->add($s); $m->nextFrame(); // and output header('Content-type: application/x-shockwave-flash'); $m->output();
18
FEATURES object, which in this case we are calling $s. Then we use the setLine() method to set the line thickness (the first argument) and the colour (the last three arguments - red, green and blue). In this case, we specify a dark green. The line $f=$s->addFill(0,255,0,255);
adds a light green fill (the first three arguments) to the outline we are about to draw. The last argument to addFill() is the "alpha" of the fill. Alpha can be best explained as the opaqueness of the fill, with 0 being transparent and 255 being fully opaque. The setRightFill() and setLeftFill() methods are best explained in the Ming documentation. Now that we've told Ming what we want to draw, we can now give the commands to draw. The best way to visualize this is to think about actually drawing on paper by hand. First you move your pen to a spot, put it down on the paper then draw a line to another point. This is the exact same process used by the movePenTo() and drawLineTo() methods.
Ming and PHP One note that I should mention here is when you are coding any Ming project, step by step is the best plan of attack. Make sure that you have display_errors on and, as a rule of thumb, don't put your head down and bang out three hours of code, because you'll spend the same amount of time, or more, debugging. If you run Listing 3 in your browser, you should see something similar to Figure 3. SWFGradient Instead of a straight colour fill, you can also fill a shape with a gradient using the SWFGradient object. A gradient is basically a smooth transition from one colour to another. There are two types of gradients: a linear gradient, which is a line; and a radial gradient, which is circular. Listing 4 shows a basic gradient fill. The code is setup to show the linear gradient first. Just comment out the first line calling addFill() and uncomment the line below it to see the radial gradient. Make sure you run the script in your browser to have a look at what it does.
Figure 3
August 2003 · PHP Architect · www.phparch.com
19
FEATURES
Ming and PHP
SWFButton Buttons in Flash act as a user interface with the movie. You create a button with Ming by using the SWFButton object. A button is basically a specialized movie clip. Once you create the object, you can add previously-drawn shapes to define the shape and size of the button. Without a shape, a button will not be seen by your users. A basic button has up, down, over, and hit states, and you can define different shapes for each button state to create some neat effects. One downfall with Ming is you cannot add text or other shapes to your button. You can work around this by first drawing the button, then drawing another shape over top. We will use this technique in our MP3 player. Have a look at Listing 5 (included in this month’s package), and then run this script in your browser to see the button in action. You can tell a shape is a but-
ton, because when you mouse over the button the mouse icon will change from an arrow to a hyperlink icon. The future looks painful. Let's create a class. drawShapes class I hope you're starting to see that if you have a movie with a bunch of buttons and shapes, it will become very painful to code. The last example has over fifty lines just to draw the shapes for one button. If you had even five buttons in your movie, your code would look very messy. We could create some functions that will do some of the drawing, but this is still cumbersome because you either have a function with a huge number of arguments, or it would really only save a couple of lines of code. There is a solution to this problem: another object, of course. I developed this class (shown in listing 6 - also included in this month’s package) to aid in the drawing of shapes. Feel free to add more
“... don't put your head down and bang out three hours of code, because you'll spend the same amount of time, or more, debugging."
Listing 4 1
August 2003 · PHP Architect · www.phparch.com
20
FEATURES shapes as you like. Have a look at Listing 7, which shows the previous example, this time using the drawShapes object. That looks a lot nicer, and is easier to follow. I think so, anyway. Animate…good times come on!!! (Or is that celebrate?) Everyone loves (or hates) Flash movies because of the animations. With the Flash authoring tools, you have a nice interface in which to draw your beginning and end shapes in different frames on the timeline, after which Flash will happily perform all the drawing in between. Not the case with Ming. Now you are the person that will happily draw all the animation in between. Really, it's not that bad. If we use our wits, and some control structures, we can minimize some of the work. We can use the following methods to animate our Listing 7
Ming and PHP shapes. • move(x,y) and moveTo(x,y) • scale(x,y) and scaleTo(x,y) • rotate(degrees) and rotateTo(degrees) • skewX(x), skewY(y), skewXTo(x), skewYTo(y) • and you can also transform colour After looking at Listing 8 (included in this month’s package), which shows some of the methods used to animate our triangle, run the script to see the little guy in action. Also note that you must advance the movieclip or movie frame-by-frame manually using the nextFrame() method. You can also fully animate shapes using only Actionscript, but that's beyond the scope of this article.
1
August 2003 · PHP Architect · www.phparch.com
Let's build this MP3 player I think we have most of the tools now to build a fully functional MP3 player, and any other tools we will pick up on the fly. I have told a little white lie, it's not really an MP3 player, but rather the player plays a SWF file with an embedded MP3. Either way, the users listening will not be able to download the track. There are two parts to this player. The first part will be a PHP script that will allow a user to upload an MP3 file. The second part is the user interface that will have the play, stop and pause buttons, volume control, and selectable playlist. Upload and add script MP3 files will be uploaded and converted to a SWF file using a separate PHP script from the player. The script, called upload.php, uses a library developed by someone else (no sense re-inventing the wheel) that will read the MP3 header information and the ID3 tag. The header information is needed to grab the length of the song. It is absolutely imperative to retrieve this information because when we embed the MP3 into a SWF file we need to make sure the
21
FEATURES
Ming and PHP
SWF file has enough frames to encapsulate the entire song. The ID3 tag information is stored into a MySQL database and is used by the player to display track information. The HTML form is called addplaylist.html, and is shown in Listing 9. It is the front end for the upload.php script, shown in Listing 10 (included in this month’s package). We need to modify the php.ini file again to make sure the file is uploaded and processed properly. Open up your php.ini file and search for the line that reads upload_max_filesize = 2M
and change to something appropriate like upload_max_filesize = 8M
We also need to change the input and execution times to accommodate the larger sized files. Change these settings to max_execution_time = 90 max_input_time = 90
You may need to fiddle with these settings if you are uploading files larger than 8 megs. As I mentioned, the script that handles the processing is called upload.php and can be seen in Listing 10. The script performs some basic validation of the uploaded file, strips out the ID3 tag and stores that as a new database record. It then processes the MP3 by embedding it into an SWF file. The Ming library is used for the last step, and the newly created SWF is saved to
the local disk. Make sure you have permissions set for the directory to be able to write files, or you could change the script slightly to suit your tastes. There are a few variables in this script that you may have to change to suit your system. The first variable is $uploaddir. You will have to change this path to reflect where your player script resides. The other variables that could be changed are $user and $pwd which are used for accessing the MySQL database. I have run into some problems uploading MP3 files that have no ID3v1 information. I know this is a pain but try and make sure there is ID3v1 information in the MP3's you upload. For those worried about security (and we all should be), having any Regular Joe upload a MP3 file to their server would keep them up at night. Think of this script as an administration tool then, don't put it online, and sleep well. The player interface (See the complete listing for the player in Listing 13, in this month's package) Let's make the interface look cool. I thought that using a radial gradient that transitions from black to white transparent, and placing the center of this gradient in the four corners of the rectangle shape would look really neat. The only way to do this is to create four identically sized shapes and place one gradient in different corners. Run Listing 11 (also included in this months package) in your browser to see the effect I've been talking about. Next up is to add some text fields. We will use three text fields for our player. One text field will contain information on which track is currently being played, another will be a status bar and the last will serve the
Listing 9 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
Add Song To Ming Mp3 Playlist <meta name="generator" content="editplus"> <meta name="author" content=""> <meta name="keywords" content=" "> <meta name="description" content=""> Add Song To Ming Mp3 Playlist
Add this MP3
August 2003 · PHP Architect · www.phparch.com
22
FEATURES
Ming and PHP
purpose of a scrolling playlist. I have not covered how to add a text field, so I will do it now. A text field cannot be animated but it can be dynamically changed during the movie's timeframe. When you instantiate the SWFTextfield object, $t = new SWFTextField([$flags]);
you can specify the behaviour of the text field by specifying different flag arguments. The flags are listed below from the Ming documentation. You can set multiple flags by using the bitwise OR operator ( | ). Valid text field flags:
SWFTEXTFIELD_NOEDIT
indicates that the field shouldn't be user-editable
SWFTEXTFIELD_PASSWORD
obscures the data entry
SWFTEXTFIELD_DRAWBOX
draws the outline of the text field
SWFTEXTFIELD_MULTILINE
allows multiple lines
SWFTEXTFIELD_WORDWRAP
allows text to wrap
SWFTEXTFIELD_NOSELECT
makes the field non-selectable
You can also specify the height, width, type and size of the font using the methods of the SWFTextField object. You can even specify the instance name of the text field so that you may populate it dynamically using Actionscript. The status bar text field for our player will be named "status", and will be the default, non-editable text field. The second text field, which will contain track information, will be named "trackinfo". The scrolling playlist will be non-editable, non-selectable, multiline, and will contain HTML code. This is an undocumented flag for
an SWFTextField, called SWFTEXTFIELD_HTML, that will make our text field render HTML code. We want to render this text field as HTML so that each track will be listed with a hyperlink which when clicked on will load that song into the player. Once you see you see the Actionscript it will make more sense. Now we can add our buttons to play, pause, and stop, as well as volume controls and scrolling buttons. The buttons for track control will be circles, and the volume and scroll buttons will be triangles. The drawShapes class comes in very handy. Have a look at the listing to see the addition of the buttons. Let's make our buttons a cool X-box green, with dark green borders With buttons, come actions Of course, now that you have a button, you want to be able to do something once the user clicks on it. You can add Actionscript to a button, movie clip or the movie itself. All actions are added using the SWFAction object. You can add many Actionscript statements inside one add SWFAction command, just make sure to end each Actionscript statement with a semi-colon. It's very simple to add Actionscript to a button. The object has a method called SWFButton addAction(), and the first argument we pass to this method will be an SWFAction object complete with the Actionscript. The second argument is a flag to tell Flash after which button event this code should be called. $b->addAction(new SWFBUTTON_MOUSEUP);
SWFAction("_root.mp3.play();"),
Have you had your PHP today?
• Subscribe to the PRINT edition • Subscribe to the ELECTRONIC edition
Visit us at http://www.phparch.com and subscribe today. August 2003 · PHP Architect · www.phparch.com
php|architect 23
FEATURES
Ming and PHP
For the volume controls, we have a few lines of Actionscript. When the volume up or volume down buttons are clicked the Actionscript global variable called Vol will be incremented or decremented accordingly, then a new Sound object will be created, and will be passed this new volume setting. The scroll up and scroll down buttons also have some Actionscript code. They access a property of the text field called 'scroll'. If the playlist has more items than what can be seen in the text field, these buttons will allow the user to scroll up or down to see more items. This scrolling action is done by incrementing or decrementing the scroll property of the text field.
“For those worried about security, having any Regular Joe upload a MP3 file to their server would keep them up at night.”
Add some fancy touches with MySQL We are going to also harness the power of MySQL, and use it to hold information about the MP3. Ultimately, we are using an SWF with an embedded MP3 file, so we cannot grab any information about the MP3 unless it is stored somewhere else. We are going to use the same database to populate the scrolling playlist, and also to retrieve information about the current track being played. Listing 12 shows the SQL file to import the table structure into a MySQL database. Let's call the database "mp3stream", and import this information into it. Now, last but not least, we can now add a movie clip
or SWFSprite that will act as a container for the MP3 embedded SWF file. Let's call this movie clip (sprite) "mp3", and leave it empty for now because we are going to add the MP3 dynamically using Actionscript code. Actionscript for the movie There are only a few lines of Actionscript to get this whole movie going (see the bottom of Listing 13). The first thing we do is set the initial volume using the previously mentioned Vol Flash variable. Then we load the SWF movie containing our MP3 using the loadMovie function into the empty movie clip called "mp3". Next, we create a new Sound object to handle the volume initially. Lastly, we add the track information into the textfield. That's it. We are ready to press play….so go on, press play. All of this code is neatly commented and can be seen Listing 12 # # Table structure for table `mp3info` # CREATE TABLE mp3info ( mp3ID int(11) NOT NULL auto_increment, mp3Filename varchar(255) NOT NULL default '', mp3Length int(11) NOT NULL default '0', mp3Title varchar(255) NOT NULL default '', mp3Artist varchar(255) NOT NULL default '', mp3Album varchar(255) NOT NULL default '', mp3Year varchar(10) NOT NULL default '', mp3Comment varchar(255) NOT NULL default '', mp3Genre varchar(255) NOT NULL default '', PRIMARY KEY (mp3ID) ) TYPE=MyISAM;
Been handed a PHP project and don't quite know where to start? Itching to learn PHP in a classroom environment? Tap Internet has trained hundreds of PHP developers over the past 2 years, and is the largest independent PHP training organization in the U.S. We provide complete PHP training solutions for all levels, including on-site courses. PHP Beginner- $999
PHP Advanced - $1999
LogiCreate - $1299
• • • •
• • • •
• • • •
Next class: Sept 22-24, 2003 A 3 day course providing beginners a solid foundation in PHP. • Basic programming concepts Dynamic HTML pages Loops/control structures Array handling File system management
Next class: Sept 15-18, 2003 A full 4 days with all topics from PHP Beginner plus: • Session management Objects PDF creation Database usage Security issues
Next class: Sept 29-31, 2003 A 3 day course covering the LogiCreate application server. • Installation User management Custom permission systems Rapid application development Error handling
All classes feature hands-on instruction with friendly experienced teachers. Each student is assigned an individual workstation, and class sizes are limited to increase personal instruction. Lunch and snacks are also provided. Call 1-866-745-3660 or 734-480-9961 to book your seat now or visit http://www.tapinternet.com/php August 2003 · PHP Architect · www.phparch.com
24
FEATURES in Listing 13. The finished player can be seen in Figure 4. How to make this better To make this script a full blown application, we would have to add a few touches to really make it polished. A pre-loader would be essential to show users on slow connections that the MP3 is loading. A Flash version check would also be really slick. That way you can direct users that do not have the Flash plug-in installed to Macromedia's site. The ultimate touch would be a Winamp-style visualizer with some cool animations that would really make your CPU smoke. You could change the architecture of this script a small bit, and have the script embed the MP3 into the SWF on-the-fly (rather than creating it up-front). This would limit the size of media files on your server, but will slow the performance of the script significantly. A second step in the file upload procedure allowing users to add or change the ID3 information before saving to the database would be nice feature, I think. Also adding more shapes to the drawShapes object would be nice. Hearts, stars, more geometric shapes like pentagons, hexagons and the like would be really helpful, as well. So what is this script really good for? I think this script would be great for anyone who wants
Ming and PHP to offer some audio content on their website. If you are a motivational speaker or a local garage band, and you don't want people to grab your content this might be a solution for you. Since this player uses Flash, you are going to reach nine out of ten Internet users without them having to download a separate plug-in. Acknowledgements I would like to thank a regular contributor to the Ming mailing list, Armel Grignon at mingshop.arpane.net, who indirectly, through his posts to previous queries, helped me immensely. I would also like to thank the author of the mp3.inc.php script. I searched high and low for the author, but came up empty. Thanks for saving me some work!
About The Author
?>
Seth Wilson runs his own computer consulting business based in Waterdown, Ontario, Canada. He enjoys breaking and fixing computers, writing code, playing, watching, and talking hockey and rollerblading with his wife Trish
Click HERE To Discuss This Article http://www.phparch.com/discuss/viewforum.php?f=38
Figure 4
August 2003 · PHP Architect · www.phparch.com
25
Granted! Announcing the winners of the 2003 php|architect Grant Program
A
fter months of patient wait, hundreds of submissions and hours of deliberation, it’s finally time to pick two winners for our Grant Program. The choice was made particularly tough by the fact that of the over 200 submissions we received, many were either original applications that broke new ground, or established platforms that were looking to expand in new directions. However, having a clear goal helped us a great deal: we wanted to find (and fund) two projects that were capable of demonstrating that PHP is a worthy platform in highly demanding environments and industries. In the end, we had to make a choice... so, to quote Zapp Brannigan from Futurama, “without further adieu”, here are this year’s winners! Nurse—I need some PHP... stat! There are few industries that are as demanding and exacting as healthcare. After all, a mistake of any kind could mean serious harm to a patient—this is an environment in which the expression “blue screen of death” takes on a whole new (and much more ominous) meaning. We were quite surprised, therefore, when we found out that a group of developers were working on a hospital administration platform based entirely on PHP, called Care 2002. After a chat with the team leader, and a quick visit to their website we (http://www.care2x.com) were convinced that this team was on to something big. A hospital implementing a Care 2002based management solution would be a great vote of confidence for PHP from an industry sector where “tolerance for error” is usually in the same sentence as “zero”. “Care 2002 is software for
hospitals and health care organizations, designed to integrate the different information systems existing in these organizations into one single efficient system,” says team leader Elpidio Latorilla. “It solves the problems commonly found in a network of multiple programs that are incompatible with each other. It integrates almost any of the services, systems, departments, clinics, work processes, data, communication, etc., that exist in a hospital. Its design can even handle non-medical services or functions like security, maintenance, surveillance, etc. It is also user-configurable, modular and scalable.” Care 2002 uses standard SQL. The use of a single data format solves the problem of data redundancy. With its database abstraction layer, it can support different databases. The entire system is web-based, and all its functions can be accessed with a common web browser. Therefore,
there is no need for special UI software on the client side. Since all program modules are processed on the server side, updates and extensions do not require changes on the browsers, eliminating the need for network interruptions and downtimes. According to Elpidio, his team chose to use PHP for their project mainly because of its stability, portability and ease of use. Even though it was started in 2000, Care 2002 is still in beta—for the most part, due to the grueling requirements imposed on it by its intended audience. Still, many parties have expressed interest in implementing it at their healthcare facilities once it exits from its long beta testing cycle. We’re happy to announce that our grant will be put to good use. As the project’s final stable release comes close, the team is planning to do numerous presentations, and our contribution will go towards the purchase of hardware needed for these events. Assisting the success of a project like Care 2002 is what makes the Grant Program so important to us.
work management tools written entirely in PHP. According to team leader Javier Szyszlican, it started as an experiment in 2002 and it has rapidly become a widely-used open-source project, fueled by the power and ease of deployment of PHP. JFFNM, which is used in about 300 mid-sized networks, contains a total of over 16,000 lines of code. That may sound like a lot, but given its functionality, we were surprised that so little was needed to make it function. JFFNM supports all sorts of network management functions, such as SNMP integration, Tacacs+ Authentication and Accounting, Syslog Logging with PCRE Matching, SNMP Trap Handler, TFTP Configuration, Smokeping, MSyslog and Syslog-NG. “Using PHP allowed us to reuse all possible code,” says Javier. The JFFNM team has a long list of needs that our grant will help to cover, from new hardware to actual development time for the introduction of new features, such as network auto-discovery. Congratulations to the entire team, and keep up the good work!
Let’s manage a network... just for fun The second winning entry in our program comes from a group who’s trying to work in what is perhaps one of the more demanding sectors of I.T.: network management. Just for Fun Network Management (JFFNM), available at http://jffnms.sf.net/, is a complete suite of extensible net-
php|a
More screenshots at http://jffnms.sourceforge.net/shots.php.
The designers of PHP offer you the full spectrum of PHP solutions
Serve More. With Less. Zend Performance Suite Reliable Performance Management for PHP
Visit www.zend.com for evaluation version and ROI calculator
Technologies Ltd.
Datanamic DeZign for Databases 3
R E V I E W
By Peter James
Quick Facts Price: Single User License $229.00 5 User License $916.00 10 User License $1603.00 Free Trial: 30 day unrestricted free trial System Requirements "DeZign for Databases" is available as a 32-bit application. The product is available for Windows 95 or later, Windows NT, Windows 2000 and Windows XP. "DeZign for Databases" requires a minimum of 16MB RAM and will run on a 486 processor. Disk space requirements is 6 MB. Supported Databases
Oracle Interbase IBM DB2 MySQL dBase Paradox MS SQL Server MS Access SQLAnywhere
Sybase Informix Pervasive Advantage DB DBISAM FoxPro CA Clipper PostgreSQL
Homepage: http://www.datanamic.com
August 2003 · PHP Architect · www.phparch.com
F
or months now I’ve been waiting for Dezign 3. I’ve faithfully used Dezign 2 to design almost every database I’ve created in the last two years. Its combination of support for multiple database servers, feature list, and price were unbeatable. Unfortunately, though, I’ve been recently searching for a replacement. Dezign 2 was two years old, with no major updates, and was beginning to show its age. Much to my dismay, there are no products even close to its price point. Finally, earlier this month, Dezign 3 was released. It features a shiny new interface and lots of new features. I was eager to play with this new version, and I thought I would share my experience with you. What is Dezign? In their own words, Datanamic Dezign “... is a database development tool using an entity relationship diagram. It visually supports the lay out of the entities and relationships and automatically generates SQL schemas for most leading databases.” I couldn’t have said it better. Dezign allows you to visually design your database schemas. If you’ve ever worked with a large CMS like Drupal or PostNuke, you might be vaguely familiar with the enormous database structure lurking beneath. Imagine trying to design that structure in phpMyAdmin. Simply specifying the final layout of a system like this could be a traumatizing experience. If you do any medium- or large-scale work with databases, you know that having a visual representation is imperative, especially in the design phase. Using a tool like Dezign, I can try out my wacky database layout
29
REVIEWS ideas with little effort. I couldn’t do that easily on the command-line, or using a tool like phpMyAdmin. Plus, being a visual person, I enjoy the abstract representation that modeling tools like Dezign offer. Without further delay, let’s take a look and see if this new version of Dezign stacks up. Installation and Configuration I downloaded the 30-day trial from the Datanamic website, and ran the installer with no problems. Although there are plenty of ways to configure the interface, no initial configuration was necessary. On first run, a sample entity-relationship diagram (ERD) came up automatically. Dezign ships with a number of example files, which could be very helpful to someone just starting out. One thing to note for anybody upgrading from Dezign 2 to Dezign 3 is that it will automatically convert your v2 files to v3, letting you know what it had
Datanamic DeZign for Databases 3 problems with. Note that this conversion is irreversible, although Dezign helpfully (and thankfully) saves a backup copy of your v2 file. Designing with Dezign There’s certainly a lot of functionality in Dezign 3. As you start to build your ERD, you are afforded a great deal of control over your design. As an example of the plethora of options, let’s look at adding a table (or entity). Upon specifying that you want a new table, you have the option to add attributes (or fields), indexes, triggers, table constraints, extra scripts, a detailed description, and a set of TODO’s. And each of these options has it’s own set of detailed options. Amazingly, the huge amount of configurability does not detract from the simple tasks. If you just want to get your structure in there, it’s very fast. If you want to take your time and set everything up with proper
Figure 1
August 2003 · PHP Architect · www.phparch.com
30
REVIEWS defaults, constraints, and indexes, then it’s going to take a little more time. A really cool feature is that you can maintain multiple diagrams (or views) of your main schema. This includes subsets of tables, meaning that you can have a main diagram of your whole ERD, and then provide more concise and more documented diagrams of the user management system, or the commerce system, all the while maintaining only one physical table set. Adding relationships between entities has become really easy in Dezign 3. One-to-many relationships are doable in two clicks and a drag. Many-to-many relationships require a comparable amount of work, but automatically create the association mapping table, which saves a fair bit of effort. The help system in Dezign is quite thorough, and even includes a section on database theory. I must mention, though, that the Dezign help system doesn’t contain a single screenshot or icon. This may sound nit-picky, but – again – I’m a visual person. Explaining the options on a dialog or a toolbar is fine, but I like pictures. Especially ones with arrows. Trying to support as many database systems as Dezign does is an amazing feat, especially if its done well. I can’t comment on the other RDBM systems sup-
Datanamic DeZign for Databases 3 ported by Dezign, but PostgreSQL support is actually quite good. There are a couple of quirks here and there, but nothing that will prevent you from being miles ahead of where you were. And some of these quirks can be immediately addressed by one of Dezign’s coolest features: the template system. Dezign allows you add your own new database type. I found this interesting, and poked around trying to find out how it works. Dezign maintains a list of templates for each database type that it supports. There are a handful of files necessary to add a new type, but usually it is acceptable to just copy and modify files from other database types. Besides the configuration directives and data type specifications, you also must provide a template file. The templating language is a subset of Pascal, making it pretty easy to work with. Although there is zero documentation (that I could find) on the templates, I found I could make significant changes inside of about 20 minutes of first looking at the files. Of course, you probably want to back up your original copies first, so you don’t irreversibly change something important. While the support for triggers and procedures is there, it’s pretty basic. There is a semi-highlighting code editor, but it doesn’t do much more than that. In
Figure 2
August 2003 · PHP Architect · www.phparch.com
31
REVIEWS
Datanamic DeZign for Databases 3
order to add triggers to PostgreSQL using Dezign, I’d have to pretty much not use the trigger interface, and just stick with the general SQL editing box. But I’m going to say that this is really just a function of having to support multiple database systems. A very simple report generator has been incorporated into Dezign 3. This allows you to specify complex templates (again, using the Pascal subset), and build your own reports. The HTML reports require a stylesheet, and this allows even more flexibility in your reports. Dezign offers the ability to print your ERD, as well as exporting it to any of a number of image types. This is nice for presenting your design to pointy-haired bosstypes. Dezign helps you manage document versioning. The system is pretty simple when compared to CVS, or even RCS, but it is nice to have it built in. Speaking of CVS and RCS, Dezign’s file formats are all plain text, including some XML. This means that standard version control systems will work properly and usefully. What I liked I really like the easy specification of table relationships. That’s going to save me lots of time. The template paradigm is awesome. Datanamic has really taken a step forward and put the power back where it belongs. If you can offer this sort of extension system to your users, your product will almost certainly enjoy a longer and happier life. The diagram system is great. Being able to cut up your larger ERD into small bite-sized pieces is invaluable when it comes to explaining it to other people.
What I didn’t like I didn’t like the help system; I found it was plain. A help system may not seem like an important bell to ring. I mean isn’t the help content the important part? Yes and no. The help system is what acquaints users with your product. If your help system is not strong and engaging, people are not going to learn your product properly. I didn’t like the lack of documentation on the templating systems. Perhaps this is in the works, but it is going to be noticed and people are going to want to use it. We need documentation. The support for triggers and procedures leaves much to be desired, at least for me. But again, this comes down to supporting many database types.
In conclusion After using Dezign 2 for two years, and really enjoying it (for the most part), I’m really liking Dezign 3. It has a very nice interface, lots of features, and an eye for the future. I suggest you download the evaluation copy, and see if it can make you more productive. I give Datanamic Dezign for Databases a 4 out of 5.
php|a
Connect with your database Publish your data fast with PHPLens PHPLens is the fastest rapid application tool you can find for publishing your databases and creating sophisticated web applications. Here’s what a satisfied customer, Ajit Dixit of Shreya Life Sciences Private Ltd has to say: I have written more than 650 programs and have almost covered 70% of MIS, Collaboration, Project Management, Workflow based system just in two months. This was only possible due to PHPLens. You can develop high quality programs at the speed of thinking with PHPLens
Visit phplens.com for more details. Free download.
August 2003 · PHP Architect · www.phparch.com
32
Sockets: Part 2
F E A T U R E
By Eugene Otto
In one of last month's articles, we talked about building a client/server system with PHP and xinetd. This month, we'll run through a couple of case-studies showcasing a couple of ways to employ this very powerful technique. We'll start with a quick review.
Review There are two main parts to our system, the client and the server, both written in PHP. The client part, when executed, connects to a server through a port and address using the fsockopen() function. In the examples we cover in this article, the client will actually be used to connect to a number of servers. The server is executed when it is called by the xinetd daemon. xinetd is the successor to inetd, and is common to recent distributions of RedHat. Xinetd is a super-server - a server that listens on many ports and executes the appropriate service when a request is made to a certain port. For example, if xinetd were watching port 23, it would probably start the Telnet service when a request came through. In this article, we'll examine two scenarios. The first deals with a small web hosting company with three different servers having trouble centralizing control of their accounts. The second scenario demonstrates the construction of a simple load monitoring system for several servers. Let's get started! Case study 1: Multiple-Server Control Panel Let's imagine that we're running a small web-hosting company and that we're operating a couple of web servers. Our web servers each came packaged with a web-based control panel, but it's clunky and slow and August 2003 · PHP Architect · www.phparch.com
we want a better way to manage customer accounts. Fortunately, the web servers also came with a fairly robust suite of command line account-management utilities. "But," a small voice in the back of your head may ask, "how can we turn these sleek command line utilities to our advantage?" "With our PHP client/server solution of course!" the copious other voices inhabiting your mind should instantly reply. ;-) Let's think this through. We have several web servers to operate on, each one with a set of utilities we can use to manipulate customer accounts and we'd like to be able to manage all of the web servers from a single control panel instead of with each individual machine's control panel - how can we put this together? We'll start with the idea of a central control panel. The obvious candidate for this is the PHP client. What about the PHP server? Not-so-coincidentally, our web servers are the ideal choices for the server part of our system. REQUIREMENTS PHP Version: php 4.0.6+, CLI, O/S: Unix/Linux Additional Software: MySQL Code Directory: sockets2
33
FEATURES Figure 1 shows a basic diagram of the setup of our client-server system. Figure 1
The Client The client will have access to customer data (through a MySQL database, we'll say). A few fields would be domain name, service plan, e-mail address, username, password, and a unique ID number to help us identify accounts. Listing 1 shows the SQL code for creating the table we'll be using. Listing 2 shows a row of our database where I've inserted some data for testing. Listing 1 CREATE TABLE customers ( Domain varchar(255) NOT NULL default '', ServicePlan varchar(32) NOT NULL default '', Email varchar(255) NOT NULL default '', Username varchar(32) NOT NULL default '', Password varchar(32) NOT NULL default '', id int(5) NOT NULL auto_increment, PRIMARY KEY (id), KEY id (id) ) TYPE=MyISAM;
Sockets: Part 2 debugging sessions, I've often opened up my HTML source to try to find a problem but been dismayed to find one long line of HTML. Using our PrintLn() function instead of print or echo will space out our HTML output very nicely and will help us keep our PHP code free of new-line characters. PrintSoc() is similar to PrintLn() except that it uses the fputs() function to print to a network socket. Although it doesn't impact HTML output, it does add a necessary new-line character which is recognized by servers as a separator argument. It also cleans up our PHP code by minimizing the number of new-line characters we have to append in the actual code. Next, the if-statement checks to see if our form was submitted. If so, a connection is opened to the server that was chosen. The server we'll be writing later on will run on port 64401, so we're setting up our client to connect to that port. If the connection fails, an error is reported. Otherwise, our pass-phrase and the customer's domain name, service plan, e-mail address, username, and password are sent across the server connection. The pass-phrase is a form of authentication known as a handshake where the client and server basically introduce themselves. Finally, we read and print out any output that is sent from the server until the connection is closed - hopefully the information the server sends us will say that the customer's account was created successfully. Figure 2 shows a screenshot of what our barebones control panel looks like in a browser. Although we've left it out here, last month's article discusses encryption as an important security measure. Figure 2
The client will also contain a list of our web servers and their addresses. In this simplified control panel, the administrator will be able to view a customer's records and select a server for the customer's account. Our client code is shown in Listing 3 (included in this month’s package). We start out by connecting to MySQL and storing a customer's data in an associative array - note that for simplicity, I've hard-coded the customer's ID number into the script instead of first printing a list of customers for the administrator to choose from. Next, I've thrown in two functions that I often use to help me Listing 2 organize my PHP code, as well as mysql> select * from customers; +------------- +------------- +------------------ +---------- +---------- +----+ the HTML output. PrintLn() | Domain | ServicePlan | Email | Username | Password | id | simply echoes whatever was sent +------------- +------------- +------------------ +---------- +---------- +----+ | phparch.com | Corporate |
[email protected] | testuser | testpw | 1 | to it in $Line and appends a +------------- +------------- +------------------ +---------- +---------- +----+ new-line character. During 1 row in set (0.00 sec) August 2003 · PHP Architect · www.phparch.com
34
FEATURES Encrypting the customer's information before transmitting it to your server would be a wise decision in order to try to be as safe as possible. That about does it for our PHP client, now let's work on our server. PHP Server The purpose of our server (shown in Listing 4) will be to act as an intermediary between our client and the account creation utilities local to the server. It will need to be executed from the command line, so we'll have to add a few arguments usually not seen in a normal PHP script. If you're unfamiliar with shell scripting, the most apparent difference is probably line 1. This line simply tells the operating system the location of our parser. The -q argument tells the PHP parser to go into quite mode, which means that the HTML headers that are usually added to our output will be suppressed. The next two statements set STDIN and STDOUT as streams to standard input and standard output. These constants will be used as the means for reading and writing information to and from the client. Note that if you're using PHP 4.3.0 or above, the -q argument on line 1 and the definitions for STDIN and STDOUT are unnecessary as they will be defined by default. Next, we read in our pass-phrase from STDIN using the fputs() function and trim off any white space that might have come through. We compare the passphrase to our hard-coded answer, and if it doesn't match, the script is exited. If all is well thus far, we continue on and read in the customer's account information. I haven't included it here, but we can put in some error checking and return Listing 4 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
#!/usr/bin/php -q
August 2003 · PHP Architect · www.phparch.com
Sockets: Part 2 an error message if a variable turns out to be invalid. We now construct a command string with the path to our account-creation utility and the necessary arguments. I've chosen to use the shell_exec() function to execute the utility so that the utility's output will be returned, giving us a little more control over how to store it. In this example we've simply returned it to the client by printing it to STDOUT, but we could have stored it in a local database or used any of a number of other methods to communicate the result of the command. We finish by closing the STDIN and STDOUT streams and exiting the script. One last thing we have to do before this script will run is chmod it so that our system knows that it's an executable file. See how this is done below. Notice the changes in permission settings. [root@ns1 root]# ls -l server.php -rw-r--r-- 1 root root 5 Jul 8 04:25 server.php [root@ns1 root]# chmod 755 server.php [root@ns1 root]# ls -l server.php -rwxr-xr-x 1 root root 5 Jul 8 04:25 server.php [root@ns1 root]
Before we move on, let's test out our server script on the command line to make sure it works. I've included the code for a dummy account-creation script in Listing 5. To use it, save it as a file named CreateAccount in the same directory as server.php. Follow the same chmod process that we used for server.php. [root@ns1 root]# chmod 755 CreateAccount
Listing 5 #!/usr/bin/php -q
As you can see, the dummy script is very minimal and does absolutely nothing with the arguments sent to it, but it will provide an indicator of how well server.php works. Now, follow the interactions shown in Listing 6. We first execute the server and then enter a few values to make sure that everything works as expected. Listing 6 [root@ns1 root]# ./server.php PHP|ARCH phparch.com Corporate
[email protected] testuser testpw Account created successfully!
35
FEATURES
Sockets: Part 2
If your script doesn't run correctly, here are some tips that might help: • If you try to execute the script but get “No such file or directory” bash error, your path to the PHP interpreter on line 1 of the script is probably incorrect. Run the command whereis php to find its location and experiment with the paths that are returned. If it still doesn't work, then it's possible that PHP is not configured to run as an executable on your system. Try php.net for help on recompiling or reinstalling PHP with command line processing enabled. • If you get a “Permission Denied” bash error, then you might not have chmodded your script correctly. You need to run the command chmod 755 server.php before it will execute properly. Hopefully we've now got a tested and working server. Let's set up Linux to accept remote requests. As discussed last month, the easiest and most secure way to do this is probably with xinetd. Setting up Xinetd There's not much involved in setting up a server with xinetd. All we're going to do is add the file my_php_server, shown in Listing 7, to the /etc/xinetd.d/ directory (though the process for adding a server to xinetd may be a little different for you depending on your system). Listing 7 service my_php_server { port server user socket_type protocol #only_from }
= = = = = =
64401 /path/to/server.php root stream tcp 10.0.0.4
There are a number of xinetd directives besides the ones shown here that you may want to research. An interesting xinetd directive that we've commented out here is only_from. only_from will limit the incoming connections to a specific IP range. Limiting connections to the address of your client machine, for example, would be a very applicable security measure. Now that we've added our server definition to xinetd.conf we have to restart xinetd. To do that, type service xinetd restart or, if that doesn't work, /etc/rc.d/init.d/xinetd restart should do the trick. Let's test it out a little. Follow the interaction shown in Listing 8. If you see something similar, congratulations, you're almost done! If not, here are some troubleshooting tips August 2003 · PHP Architect · www.phparch.com
that will hopefully speed you along to the final step: • If you're having trouble Telneting to your server, be sure that server.php executes properly when executed by itself from the command line. • If the script executes as expected, but you still can't telnet to the server, make sure that you restarted xinetd. • If it still fails, check to see if you are running a firewall - you could very well be blocking port 64401. You'll need to unblock it (and probably restart your firewall software) before any connections you try to make will get through. Putting it all together If our tests so far are any indication, everything should work fine. Let's find out by loading up client.php in a browser, choosing a server, and submitting the form. Hopefully, after a few seconds, you'll get confirmation that a new account was created. Now all you have to do is copy the server script and xinetd config file to your other web servers and you should be good to go. One of the most frustrating bugs I ran into when I was first designing this system was when I set up a server to run on the same machine as my client. What happened was that every time I tried to create an account, the connection would break. I didn't realize until after hours of fruitless walkthroughs of my code that the reason my connection broke was because the account creation utilities restarted Apache upon creating an account and would thus terminate my client script. To get around this problem, I made a work-inprogress HTML page that consisted only of a 30-second pause and a work status graphic. Once the client sent the customer's information to the server, the client would redirect the browser to the work-in-progress page. This work-in-progress page required no interaction with the server and therefore wasn't impacted when the server restarted. After the 30 seconds, the work-in-progress page would direct the browser back to the client script which could request the status of the account without fear of being broken off by Apache. Additional Ideas An interesting off-shoot of this technique would be to Listing 8 [root@ns1 root]# telnet 127.0.0.1 64401 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. PHP|ARCH phparch.com Corporate
[email protected] testuser testpw Account created successfully!
36
FEATURES develop a system that took given information and created the user's account on the optimal server automatically without requiring administrator interaction. For example, if your three servers ran Linux, FreeBSD, and Windows, your script might check to see which type of account your customer wanted and then automatically start the install process on the correct server. Another idea would be a sort of load-balancing mechanism where you monitor your web servers' load averages (or disk space usage, or free memory, etc.) and install the customer's account on the web server with the best stats. In fact, this is so interesting, that our next task will be to build a load average monitoring system!
"...cron is a standard unix utility that runs programs according to a predefined schedule..."
Case Study 2: Load Average Monitoring System We'll pretend now that we want to monitor our three web servers' load averages. We'll develop a client-server system similar to our accountcreation utility to provide a remote centralized location with access to our web servers' vital stats. We'd like to have a client script that can query our servers for their current load averages and then store the information in
Sockets: Part 2 a database so that we can later summon it up for averaging, graphing, and additional reporting. Unfortunately (or fortunately, depending on how much you like to code), servers don't just send out their load averages anytime someone wants them - we'll have to write a script to return this info to us when we request it. This should be starting to sound a little like our account creation utility. Here's our plan: we'll write a client script to send requests to our servers for data. We'll use xinetd on our servers to accept the requests and start the server scripts. The server scripts will each print their system's load average to STDOUT, which will be read by the client script and inserted into a MySQL database. Storing our load averages in a database lets us easily pull them up whenever we want. It would, for example, be a very simple matter to create a graph of the load averages for all of our servers. Listing 9 shows the SQL code for creating the table we'll be using to store our load averages. Listing 9 CREATE TABLE systemlog ( ServerName varchar(200) NOT NULL default '', Load varchar(10) NOT NULL default '' ) TYPE=MyISAM;
The Client Script Our client script will need to record data on a regular schedule which means that it would be pretty ineffi-
Listing 10 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
#!/usr/bin/php -q
Figure 3 minutes
hours
days of month
months
days of week
command
*
*
*
*
*
/path/to/load client.php
August 2003 · PHP Architect · www.phparch.com
38
FEATURES
Sockets: Part 2
Up until line six, there's nothing we haven't seen before. Starting on line six, we read in the result of the uptime command and strip out the appropriate load average. We then send the load average to STDOUT, close the streams, and exit the script. Don't forget to chmod load_server.php as shown below. [root@ns1 root]# chmod 755 load_server.php
As always, to make sure it's working, we'll do a test run. First, we'll run uptime to see what the current load is, and then immediately after, we'll run load_server.php to see how well they match (in reality, we'd run it a couple times to be certain, but for space-saving purposes, we've cut it down here). [root@ns1 root]# uptime; ./load_server.php 2:45pm up 19 days, 12:47, 1 user, load average: 0.55, 0.41, 0.41 0.55[root@ns1 root]
As you can see, it seems to work fine - the minute load average of the uptime command (0.55) matches the load average output by load_server.php (0.55) exactly. Now we need to set up xinetd to accept incoming connections. To do this, we add the file shown in Listing 12 to our /etc/xinet.d/ directory (this may be different for you depending on your system). Make sure you remember to restart xinetd after adding your config file. Alrighty, we should be good to go here. The cron job is running load_client.php every minute, and load_server.php should be responding. I've installed it on just one server here, but it'll give us a good indication of how well everything's working. Listing 13 shows what we've collected in our database so far. Looks good! We've collected nine pieces of data and we can tell from the time stamps that they're spaced about 60 seconds apart. If you don't get similar results, see if the troubleshooting tips from Case Study 1 can help. Additional Ideas There are a number of useful applications we can build on top of what we've written in Case Study 2. Firstly, we might add a mechanism to delete rows after a cerListing 12 1 service loadwatch 2 { 3 disable 4 port 5 socket_type 6 protocol 7 wait 8 user 9 server 10 }
= no = 64402 = stream = tcp = no = root = /path/to/load_server.php
August 2003 · PHP Architect · www.phparch.com
tain amount of time (because, data would accumulate very quickly - over 10,000 rows in one week for one server alone!). Then, we could combine it with an account creation system as suggested in Case Study 1 to help accurately distribute customer accounts to the most optimal systems. Some other ideas would be to use the data harvested with this technique to display graphs of our (hopefully very low) server loads to prospective customers. We could edit load_client.php to e-mail us whenever the load reaches a certain level, to initiate the shutdown of non-critical services, or even to restart the server machine itself. Conclusion I hope this article demonstrated a way to use PHP you hadn't seen before. We covered a few *nix utilities usually not associated with PHP and we took a look at PHP shell scripting. There are undoubtedly countless other applications besides the two we examined today. If you come up with something else or if you run into any roadblocks, post a message in this article's forum at http://www.phparch.com/discuss and I'll try to get back to you. Good luck and happy coding!
About The Author
?>
Eugene Otto is an undergrad at the University of Virginia majoring in Computer Engineering. He applied the techniques covered in this article to a suite of tools he built for a small web hosting company. He can be reached at
[email protected].
Click HERE To Discuss This Article http://www.phparch.com/discuss/viewforum.php?f=39
Listing 13 mysql> select * from ServerLog; +---------+------------+------------+----+ | LoadAvg | ServerName | Time | id | +---------+------------+------------+----+ | 0.34 | Server 1 | 1058556961 | 1 | | 0.12 | Server 1 | 1058557021 | 2 | | 0.37 | Server 1 | 1058557080 | 3 | | 0.13 | Server 1 | 1058557141 | 4 | | 0.47 | Server 1 | 1058557201 | 5 | | 0.17 | Server 1 | 1058557260 | 6 | | 0.61 | Server 1 | 1058557321 | 7 | | 0.73 | Server 1 | 1058557381 | 8 | | 0.30 | Server 1 | 1058557440 | 9 | +---------+------------+------------+----+ 9 rows in set (0.00 sec)
39
Can’t stop thinking about PHP?
Write for us! Visit us at http://www.phparch.com/writeforus.php
Click HERE for our “Author Guidelines”
Grokking cURL
F E A T U R E
By Peter James
L
ast month, I wrote about web automation. The tool that I wrote, affectionately named Scout, used curl as its HTTP engine. I included a brief introduction to curl (correctly written as cURL) in that article, but it left me thinking that a more formal treatment was necessary. I was amazed to find that php|architect had yet to run an article dealing with this indispensable tool. I know, I know. In PHP 4.3, the brand spanking new Streams API was unleashed, which, among other things, means that anything that remotely resembles a data stream can now be controlled using ordinary file functions. Previous to this, curl was the only way to do most protocols, unless you got down and dirty in raw sockets. Because of the ease of use of file functions, some people might be tempted jump on the wagon and say that curl is no longer useful. Not true. Not even a little. There are plenty of things that curl still offers that the file functions can’t. Things like: • • • •
cookies HTTP POST HTTP Authentication redirection (via Location: headers)
In this article, I’m going to introduce curl by example August 2003 · PHP Architect · www.phparch.com
in all of its humble glory The article’s main focus will be on HTTP transfers, but we’ll look at some of the other supported protocols as well, including DICT and FTP. Let’s get started. What is curl? Basically, curl is an Internet agent. It is a general purpose client, with support for many different protocols, including HTTP, HTTPS, FTP, FTPS, LDAP, DICT, FILE and TELNET. This is pretty impressive in itself, but curl also makes things easy to do. Handling cookies is trivial. Doing HTTP file uploads are a joke. Using SSL or TLS is ridiculously simple. What is curl? curl is awesome. The base libcurl library and the command-line tool are maintained by Daniel Stenberg, who also happened to write them. Sterling Hughes wrote and maintains the PHP wrapper to libcurl. In my humble opinion, curl is one of the top ten most useful extensions in PHP.
REQUIREMENTS PHP Version: php 4.1+ O/S: Any Additional Software: cURL extension Code Directory: cURL
41
FEATURES
Grokking cURL
Getting started with curl Let’s start off with a simple HTTP example. Take a look at Listing 1. This is about the simplest curl session you can make. Let’s walk through it. Listing 1
First, we have to initialize the curl session using curl_init(). This is similar to calling fopen() on a file, and returns a resource handle that is needed for all other curl calls. The parameter to curl_init() is a URL, and is actually optional. If it is omitted, we can specify it later by setting a specific option – but we’ll deal with that in a moment. Our next step is to execute the curl session using curl_exec(). This simply (in this case) performs the HTTP request, and outputs the results. curl_exec() returns a boolean indicating success or failure. As we’ll see in a moment, the behavior of this function can be changed by setting a number of options. The last thing to do is close the curl session using curl_close(). This is like any other standard close function, and just frees up the resources used by the session. So that’s that. You’ve just joined the curling club. The script operating on the other end of this request is shown below:
You can see the output of Listing 1 below. should see something similar.
You
petej@www $ php -f listing1.php Hello, 192.168.2.100
Getting fancy Right now, our curl_exec() call just dumps out the output from the remote URL directly to the browser Sure, you could surround it with output buffering functions, but why? All you need to do is set an option or two, and you can tap into a massive amount of ability. Have a look at Listing 2. This is basically the same script as Listing 1, but contains a number of optional settings. Let’s run through it. This time we opted not to put the target URL in the curl_init() call; instead, it’s set with the CURLOPT_URL option, using the curl_setopt() function. curl_setopt() is probably the most powerful August 2003 · PHP Architect · www.phparch.com
function in the curl extension. Its manual page on http://www.php.net/ contains dozens of options, many of which are sparsely documented. The user comments on php.net are great, and the libcurl documentation at http://curl.haxx.se/ sometimes contains that extra little bit of understanding that is needed. Let’s look at the options we set in Listing 2. We already mentioned CURLOPT_URL. This sets the target for the session, and can be used instead of specifying it in the curl_init() call. CURLOPT_RETURNTRANSFER is a flag that tells the curl extension to return the output from the curl_exec() call, rather than dump it straight out to the browser. This means that curl_exec() no longer returns a boolean on a successful call, but it does still return false on a failed call. Note that if you wish to output the results from curl_exec() to a file directly, you can do so by using CURLOPT_FILE, instead. CURLOPT_FOLLOWLOCATION is a very valuable flag for letting curl know that we want the curl session to follow any “Location:” headers. There are lots of situations where this would be extremely valuable. If you notice, we actually slightly changed the URL in our call. We dropped the trailing forward slash. This will cause a redirect response from the server, strongly suggesting that we should get the document we’re looking for at http://php.shaman.ca/curl/, not http://php.shaman.ca/curl. If we don’t set CURLOPT_FOLLOWLOCATION, our curl session will end with the redirect response, and the document we thought we were fetching will never see the light of day (try it – just comment out this line). CURLOPT_FOLLOWLOCATION handles “Location:” headers seamlessly, and, in my opinion, is invaluable. CURLOPT_HEADER is another flag that tells curl that we want to output the response headers along with our session results. This can be extremely useful, for example, when debugging your file download scripts. You can make curl simulate the browser, and see what MIME type is being sent for your data. Another use for this option is grabbing the correct MIME type when tunneling calls from one server to another. CURLOPT_VERBOSE is a very handy little option that lives up to its name. By setting this option you can see Listing 2 1 2 3 4
$ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'http://php.shaman.ca/curl' ); curl_setopt($ch, CURLOPT_RETURNTRANSFER , 1); curl_setopt($ch, CURLOPT_FOLLOWLOCATION , 1); curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_VERBOSE, 1); $output = curl_exec($ch); curl_close($ch); print "CURL OUTPUT:\n{$output}\n";
42
FEATURES exactly what your curl session is thinking and doing throughout its life. This is obviously invaluable for debugging, but beware, as it dumps all informational output to the browser directly. The last change in Listing 2 is that we are now grabbing the output from the curl_exec() call, and outputting it how we want to output it. Have a look at Listing 3 for the output of Listing 2. Listing 3 is pretty long, especially given what the output of Listing 1 was, and could probably do with some explanation. All of the lines prefixed by “>”, “” lines are the request headers sent to the server. The “ GET /curl HTTP/1.1 Host: php.shaman.ca Pragma: no-cache Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */* < HTTP/1.1 301 Moved Permanently < Date: Mon, 28 Jul 2003 05:34:23 GMT < Server: Apache/1.3.27 (Unix) PHP/4.3.3RC1 < Location: http://php.shaman.ca/curl/ < Transfer-Encoding: chunked < Content-Type: text/html; charset=iso-8859-1 * Follow to new URL: http://php.shaman.ca/curl/ * Connection #0 left intact * Follows Location: to new URL: 'http://php.shaman.ca/curl/' * Re-using existing connection! (#0) * Connected to php.shaman.ca (192.168.2.100) port 80 > GET /curl/ HTTP/1.1 Host: php.shaman.ca Pragma: no-cache Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */* < HTTP/1.1 200 OK < Date: Mon, 28 Jul 2003 05:34:23 GMT < Server: Apache/1.3.27 (Unix) PHP/4.3.3RC1 < X-Powered-By: PHP/4.3.3RC1 < Transfer-Encoding: chunked < Content-Type: text/html * Connection #0 left intact * Closing connection #0 CURL OUTPUT: HTTP/1.1 301 Moved Permanently Date: Mon, 28 Jul 2003 05:34:23 GMT Server: Apache/1.3.27 (Unix) PHP/4.3.3RC1 Location: http://php.shaman.ca/curl/ Transfer-Encoding: chunked Content-Type: text/html; charset=iso-8859-1 HTTP/1.1 200 OK Date: Mon, 28 Jul 2003 05:34:23 GMT Server: Apache/1.3.27 (Unix) PHP/4.3.3RC1 X-Powered-By: PHP/4.3.3RC1 Transfer-Encoding: chunked Content-Type: text/html Hello, 192.168.2.100
August 2003 · PHP Architect · www.phparch.com
Grokking cURL redirect). As you can see from the output, as well as the CURLOPT_VERBOSE output in the previous lines, a redirect was followed by curl. In the end, the exact same script output was generated, but we’ve now got a boatload of debugging information if we need it. I encourage you to play around with this example Taking it even further Although, in my opinion, we’ve already covered some pretty cool stuff, we’ve barely scratched curl’s surface. Let’s add some more complexity, and see what sort of trouble we can get into! Listing 4 shows our new script, which handles cookies, HTTP basic authentication, HTTP POST, HTTP GET, and multipart form transfers. Let’s run through it. First, we set initialize the curl session. Note that we’re now targetting the protected directory. This directory contains an .htaccess file specifying HTTP Basic Authentication. The contents of this .htaccess file are shown below:
"What this means is that the protected directory requires a username and password to be given before anything in that directory can be served by Apache."
AuthType Basic AuthName “php|architect test site” AuthUserFile /usr/local/www/data/php.shaman.ca/curl/protected/.htpasswd Require valid-user
What this means is that the protected directory requires a username and password to be given before anything in that directory can be served by Apache. In our case, that username and password is stored in an .htpasswd file in the same directory. You can make this .htpasswd file with the following command (in Unix): htpasswd -c -b .htpasswd testuser testpass
For more information on htpasswd, see the man page. For more information on directory access control with .htaccess files, see the Apache web site at http://www.apache.org/. Let’s get back to Listing 4. Now that we’ve specified our target, we can get down to the business of setting options again. We first set the CURLOPT_RETURNTRANSFER option, so we can get the output of the session in a variable. We then set the username and password to use for the site with the CURLOPT_USERPWD option. This option takes a value in the form of username:password. The next two options set up the curl session to handle cookies. The CURLOPT_COOKIEJAR is where cookies should be stored, and CURLOPT_COOKIEFILE
43
FEATURES specifies from where cookies should be retrieved. These are generally the same file, but you may have reason to differentiate. NOTE: The file that you specify in the CURLOPT_COOKIEJAR and CURLOPT_COOKIEFILE options must exist prior to use. If it does not exist, the cookies you try to set will fail silently. This can be the cause of many a headache.
The last option that we set in this first session is CURLOPT_POSTFIELDS. This option allows you to specify variables to be submitted to the host via HTTP POST. If this option is set to a string of name-value pairs separated by ampersands, it is sent to the server as a normal application/x-www-form-urlencoded POST. If, as in our example, this option is specified as an associative array, then it is sent to the server as a multipart/form-data POST. You might know that in order to upload files from your web pages, you must specify the enctype attribute of the form to be multipart/form-data. This may suggest, then, that we could upload a file using the associative array, which is true. You can see in Listing 4 that we’ve set two variables in the array. var1 is just a basic POST variable, and contains “data1”. var2, however, is different. By prefixing the value of a variable in this array with the “@” symbol, we tell curl to use the contents of that file as the variable’s data. If you sneak ahead, and look at the output of this script, below, you can see that it uploads the Listing 4 file to the server. Pretty sweet. Finally, we execute the curl session, close the handle, and output the results. The reason that there are two
Grokking cURL sessions in this script is so that we can demonstrate the cookie handling features of curl. Listing 5, which we’ll look at in a moment, sets a cookie on the first request, and displays it in the second request. Let’s take a look at the second half of Listing 4 now. Our second request is very simple. The big difference is that we are no longer specifying the CURLOPT_USERPWD option; instead, we set the username and password in the URL, just like we could in a browser. Curl handles this seamlessly. Next, you can see that we again set the CURLOPT_RETURNTRANSFER, CURLOPT_COOKIEJAR, and CURLOPT_COOKIEFILE options. Finally we execute the session, close the handle, and output the results. Listing 5 shows the script on the server that handled our request and produced the output. The only thing of note here is that we set the cookie using setcookie(), but give it an expiry of time() + 5. This means that our cookies will time out after 5 seconds, so each time you run the script you should see consistent output. If we set our cookies to timeout immediately, we wouldn’t see any cookie output; if we set our cookies to timeout farther in the future, subsequent requests would show cookie output in both sessions. You can see the output from Listing 4 below. petej@www $ php -f listing4.php FIRST CURL OUTPUT: - You uploaded a file called listing4.php in the var2 variable. It was 921 bytes in length. - POST variable submitted: var1=data1 SECOND CURL OUTPUT: - GET variable submitted: var3=data3 - COOKIE variable retrieved: monster=I love cookies
One thing to note about Listing 4 is that we had to
Listing 4 1