Anda di halaman 1dari 72

MARCH 2003 VOLUME II - ISSUE 3

php|architect The Magazine For PHP Professionals

Exploring XSLT Processing


Options Within PHP
Turn XML into HTML with
various proven methods Web-Free PHP:
Using PHP's CLI Interpreter
Beauty and Brains:
Using Flash's ActionScript, XML and
PHP for Easy Multi-tier Solutions

Blazing Site Performance


Using Objects and Sessions
Creating an RSS Client With PHP
www.phparch.com

FreeTrade:
A PHP-Based E-Commerce Solution

Licensed to: Plus:


Tips&Tricks, Book
Joseph Crawford
info@josephcrawford.com Reviews, Product Reviews
and much more..
User #63883
Introducing
the php|architect Grant
Program

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


As PHPs importance grows on the IT invite all the leaders of PHP projects to
scenesomething that is happening every register with our website at
dayits clear that its true capabilities go http://www.phparch.com/grant and submit
well beyond what its being used for their applications for a grant. Our goal is
today. The PHP platform itself has a lot of to provide a financial incentive to those
potential as a general-purpose language, projects that, in our opinion, have the
and not just a scripting tool; just its basic opportunity to revolutionize PHP and its
extensions, even discounting repositories position in the IT world.
like PEAR and PECL, provide a high- In order to be eligible for the Grant
quality array of functionality that most of Program, a project must be strictly related
its commercial competitors cant afford to PHP, but not necessarily written in PHP.
without expensive external components. For example, a new PHP extension written
At php|a, weve always felt that our mis- in C, or a new program in any language
sion is not limited to try our best to pro- that lends itself to using PHP in new and
vide the PHP community with a publica- interesting ways would also be acceptable.
tion of the highest possible quality. We The only other important restriction is that
think that our role is also that of reinvest- the project must be released under either
ing in the community that we serve in a the LGPL, the GPL or the PHP/Zend
way that leads to tangible results. license. Thus, commercial products are
To that end, this month were launching not eligible.
the php|architect Grant Program, a new
initiative that will see us award two Submit Your Project Today!
$1,000 (US) grants to PHP-related proj- Visit http://www.phparch.com/grant
ects at the end of June. for more information
Participating to the program is easy. We
TABLE OF CONTENT

php|architect
Departments Features

12 Shell Scripting with PHP (PHP CLI)


5 EDITORIAL RANTS by Jayesh Jain
INDEX

7 NEW STUFF
19 FreeTrade, e-commerce for

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


developers
by Vladan Zirojevic
8 REVIEW
TUTOS

32 Blazing Site Performance Using


29 REVIEW Objects and Sessions
by Peter Moulding
phpLens

67 TIPS & TRICKS


41 Writing an RSS Aggregator With
by John Holmes PHP
by Marco Tabini

70 BOOK REVIEWS
PHP Professional Projects
Professional PHP Web Services 49 Exploring XSLT Processing Options
Within PHP
by Stuart Herbert

72 exit(0);
Let Me Throw The First Stone

58 The Story of theBouncing Ball and


XML
by Sam Smith

March 2003 PHP Architect www.phparch.com 3


The designers of PHP offer you the full spectrum of PHP solutions

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


Serve More.
With Less.
Zend Performance Suite
Reliable Performance Management for PHP

Visit www.zend.com
for evaluation version and ROI calculator

Technologies Ltd.
EDITORIAL
EDITORIAL RANTS
Awwww Yeah....
php|architect
These were the first words I uttered after accept- Volume II - Issue 3
ing an invitation to take the reins as editor-in-chief March, 2003
for php|architect magazine. About 3 weeks later,
Marco, our fearless publisher, reminded me that I
still had to write the Editors Rants aritcle. This is Publisher
essentially what you see at the beginning of seem- Marco Tabini
ingly every magazine ever published. Letter from
the Editor, From the Editors Desk. Whatever
you want to call it its all the same and as far Editor-in-Chief
as I can tell, its all pretty meaningless. Brian K. Jones
Over the past few weeks Ive read no fewer than brian@phparch.com
30 of these articles, trying to get a clue as to this
articles purpose in life. I read magazines covering
every conceivable topic, formal and informal, Editorial Team
from the politically perfect to the underground, Arbi Arzoumani
grass-roots publications and found that these edi-

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


Brian Jones
torial columns all seem to accomplish a single Peter James
goal: to make the editor look like the most preten- Marco Tabini
tious, micro-managing, self-important species
ever to roam the earth. Did these people lack
attention as children or something? The more I Graphics & Layout
read, the more I came under the suspicion that Arbi Arzoumani
the species I had been recruited to emulate might
be... well... not my cup of tea. I also couldnt help
but ask myself out loud on several occasions, who Administration
reads these things? Emanuela Corso
But alas, this is not meant to be an editorial on
editorials (or a metatorial if you will). If Im
forced to partake in this charade, then I will Authors
endeavor to make it something useful. It so hap- Stuart Herbert, Jayesh Jain, Peter Moulding,
pens that, being my first month at the helm, I Dave Palmer, Sam Smith, Marco Tabini, Vladan
have plenty to share with whoever is reading this, Zirojevic
specifically with regard to my vision for php|a over
the coming months. I do welcome comments on
all of this, by the way: brian@phparch.com php|architect (ISSN 1705-1142) is published twelve times a year by Marco
Tabini & Associates, Inc., P.O. Box. 3342, Markham, ON L3R 6G6,
Ill break my thoughts down into sections cover- Canada.
ing our current status, the near future, and the Although all possible care has been placed in assuring the accuracy of the
longer haul. 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.
Where We Are
Contact Information:
From rather humble beginnings, I think php|a has
accomplished much with regard to the vision of General mailbox: info@phparch.com
its creator Marco Tabini. This vision was two- Editorial: editors@phparch.com
Subscriptions: subs@phparch.com
fold, so Ill cover them seperately. Sales & advertising: sales@phparch.com
First, php|architect has become known as a rep- Technical support: support@phparch.com
utable resource for well written, well edited docu-
mentation covering all aspects of PHP develop-
Copyright 2002-2003 Marco Tabini & Associates,
ment. While I will not bore you with the laborious
Inc. All Rights Reserved
details of how challenging it can be to combine

March 2003 PHP Architect www.phparch.com 5


EDITORIAL

editorial precision with technical savvy, I will say the php|architect website for more details.
that this process and this accomplishment both
fall under the heading of non-trivial tasks. The Longer Term
Certainly a very large thank you is in order for
each of the many authors who collaborate with Looking out and attempting to predict our
our editorial staff each month. Working together, future at such an early stage, while fun, is proba-
we have been able to overcome challenges rang- bly also an exercise in futility. All that can be said
ing from differences in opinion to differences in for sure is that there are plans of great size and
time zone, culture, and even language. Their time, great number. More huge ideas are born every
patience, and hard work is very much appreciated. day. Surprisingly, only some of the ideas belong to
Second, php|architect continues to make great us staffers. The rest come from you!
strides in establishing itself as a voice for the PHP So far, most of the changes and adjustments
developer community, as well as an advocate for weve made, and some which were working on,
the deployment, evolution, and progress of the have come directly from our readers. Were all
PHP platform. We havent sought to glorify the constantly monitoring emails, looking in the
state of PHP, nor to shun its more proprietary php|architect forums, and even monitoring the
peers. Instead, we try to maintain a realistic focus PHP mailing lists in search of yet another unful-
on the use of PHP in production environments. filled need that we might be able to lend a hand

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


We also take a decidedly optimistic view of the to. Please keep these requests coming! There is
future of PHP, which at times finds itself at odds hardly a greater compliment that can be paid to a
with what some of the larger names in communi- publication than constructive feedback.
ty might have in mind. Nevertheless, we feel that
the opinions of our authors, the editors, and the In Conclusion
community are an important part in shaping any
project which lists open development as a goal. So now that Ive firmly planted the image in
your head of an SUV-driving, tie-wearing, clean-
The Months Ahead shaven, pretty boy editor sitting in his overstuffed
couch with a $4.00 cup of coffee and a laptop, let
The months ahead will continue to build upon me assure you that while I take the publication
the foundation laid in the first months of our exis- and my work seriously, I dont take myself all that
tence. The main goals in the immediate future seriously at all. The reality is that I have hardly a
focus largely on quality and efficiency in other clue how I got here. I can only be thankful for
words, bringing you even better quality content Marcos obvious and complete insanity in choos-
whithout breaking our necks or collapsing from ing me for this post.
exhaustion in the process. To that end, there have I am grateful both to Marco for this opportunity,
already been improvements, tweaks and hacks put those authors with whom Ive had (and continue
in place. to have) the distinct honor of collaborating, and
First, weve convinced a one-time volunteer edi- the many readers who have offered their feedback
tor, Peter James, to come on staff as a full-fledged and encouragement.
editor. Peter has proven himself to be a tireless
worker and a great collaborator. He has already Until next month...
done wonderful work here, and he will undoubt-
edly leave an indelible mark upon the pages of
php|architect over the coming months.
Second, as further proof of our commitment to
the community at large, we are very proud to
announce this month the launch of a grant pro-
gram to aid fledgling (or not-so-fledgling) projects
to continue to progress and bring PHP into the
places where no man has gone before or at least
the road less traveled. See this months pages and

March 2003 PHP Architect www.phparch.com 6


NEW STUFF

PHP 4.3.1 Released new features, such as CVS


integration, advanced
The PHP Group has announced the release of project management
version 4.3.1 of the PHP interpreter. The new capabilities and improved
release addresses a bug in the CGI version of the performance on all plat-
interpreter that invalidates the effectiveness of the forms.
enable-force-cgi-redirect compile-time switch. The Zend Studio 2.6 is
This, in turn, makes PHP-CGI susceptible to out- priced starting at $195
NEW STUFF

side hacking attacks that could result in the execu- (US). More information is
tion of arbitrary PHP code. available from the Zend
For more information, visit website at
http://ca.php.net/release_4_3_1.php http://www.zend.com.

ADODB 3.20 Available Introducing the php|architect


Affiliate Program
A new version of ADODB, the popular and effi-
cient database abstraction library, has been Earlier this month, we proudly announced the

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


released by its maintainer, PHPEverywhere blog introduction of our new affiliate program, which
author John Lim. pays a commission for each purchase made
through our website by visitors referred from on of
ADODB 3.20 supports several new features, our partners.
including abstracted capabilities for creating
tables and indexes, although this functionality is Participation in the program is free, and open to
still considered in its alpha stage. all websites, without any minimum requirements.
According to its website, available at You can find more information on the php|a web-
http://php.weblogs.com/adodb, ADODB is twice site at https://www.phparch.com/afflogin.php.
as fast as PEAR-DB and 10% faster than PHP-Lib

PHP Conference in Montral


Nova: A P2P Client In PHP
PHP Qubec will hold their first PHP conference
The Nova Project has released Nova, a peer-to- in the city of Montral on March 20 and 21.
peer application compatible with the popular Speakers at the event include a whos who of the
GNUtella file-sharing network. Nova is written PHP community, including Zeev Suraski, Andrei
entirely in PHP using the PHP-GTK extension, and Zmeivski and Rasmus Lerdorf. php|architect will
provides an excellent example of how PHP can be also be there and our own Marco Tabini will give
used to develop application outside the Web a presentation on PHP-based business and our
space. experience in the world of electronic publishing.
Nova, which is based on the GnucDNA library, For more information, you can visit the confer-
currently supports only basic functionality and is ences web site at
only compatible with Windows. You can find more http://phpconf.phpquebec.com/.
information at the projects homepage
(https://sourceforge.net/projects/novap2p/).

Zend Releases Studio 2.6

PHP powerhouse Zend Technologies has release


version 2.6 of their Zend Studio IDE. The new
application includes several bug fixes, as well as php|a

March 2003 PHP Architect www.phparch.com 7


REVIEWS

Reviewed For You

TUTOS
The Ultimate Team Organization Software
REVIEW

By Marco Tabini

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


A few days before writing this review (which, at the
time, was another review), I had something of an
epiphany. I was on the phone with a client, boast- The Cost:
ing about how organized I am, when Arbi walked by Free (released under the GPL)

my office and laughed a typical I know better at Requirements:


me. My ego was bruised, so I wrote down a list of Apache
ways that I am organized on a piece of paper. Id You wil need one of these Databases
PostgreSQL Database
gladly show you that piece of paper if I hadnt, er, MySQL Database (MySQL-3.23.21-1.i386.rpm)
temporarily misplaced it. Oracle Database
Borland Interbase 5
As the saying goes, if you cant blame anyone but PHP 4 (minimum php4.1)
yourself, blame the process. After all, a person can
only do so much on his own. I set out to find a Download Page:
http://www.tutos.org/homepage/download.html
decent groupware application that would allow me
to get more organized and, at the same time, better Home Page:
http://www.tutos.org
manage the offices internal processes (formatting
Arbis hard drive would have been a bonus, but I
Developer Background:
couldnt find any applications capable of the perfect I started a first TUTOS like system for my former compa-
union between a calendar and that). ny back in 1997 or so. They still use it alot to have an
overview of all their customers , software installations and
After a bit of searching, I came across TUTOS, different products and to support the internal Quality
which stands for The Ultimate Team Organization Managment (ISO9600). After leaving this company I start-
ed TUTOS, an enhanced system based on the same
Software. Created by German developer Gero thoughts with a lot more features. On TUTOS I'm working
Kohnert as an internal application for his previous now for more than a year and after making a first installa-
tion in my department at my current employer I think it is
employer, TUTOS has evolved into a complex group- time to release it to the public, giving something back to
ware application that is used by the likes of Siemens. the Open Source Community.
Best of all, its written entirely in PHP.

March 2003 PHP Architect www.phparch.com 8


REVIEWS TUTOS

Groupware, Anyone? on changes to systems such as the bug tracking data-


base or the calendar schedule. Even for a small work-
TUTOS is a complete groupware application in every group, this active notification approach is a very
sense. It includes a calendar, a contact management important feature, particularly when the members of a
system, a bug tracking system, a product/project repos- team do not all work in the same office.
itory, mail capabilities, a time tracking system, an
invoicing system, and much more. The system is also User Interface
multilingual, supporting about fifteen languages right and Security Features
out of the proverbial box.
For companies whose groups work in different parts The system implements a very fine-grained permission
of the world, TUTOS includes the ability to specify time- system, thus ensuring that the administrators have
zone information for each user profile, so that every- every way to control access to each function. In addi-
thing remains properly synchronized and meaningful tion, a TUTOS administrator has prompt access to all of
to all users. the security settings, including his/her own.
Finally, the system includes a watchlist mechanism TUTOS features a colourful and easy-to-use interface
that makes it possible to remain up-to-date, via e-mail, that is sure to please both lovers and haters of GUIs. As

Figure 1

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)

March 2003 PHP Architect www.phparch.com 9


REVIEWS TUTOS

Figure 2

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


you can see in Figure 1, only a minimal amount of think that attention should be paid to integration with
information is kept on the left-side frame menu, leaving outside services like LDAP and/or Microsoft Exchange.
as much real estate as possible available for the systems This could help make TUTOS a more appealing solution
actual functionality. for larger organizations.
Since the entire system is based on PHP, however, it
should be relatively simple to expand its functionality to
include whatever elements one needs. The code is
TUTOS doesn't use any quite clean and well documented, so it forms a good
proprietary components that foundation on which to build a more complex and spe-
cialized application.
are tied to a particular TUTOS includes a good deal of documentation. For
the developer, the TUTOS website provides a detailed
architecture. analysis of all the data structures and logical organiza-
tion of the system, as well as an API reference and an
installation guide. As for the end user, they get a com-
plete contextual help system, shown in Figure 2, that
Documentation, Interoperability includes an accurate description of what each individ-
and Limitations ual screen does.

Its interesting to notice that the ultimate goal of the Conclusion


TUTOS project is to create a portable groupware sys-
tem whose interface can be written for a number of dif- Now that I have TUTOS running on my computer, I
ferent platforms. As such, the development team has can say with confidence that I feel more organized. As
put a large amount of work into the design of the with all groupware systems, the trick to getting TUTOS
underlying data structures themselves. Naturally, thats into an organization is to stimulate user acceptance.
good news because, in addition to the PHP interface, After all, your average user is normally not keen on try-
other interfaces are likely to be developed. In fact, a ing new things, and prefers instead to stick with the
KDE/Gnome version is currently in the works. application that he or she has learned to use well.
From a portability perspective, TUTOS PHP interface TUTOS represents an excellent groupware solution
doesnt use any proprietary components that are tied that is easy to learn and offers a wide array of function-
to a particular architecture, and it supports several dif- ality. It is organized in a logical fashion, provides lots of
ferent database systems, including MySQL, PostgreSQL documentation, and its PHP codebase makes it easily
and Oracle. extensible.
In my opinion, the system could use a bit more work
as far as interoperability is concerned. In particular, I
php|a

March 2003 PHP Architect www.phparch.com 10


Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)
FEATURES

Shell Scripting with


PHP (PHP CLI)
FEATURES

By Jayesh Jain

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


PHP 4.3.0's new CLI SAPI improves greatly on the foundation laid by its CGI ancestor.
Take it for a spin no web server required!

Introduction
- The CLI starts up in quiet mode by default,
With the introduction of version 4.2, PHP started sup- though the -q switch is kept for compatibility so
porting a new SAPI (Server Application Programming that you can use older CGI scripts.
Interface) called CLI (Command Line Interface). This
facility was introduced to help developers create small - The CLI does not change the working directory
shell applications with PHP that have no dependencies to that of the script. (-C switch kept for compati-
on a web server. bility)
In version 4.2.0, the CLI SAPI was experimental, and
had to be explicitly enabled using the enable-cli - Three words: Plain text errors! (no HTML format-
option when running ./configure. However, with ting).
PHP 4.3.0, the CLI SAPI has been deemed stable and is
therefore enabled by default. It can be explicitly dis- In this article, well discuss how to use PHPs CLI fea-
abled by specifying the disable-cli option to ture to exploit the power of PHP from the command
./configure. line. Well assume that you have a fair understanding
Though it was technically always possible to create of PHP and that PHP is installed and working properly
independent shell-style scripts with PHP using a stand- on your computer. Most of the examples in this tutori-
alone CGI interpreter, there are a few things that make al were tested on a Windows platform (w2k, to be pre-
the new CLI SAPI an especially attractive and unique cise), but should work unaltered on a Linux box.
tool:

- Unlike the CGI SAPI, no headers are written to REQUIREMENTS


the output. Though the CGI SAPI provides a PHP Version: 4.3 and Above
way to suppress HTTP headers, theres no equiv- O/S: Any
alent switch to enable them in the CLI SAPI. Additional Software: N/A

March 2003 PHP Architect www.phparch.com 12


FEATURES Shell Scripting with PHP (PHP CLI)

What is a PHP Shell Script? Getting Started

The term PHP shell script can cause some confusion Lets start with a small script (the most familiar one)
for those who are used to traditional UNIX-style shell which simply prints the words Hello World to the
scripts. The reason for the confusion is that PHP, strict- screen. Using your favorite text editor, create a text file
ly speaking, does not provide a traditional interactive called world.php in your PHP folder and enter the fol-
environment for command execution. Another source lowing text.
of confusion is the fact that the name of the SAPI CLI
is also an acronym used to refer to a UNIX shell, which <?php
by definition is an interactive command execution envi- echo "Hello World\n";
ronment! Lets clarify this right now by simply stating ?>
that the PHP CLI is an interface to a PHP interpreter that
can be accessed via the command line - not an interac-
tive shell. In reality, PHP shell scripts are just PHP Save the file, ensure that you have the PHP CLI exe-
scripts that can be run from a command line or a cron cutable in your path, and run the following command
job (which will be discussed later in the article) in the
> php world.php
same fashion as a shell or PERL script.
A normal shell script consists of two main parts: the
very first line, which indicates the interpreter that Surprised to see the output, Hello World in the com-
mand prompt instead of the web browser? Welcome

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


should be parsing the script, and the code to be exe-
cuted by the interpreter. Normally, the interpreter that to the other dimension of PHP!
is specified is a UNIX shell, or some other interpreter, Note: If you are using PHP version 4.2 (or the CGI
like PERL. However, since the creation of a PHP inter- Version) you may have noticed that the following head-
preter which can be executed directly from a shell er is also in the output:
(without the help of a web server), we can specify PHP
X-Powered-By: PHP/4.2.3
as the interpreter for our script, and then fill the file
Content-type: text/html
with PHP code. For all you Windows users, PHP shell
scripts are just like batch (.bat) files in MS-DOS, but
with the power of the PHP programming language. The PHP CGI version does that by default, which can
serve as an indicator that youre using the CGI (in addi-
Finding Your PHP CLI tion to cluing you in on the PHP version youre run-
ning).
Output on my Windows machine of the php.exe in the To suppress the HTTP headers and follow along with
cli folder was: the remaining examples, run the PHP CGI with the -q
command line flag.
PHP 4.3.0 (cli) (built: Dec 27 2002
05:34:00) > php -q world.php
Copyright (c) 1997-2002 The PHP Group
Zend Engine v1.3.0, Copyright (c) 1998- Using the -r option, you can also pass the PHP code
2002 Zend Technologies to be executed to the PHP CLI directly from the com-
mand line. For example:
Output on my machine of php.exe in the php root
> php -r echo Hello World\n;
folder was:

PHP 4.3.0 (cgi-fcgi), Copyright (c) Will output Hello World, the same exact message as
1997-2002 The PHP Group our earlier example. This is fine for small bits of code
Zend Engine v1.3.0, Copyright (c) 1998- you only want to execute once. Only as the code gets
2002 Zend Technologies larger will you need the power of a script.

This would indicate that the php.exe file on my You can redirect the output from any script to a file
machine in the php root folder is actually the old CGI. by running:
For the reasons mentioned earlier comparing the CGI
> php world.php > outputfile
and CLI interpreters, and because the title of this article
says CLI in it and not CGI, Ill be refraining from
using the CGI in the examples going forward. I just Linux and UNIX users can also redirect the script out-
wanted to point out a possible point of confusion put to another command by using the | (pipe opera-
here... so on we go! tor). For example:

March 2003 PHP Architect www.phparch.com 13


FEATURES Shell Scripting with PHP (PHP CLI)

Getting Interactive
> php world.php | wc -w
PHP has always made it very easy to interact and trade
Will return the word count of the output (in this case, data with a user via the traditional browser interface.
it will output the number 2). Lets have a look at how PHPs stream functions and
Conversely, you can also send output from some associated constants can extend this ease to the com-
application or command to the php interpreter by mand line interface. There are three streams available
using the pipe operator, for example: in PHP CLI. If youre familiar with the corresponding
standard UNIX device names, youll be right at home
> someapp | php here, as these streams emulate the same functionality.

This might be useful if someapp outputs PHP code stdin (php://stdin)


which can be immediately executed by the interpreter stdout (php://stdout)
instead of adding the overhead of first writing to a file stderr (php://stderr)
and then calling the interpreter separately which will
then also be subject to the added overhead of opening The following example will display Hello World in
and reading a file from disk. the output window using the output stream.

<?

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


$stdout = fopen('php://stdout', 'w');
Why Do You Need A fwrite($stdout,"Hello World");
fclose($stdout);
PHP Shell Script? ?>

- Shell scripts can take input from, This time, well demonstrate how to use an input
and send output to a user via stream. It will accept an input from the user, wait for
STDIN/STDOUT, a regular text file, the user to press the Enter key and then display the
or even another command. In other entered text.
words, theyre very flexible.
<?
$stdin = fopen('php://stdin', 'r');
- Shell scripts are a quick and dirty echo "Please Enter your Name: ";
way to create your own command- $mystr = fgets($stdin,100);
line tools and applications. echo "Your Name Is :\n";
echo $mystr;
fclose($stdin);
- If you already use PHP for web ?>
development, why learn another
language to write shell scripts? The following example shows you how to output text
to an error stream.
- Shell scripts are commonly used to
automate day-to-day administration
tasks.

Figure 1

March 2003 PHP Architect www.phparch.com 14


FEATURES Shell Scripting with PHP (PHP CLI)

<?
$stderr = fopen('php://stderr', 'w');
fwrite($stderr,"There was an Error"); Constant Details
fclose($stderr);
?>
STDIN
To make accessing and moving data around in a shell An already opened stream to stdin. You dont
environment simpler, PHP has added the following have to open it with:
constants. Again, for UNIX users, these names should
look familiar. In UNIX, these typically map to the key- $stdin = fopen('php://stdin', 'r');
board (STDIN) and the screen (STDOUT and STDERR)
by default. STDOUT
An already opened stream to stdout. You dont
Using Arguments in Scripts have to open it with:

Anyone who has experience writing shell or PERL $stdout = fopen('php://stdout', 'w');
scripts would probably consider the PHP CLI complete-
ly useless if the scripts werent able to handle argu- STDERR
ments passed to it from the command line. The CLI An already opened stream to stderr. You dont
handles this with the use of the $argv and $argc vari-

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


have to open it with:
ables.
All of the arguments passed to your script are stored $stderr = fopen('php://stderr', 'w');
in a zero based global array variable $argv. Another
global variable, $argc, holds the number of argu-
Here is an example of how to use a stream with constants:
ments passed to the script (everyone coming from a
C/C++ background, should be familiar with this prac-
<?
tice). Here is the code, which displays the total num- fwrite(STDOUT,"Hello World \n");
ber of arguments passed as well as the arguments: ?>

<?
echo "Total argument passed are: $argc\n"; For users of version 4.2, youll need to add these addition-
al lines to the top of your script in order for the above
for( $i = 0 ; $i <= $argc 1 ; $i++) example to work:
{
echo "Argument $i : $argv[$i] \n"; define('STDIN',fopen("php://stdin","r"));
} define('STDOUT',fopen("php://stdout","w")
?> define('STDERR',fopen("php://stderr","w")

Figure 2

March 2003 PHP Architect www.phparch.com 15


FEATURES Shell Scripting with PHP (PHP CLI)

Assuming this is stored in a file argument.php you Also, dont forget to add the PHP tags in the script
could test this script by running: file, otherwise PHP will not interpret it properly. If
youre stuck with the CGI version and want to suppress
> php argument.php arg1 arg2 the HTTP headers, use #!/usr/bin/php -q.
Similarly, you can also use any other PHP arguments.
Refer to Figure 2 for
sample output Scheduling PHP Scripts

Important : You can use $argc and $argv in the man- PHP scripts can be scheduled to run automatically with
ner described above only if you have both the regis- the help of cron. Cron is the name of the standard
ter_globals and register_argc_argv settings in execution scheduling mechanism that enables UNIX
php.ini set to on. As of PHP version 4.3, regis- users to execute commands or scripts automatically at
ter_globals is set to off by default. You should do some regular interval. A common use for it is to back-
your best to write your scripts so that they do not require
up all of the file on the server, do some database
register_globals to be on, to avoid possible security issues
cleanup, or send emails to administrators about the
in your incoming form variables. You can still use $argv
state of the system. Try typing man cron or man
and $argc if register_globals is set to off. Just
use the following code: crontab on your Linux system to learn more about
how to get your PHP scripts to run at regular intervals.
$argc = $_SERVER['argc'];
This can be an extremely valuable tool to developers

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


$argv = $_SERVER['argv']; who need to do major data crunching without the user
in the browser being adversely affected. For example,
while its possible to have a user click a button which
Using External commands
triggers the collection of live network data coming
in Scripts
from remote LDAP, SNMP or other sources, grabbing
In order to run any external command from the script,
and manipulating this code to make it suitable for pres-
we will have to use the php function shell_exec. entation can really take some time. Why not poll these
Shell_exec will execute a command via the shell and sources in a cron job at regular intervals, store every-
return complete output as a string. thing to a database in a more friendly format, and have
the front end simply grab from the database in a more
<? bite size format. The possibilities are endless!
$command = "ls - al";
echo shell_exec($command);
?>
...using PHP's CLI
For Linux/UNIX Users
interface makes your
As weve discussed, the PHP executable runs independ-
ently of the web server. An added benefit of using this scripts more portable
interface for your scripting is that it makes your scripts
more portable than using, say, /bin/bash. Using than using, say,
#!/usr/bin/php at the top of your script will allow
you to invoke the PHP interpreter on a Linux machine, '/bin/bash'.
and on Windows, this line will be skipped over to get to
the PHP code below. Now you can write in your famil-
iar Linux environment, and still deploy to Windows.
For Windows Users:
Tip: Dont forget that Linux wont let you do any- Configuring Windows to Execute
thing until the script has its execute bit set. Assuming PHP Scripts
your script is called testscript.php, you can just cd to
the directory where the script lives and type: To run PHP scripts on your Windows machine, youll
need to associate the PHP files with the PHP interpreter.
> chmod +x testscript.php To do this, open Windows Explorer, click on the tools
menu and select folder options. Click on the File
Once you have set the executable attribute, you can Types tab and select the New button. Type .php in
execute the file like this: as the file extension, and click OK.

> ./testscript.php

March 2003 PHP Architect www.phparch.com 16


FEATURES Shell Scripting with PHP (PHP CLI)

Refer to Figure 3 You can also register files with extension .php3 or
.php4 in the same fashion mentioned above.
Select the PHP entry in the Registered File Types list
box, click the Advanced button, click new and type More Information
Run in the Action box. In the Application Used to
Perform Actions box, type C:\PHP\PHP.exe %1 There are certain php.ini directives, which are over-
%* (change the PHP path if its different on your ridden by the CLI SAPI because they do not make sense
machine. Note that %* is used to send any command in shell environments:
line arguments). Click OK, OK again and then the
Close button. Your Windows machine is now config- a) html_errors = FALSE
ured to run PHP Scripts. Just double click on any PHP As the output of the PHP CLI does not go to a brows-
file in Windows Explorer to run it. er, there is no need to echo HTML tags hence this direc-
tive defaults to FALSE.
Refer to Figure 4

Figure 3

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)

March 2003 PHP Architect www.phparch.com 17


FEATURES Shell Scripting with PHP (PHP CLI)

b) implicit_flush = TRUE Using the MySQL Database in Your Scripts


All the output coming from output commands need You can connect to MySQL database exactly the same
to be shown instantly and should not be cached. As a way as you do in your normal php scripts except you
result, this directive is defaulted to TRUE. should not use HTML tags as the output is not sent to
the browser.
c) max_execution_time = 0 (unlimited) Here is a simple example, which connects to a
Because the PHP runs in the shell environment and MySQL database and lists the username field for all
often does tasks which take a longer time, the records in the user table.
max_execution_time is defaulted to unlimited.
<?
d) register_argc_argv = TRUE mysql_connect("localhost","root","test");
This setting is TRUE so that you can always have mysql_select_db("mydatabase");
access to the number of arguments passed to the script $query = "select username from user";
$searchresult = mysql_query($query);
($argc) and array of the actual arguments ($argv) in while ($line = mysql_fetch_array
the CLI SAPI. ($searchresult))
Extensions {
print " $line[0] \n";
Now that you know all about the PHP CLI, use the fol- }
?>
lowing code examples , or make up some of your own,

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


to test the power of PHP and the CLI.
Conclusion
Sending Email in Scripts
The syntax used to send email is shown below. You can In this article we have tried to show you the power of
skip the header part, as it is an optional parameter. the PHP CLI interface. I am sure you are now convinced
of PHPs flexibility, and urge you to expand upon the
<? concepts discussed here. Certainly, this is an area that
$username = "Jayesh Jain"; will likely receive much attention as PHP evolves into a
$useremail = "jainjayesh74@yahoo.com"; more complete and mature language. Enjoy!
$message = "This is a message from
Administrator";
$subject = "System Message";
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-type: text/html;
charset=iso-8859-1\r\n";
$headers .= "From: Administrator
<admin@someserver.com>\r\n";
$headers .= "To: Administrator
<admin@someserver.com>\r\n"; About The Author ?>
$headers .= "Reply-To: >\r\n";
$headers .= "X-Priority: 1\r\n"; Jayesh Jain is working as a consultant in Auckland, New Zealand. He
$headers .= "X-MSMail-Priority: High\r\n"; has several years of n-Tier development experience and is currently work-
$headers .= "X-Mailer: PHP on ing with PHP to develop interactive client solutions. He has a passion for
Web development and in the spare time he likes to write articles. Contact
someserver.com";
him at: jainjayesh74@yahoo.com
mail($useremail, $subject, $message,
$headers) Click HERE To Discuss This Article
?> https://www.phparch.com/discuss/viewforum.php?f=6

Figure 4

March 2003 PHP Architect www.phparch.com 18


FEATURES

FreeTrade, e-c
commerce
for developers
FEATURES

By Vladan Zirojevic

A package aimed at the developer, FreeTrade offers a cost-effective yet robust framework for creating your next online

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


superstore. This article will show the power and flexibility of FreeTrade, and follow up with a short Q&A with
FreeTrade creator and author of 'Core PHP Programming', Leon Atkinson.

E -commerce applications are one of the most com-


mon requests web developers receive today. An e-
commerce solution should provide two different types
of functionality. Site visitors should see a fully-featured
online shop, while business owners should have an
FreeTrade on the Web
easy-to-use back end administration system.
There are many open source e-commerce solutions
with many good features, and I have tried almost all of The Cost:
them. Specific to PHP there are a few cool scripts Free (released under a BSD-style license
which will help you accomplish everything needed to
set up an online store and to maintain it easily.
Home Page:
Applications like osCommerce (http://www.oscom- http://share.whichever.com/index.php?SCREEN=freetrade
merce.com), FishCart (http://www.phpshop.org),
and phpShop (http://www.fishcart.org) are just
a few examples. Download Page:
http://share.whichever.com/freetrade/downloads/freetrade3.tar.gz
How can you select a package to use as your e-com-
merce solution of choice? Its not easy. You must ask
yourself what you are looking for and identify the prob- Mailing list archive:
lems which need solving. In my case I was looking for http://share.whichever.com/pipermail/freetrade-dev/
a solution which met the following requirements:
Demo store:
open source http://share.whichever.com/freetrade/demoft3/htdocs/
well written
fully-featured REQUIREMENTS
not in an early beta version PHP Version: PHP 4.1.x, MySQL
supported by the community O/S: Any
very configurable Additional Software: N/A

March 2003 PHP Architect www.phparch.com 19


FEATURES FreeTrade, e-commerce for developers

These requirements are not too strict, but some of er. These components can be laid out in the layout file
them do exclude a number of the solutions out there. using HTML tables, div tags, or any other means. Note
After searching, configuring, testing, customizing, and that none of the components of a layout are hardcod-
a good many sleepless nights, I believe that I have ed. They are just included into it with a simple
found a good, although not well-known, solution for a include(). Each component is saved in a separate
developer. file. The code making up each of the navigation, head-
FreeTrade is a fully-featured and highly configurable er and footer components is kept in the navigation
PHP script for creating and maintaing e-commerce Web module directory.
applications. FreeTrade is not a very well-known script, The content component (or screen) is kept in a file in
and there are a few reasons for this. For example, it the screen module directory. A screen may be pure
does not have its own domain name, the demo store is HTML or PHP, but the screen file never performs any
very poorly designed , and FreeTrade uses a different action past showing page content. Listing 1 lists the
(and, at first glance, strange) development model code for an example screen showing a user login page.
called FreeEnergy. This example login screen script would reside in the
login file in the modules/screen directory. Module
FreeEnergy files have no file extension (like .php) because they are
to be included, not executed. I will stay focused on this
FreeEnergy is not a new concept in PHP develop- example and explain a few things.
ment, but it is also not a widely used one. I am sure FreeEnergy has a general module, called utility, which

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


that you use the include() function in your PHP contains common functions and libraries. Some of
scripts frequently. Some of the more common uses are these files are included in every FreeEnergy page. In
for headers, footers, and function libraries. We had this Listing 1 we used functions such as StartForm(),
functionality before PHP, too, with Server-Side Includes. startTable(), and printLink(), which are func-
This include() functionality is the core of the tions from the utility module.
FreeEnergy concept. FreeEnergy doesnt force you to use these functions
FreeEnergy uses the idea of modules. These mod- (you may use print(<table>) instead of
ules are a set of directories, generally kept outside of startTable()), but using them ensures that you can
the web directory tree for security reasons. Almost all control all site elements from single functions. This
FreeEnergy code is kept in these modules. Module files means that the StartForm() function controls all site
are PHP scripts that are included when needed. Some forms. This is great because we can automatically add
standard modules are layout, navigation, action, utility as many hidden variables to all of the forms as we need.
and screen. These directories contain code specific to Site changes are very easy with this approach!
their function, and will be examined in greater detail in Lets look at the definition of the StartForm() func-
a moment. tion and compare it with the function call in our exam-
A FreeEnergy application has only one PHP script ple screen in Listing 1.
inside the web directory, named index.php. This is
the controlling code for the application, and it loads function StartForm($screen,
modules when needed. It is a small script, and we will $method=get,
discuss it in the upcoming example. $action=,
As an example of how the FreeEnergy concept works, $secure=FALSE,
$extra=,
well create the layout of an e-commerce site. Design $windowName=,
and content are elements of every page in a site. While $encodingType=,
the design can be almost the same for groups of pages, $formName=,
the content will be different on every page. Since mix- $onReset=,
ing design and content is never a good idea, we should $onSubmit=,
$class=FALSE);
try to keep them separate.
Usually we have a few design schemes for a site. For
example, we could have different schemes for the The $screen parameter defines which screen will be
home page, department and product pages, news loaded when the form is submitted. $method specifies
pages, and pop-up pages. The FreeEnergy concept whether the GET or POST form method is to be used.
calls each scheme a layout. You may have as many lay- $action names the action to be executed before the
outs for a site as you need, and these are housed in the next screen is shown. $secure defines whether we
layout module directory. will use HTTP or HTTPS for this form. These function
Each layout consists of one or more components. For parameters are the most important. For our purposes,
example, one layout might have a header, content, and the remaining parameters may be ignored.
a footer as components. Another layout might have a The action is a file in the modules/action directo-
header, a left-side navigation bar, content, and a foot-

March 2003 PHP Architect www.phparch.com 20


FEATURES FreeTrade, e-commerce for developers

Listing 1
1 <?php
2
3 /*
4 ** File: login
5 ** Description: form for logging in
6 */
7
8 /*
9 ** Login Form
10 */
11
12 print(StartForm("contents", 'post', 'LOGIN_USER', TRUE, '', '', '', 'loginForm'));
13
14 startTable();
15
16 printEditRow(localize('Name'), "inputLogin", "", 32, 255);
17 printEditRow(localize('Password'), "inputPassword", "", 32, 255, "password");
18 printSubmit(localize('Login'));
19
20 endTable();
21
22 endForm();
23

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


24 /*
25 ** Link for users who haven't registered yet.
26 */
27 print(localize("If you don't have an account, please") . " ");
28 printLink(localize('create an account'), "create_account",
29 0, FALSE, TRUE);
30 print(".");
31
32 /*
33 ** Put the cursor in the correct place.
34 */
35 if ($Browser_JavaScriptOK)
36 {
37 print("<script language=\"Javascript\">\n");
38 print("\tdocument.forms.loginForm.inputLogin.focus();\n");
39 print("</script>\n");
40 }
41
42 ?>

ry which will be included and executed before the tar- htdocs/


get screen is loaded. Actions never print text. They index.php
only take action and return a result to the modules/
$ActionResults array. Some actions are very simple action/
(ie. add a news item to a site), but some are very com- configuration/
plex and require several steps (ie. submit order). Each database/
step of an action must completed successfully for the help/
action to be successful. language/
In our example, the target screen is contents (which layout/
will show the shopping cart contents), and the action navigation/
script is LOGIN_USER. When the form is submitted it screen/
will load and execute the action script. If the action utility/
completes successfully, it will then transfer the user to a
page showing the contents of his shopping cart. We already know the purpose of the general
Now that we know how FreeEnergy works, lets take FreeEnergy modules like layout, navigation, screen and
a look at how FreeTrade builds on it. action. Lets examine the new FreeTrade-specific mod-
ules.
FreeTrade Organization The configuration module contains scripts for specify-
ing application parameters. The well-commented
FreeTrade, a child of the FreeEnergy concept, follows modules/configuration/global file defines all
the same code organization we just described. The important global constants. I will discuss this file in
directory structure is as follows: detail later.

March 2003 PHP Architect www.phparch.com 21


FEATURES FreeTrade, e-commerce for developers

The modules/configuration/screenInfo Configuration and Installation


script describes every page (page=screen) of the site.
The default screen definition is as follows: FreeTrade is easy to configure, but there are a few
things which may cause problems. FreeTrade requires
$ScreenDefaults = array( at least PHP 4.2.
SI_TITLE=>Page title..., Take a look again at the FreeTrade directory structure.
SI_DESCRIPTION =>Desctiption..., The modules directory is parallel to htdocs. This
SI_KEYWORDS=>Keywords...,
SI_LAYOUT=>with_side_nav, implies that modules has to be outside the web direc-
SI_BODY=>style=background: white;, tory. If your host doesnt allow such a configuration, it
SI_PERMISSION=>FALSE); can be a security problem. You can place the modules
directory within htdocs, but the module files dont
have .php extensions. This means that they may not
SI_LAYOUT defines the layout to use for this screen. be parsed by PHP and everyone will be able to see your
SI_PERMISSION is an array of user permissions configuration parameters. Although you can protect
needed to view the page. For instance, to limit page this directory, the only secure configuration is to place
access to the administrator, we would use: modules outside of the Web directory.
After you install the code, you will need to make a
SI_PERMISSION=> array(Administrate) database for FreeTrade. In the FreeTrade distribution
package, you will find an install directory with SQL

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


All screen definitons are stored in an array called files for both MySQL and PostgreSQL. Load the desired
$ScreenInfo. Screen definitions dont usually speci- database types build.sql into a new database. You
fy all of the parameters. Any parameters that are omit- may also load sampledb.sql into the database to
ted will be filled in from $ScreenDefaults. populate it with sample data for testing.
An example of an administrative section for adding a The last installation step is to modify the
new product into the store database is as follows: modules/configuration/global file. You will
probably need to change only database-related param-
add_item=>array ( eters, but I will explain some other interesting parame-
SI_TITLE=>Add item to Catalog, ters, too.
SI_PERMISSION =>array(Administrate)) The first section of this file defines the debug status
and the behaviour of error logging. You can set the
This page will have a default description, default key- DEBUG constant to TRUE for site-wide debugging,
words, and default navigation (with_side_nav). An but dont forget to turn it off once the site is live. Also,
example of a screen with a different layout is as follows: make sure that the error log directory you select in
LOG_DESTINATION exists and is writable by the web
process.
help=>array (SI_TITLE=>Help,
SI_LAYOUT=>plain The next section of this file is for configuring global
) network behaviour.
Setting USE_FLAT_URLS to TRUE will turn on flat
URLs (which will be decoded in index.php). Flat URLs
In this help screen we defined a different layout, but
use a sneaky technique to pass data in like a querys-
omitted permissions. This means that the default per-
tring. A flat URL looks like this:
missions will be used (everybody will be able to open
the page). http://www.site.com/index.php/item/depar
The database module is a kind of abstraction layer for tment/Green/item/Jacket.html
MySQL and PostgreSQL databases. Which of them to
use is up to you. The database type is set in the mod-
A normal URL would look like this:
ules/configuration/global file, and the data-
base module contains function libraries for database http://www.site.com/index.php?SCREEN=ite
operations. m&department=2&item=3
The help module is a set of screens to create context-
sensitive help.
Notice that the script being used here is actually just
The language module is used for localization.
http://www.site.com/index.php. Many search engines
FreeTrade may be totally localized and translated to any
will not index dynamic-looking sites like the second
language. By default, it supports English, Italian,
URL. By passing in data using the method shown in the
German, French and Spanish.
first (flat) URL, you can encourage search engines to
Now lets see how to install and configure the script.
spider the site.
The network behavior section of the global file also

March 2003 PHP Architect www.phparch.com 22


FEATURES FreeTrade, e-commerce for developers

allows you to specify the use of department names, Troubleshooting


instead of their IDs, in links (set USE_NAMED_DEPART-
MENTS to TRUE). The same is available for items (set If the script fails to run properly and your configura-
USE_NAMED_ITEMS to TRUE). Stores with these tion parameters are correct, there may be two possible
options turned on will also be better listed on search causes.
engines. FreeTrade requires magic_quotes_gpc to be off.
The DEFAULT_SCREEN constant defines the screen If magic quotes are on, FreeTrade will not function as
to be loaded if no screen has been requested. By expected. Throughout the sites form handling code
default, it is the welcome screen (home page). FreeTrade uses addslashes() when inserting or
SEND_EMAIL specifies whether or not the system will updating the database. It does not do anything special
send order confirmation emails. The USE_SSL con- when setting the value of form variables, which may
stant should be set to TRUE if you want sensitive areas cause some headaches. You can only change the value
of the system to only be viewed using an SSL connec- of this setting in the system-wide php.ini file or in a
tion (ie. checkout process). This constant affects the .htaccess file in the sites directory.
ScreenURL() and StartForm() functions. Some installations may experience problems with file
FreeTrade doesnt force users to have cookies turned paths. FreeTrade automatically tries to find the right file
on. Session variables will be tracked anyway, but if you paths in index.php in order to provide the capability
want to use cookies, set the USE_COOKIES constant to change hosts painlessly. If it fails, you can set it up
to TRUE. manually. Ive never had problems with these paths on

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


The next section of the global configuration file is Unix/Apache, but I have experienced it a few times on
dedicated to the store catalog. Set ITEMS_IN_MUL- Windows.
TIPLE_DEPARTMENTS to TRUE if you want some Setting it up manually means hardcoding the file
items to be shown in more than one department (very paths in index.php. The constants you may have to
useful). The ALLOW_DUPLICATE_DEPT_NAMES con- hardcode are SERVER_NAME (the name of the server,
stant allows two departments to have the same name like www.phparch.com), SCRIPT_NAME (the name
in the same parent department. I suggest that you turn of the store script, which is index.php by default),
this option off, especially if you have APPLICATION_ROOT (the path on the Web servers
USE_FLAT_URLS turned on. filesystem where the modules directory is located, like
FreeTrade users, by default, can buy items whether /www/ or /usr/local/apache/), and EXTER-
they are registered or not. If you want to force users to NAL_PATH (the path from the root of the Web site to
be registered before checkout, set the index script, such as / or /store/).
SHOPPER_MUST_REGISTER to TRUE.
FreeTrade supports coupons and gift certificates as FreeTrade Workflow
well. If you want to allow them on your site, you
should set the USE_COUPONS and USE_GIFTCER- Many first-time users have problems with under-
TIFICATES constants to TRUE. By default, coupons standing how things work in FreeTrade, so I will explain
are turned off and gift certificates are turned on. the FreeTrade execution process in detail.
The most important parameters in this file are related When a page is requested, the index script first veri-
to the database. Indeed, it will be challenging at best fies that the PHP version is acceptable and that the con-
to run an online store without a successful connection figuration is correct. If everything is in order, it locates
to a database! You can use MySQL (set the DATABASE the modules directory. Next we include the global
constant to mysql) or PostgreSQL (set the DATA- configuration file from the configuration module, as well
BASE constant to pgsql). You will need to set as standard libraries from the utility and database mod-
DATABASE_HOST, DATABASE_USER, ules.
DATABASE_PASSWORD, and DATABASE_NAME to Now that everything is ready for initialization, so we
match your server settings. include the initialization script from the utility
You may try the new FreeTrade caching option by module. This script will get the database initialization
turning the USE_CACHE constant on. This option will code and the caching code (if caching is turned on),
force FreeTrade to cache the results of database queries. and blank out the global variable $ActionResults.
This feature is still in the testing phase, so dont use it The initialization script handles cases where no
on live stores. session ID or department ID was provided, and sets
Now you can play with your FreeTrade application. them to defaults. Finally, this script detects the users
The default administrator login is a username of browser, CSS support, Javascript settings, and anything
admin and a password of admin. To test FreeTrade, else avaiable in the HTTP_USER_AGENT variable.
you may use the FreeTrade Test Specifications that are At this point, if everything is OK, we have finished
located in the doc directory of your distribution pack- preprocessing. Now we get into the more interesing
age. It covers all of the store functions. part: executing actions. As stated previously, the

March 2003 PHP Architect www.phparch.com 23


FEATURES FreeTrade, e-commerce for developers

ACTION form variable, from the script which requested There are a few things which may confuse you if you
the page, names a file in the modules/action direc- are new. Some of them are related to FreeTrade, while
tory to be included. This script contains the action to some are related to e-commerce programming in gen-
take on submission of the form. For index.php, the eral. I will try to clear things up and save you some
action is like a black box. We include the requested time.
action and receive the results (or error message) from it First, let me explain the FreeTrade nomenclature and
in $ActionResults. how an online store basically works. FreeTrade calls
The action usually has several steps, and all of them your online shop a store. A store may have many dif-
must be successfully completed. FreeTrade uses an ferent departments and subdepartments. For exam-
interesting technique to process these steps. Instead of ple, you might have two departments - one called
using nested ifs and ending up with highly nested Books, and another called Music. Each department
and hard-to-read code, it uses a nice feature of PHP4s can contain subdepartments and products. By default,
include() statement. there is a department called Root. All store depart-
In PHP4 a file that has ments are its subdepart-
been included can prema-
turely exit and return a value
FreeTrade is a fully-featured ments. You can place prod-
ucts in the Root department,
with a return() state- and highly configurable PHP too.
ment, like a function (this is A product is called an item
valid for PHP4+ only; PHP3 script for creating and main- in FreeTrade. A store user (a
taining e-commerce Web appli-

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


doesnt function like this). shopper) can add any item
As actions are included files, to their own shopping cart.
we may use return() if cations. FreeTrade uses the term bas-
any of the steps of an action ket when talking about the
fails. With this funcionality, we can organize the action shopping cart. When a shopper is finished adding all of
as a set of steps in if blocks. If any of them fails, the the items he plans to buy to his basket, he goes to the
others will be skipped. This improves readability and checkout page. On the checkout page, he is asked for
decreases the size of our code. his shipping and billing details. Finally, we save his
Once an action has finished executing, we include order and eventually process his payment in real-time.
the modules/configuration/ScreenInfo file and Some other important terms are attributes and vari-
try to find the requested screen definition. If the screen ations. I will try to explain them using an example. If
is not found (or one has not been requested), we show you are planning to sell shirts in your store, then a shirt
the default welcome screen to the user. The default is an item. This item can be different colors and differ-
screen is defined in the ent sizes. Color and size are referred to as attributes,
modules/configuration/global file, as men- and you are free to specify as many attributes as you
tioned earlier (the DEFAULT_SCREEN constant). The need. In our example, the shirts color variations may
welcome screen is located in modules/screen, just be red, blue, white and black. Size variations may be S,
like any other site screen. If we did find the screen def- M, L, and XL. Every variation may have an extra price
inition, we read the layout and any other definitions for attached to it (an XL shirt may cost an extra $2). You
the requested screen. are free to specify as many variations you need. You
Now we have the action results, the layout, and the can manage attributes and variations in the Attributes
screen content. We just need to print it out and were and Variations administration menu.
done. Thats all - the index.php script is over! Now that we know what attributes and variations are,
The workflow Ive described is a general one. Each lets go back to items again. If we want to sell our
page is processed this way, whether it be user login or example item (a shirt with different colors and sizes),
registration, adding an item to the shopping cart, or we must be able to give the selection of these attributes
ordering. to the shopper. For some less powerful e-commerce
scripts, the only way to offer a selection of attributes is
Real Life Tips to define a different item for each variation of each
attribute. This means that we would create an item for
FreeTrade has two sections. One section is for shop- a green and size S shirt, and another item for red and
pers and one is for administrators. When logged in as size XL shirt, and so on. We would need items for all
an administrator, you will have an additional menu combinations of sizes and colors. After that, just imag-
option (Admin Menu) in the left side navigation. This is ine how to change the price of that shirt! You would
a link to a set of protected pages for store administra- have to edit all items and change the price for every
tion. There you will find options for managing invoic- one of them. Obviously this is not a good approach.
es, departments, products, gifts, coupons, taxes, and so Lets introduce a new, well known term in e-commerce:
on. a SKU.

March 2003 PHP Architect www.phparch.com 24


FEATURES FreeTrade, e-commerce for developers

SKU stands for Stock Keeping Unit and is an ID asso-


ciated with a product for inventory purposes. Each SKU showRelationship ($Item, Cross-Sell,
in FreeTrade has external SKU information to help con- Similar Items:);
nect a stores offline business with its online business. showRelationship ($Item, Accessory,
Accessories:);
FreeTrade uses this number to identify an individual
product. A SKU is a subitem and it has its own name,
price, weight, inventory stock, and any number of Cross-Sell and Accessory are default relationships.
attributes and variations. An item has none of this. It To show any other relationship, you will need to add a
has only a name, a description and images. All addi- line for each new relationship youve created. The
tional information for an item is stored in its SKUs. showRelationship() function is defined in the
Each item has zero, one, or more SKUs. FreeTrade same file, so you are able to modify it to show related
allows you to make items with no SKUs, but such items items in the way you would like. The default way to
make no sense. As I said, you can select as many attrib- show related items is just a bulleted list of linked items
utes and variations as you like for a SKU. In our shirt names.
example, we can select all available colors and sizes for Items may have up to 3 images: Thumbnail, Graphic
our shirt. If there is more than one variation of any and LargeGraphic. The Thumbnail image is used in
attribute (for example, there are 4 colors), the shopper the department listing. The Graphic image is used on
will see a selection box with all available colors for this an items detail page. The LargeGraphic image is used
item and he will be able to select the color he likes. For in a pop-up window opened by clicking the Graphic

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


each attribute with more than one variation, FreeTrade image on the item detail page. None of these images
will show a separate selection box. In our example, the are required, so you can have items without having
shopper will see two selection boxes. One selection images. FreeTrade does not support image uploads in
box will be for color selection and the other will be for this version, which may be a problem for store owners
size selection. if they are not familiar with FTP. Of course, the beauty
You will probably have only one SKU per item, but of open source is that you can always just add the
sometimes it is good to have more than one SKU. For upload option. This would require changes in a few of
example, you may sell movies. You might have one the scripts for item administration.
SKU for the VHS version and another for the DVD ver- The checkout process takes a little getting used to at
sion of the same item. Note that in this movie exam- first, but I believe that this will be improved in future
ple you dont actually need to make two SKUs, since versions. As soon as the user steps into the checkout
you could define an attribute Edition with two varia- procedure, all of his shopping cart content is moved
tions: VHS and DVD. If, however, you need totally (cart becomes empty!) to a temporary invoice. This
different pricing for both of these editions, you will may be a problem if a user decides to exit from the
have to define two SKUs. checkout procedure to buy something else. Although
A SKU can have two prices; List Price and Sale Price. no data will be lost, the shopping cart will show only
The Sale Price is the price the item sells for, and its the items added after he exited from the checkout. The
only price used for calculating order totals. If both checkout process will collect all of the items and the
prices are defined, a user will see both prices. You may user will still be able to order all selected items, but it
leave List Price blank if you wish to show only sale can be really confusing. A user expects all items to
prices. remain in his shopping cart until the checkout process
You can use the List Price to recalculate the prices for finishes. Aside from this, the checkout procedure is
a whole department. If you implement a sale quite smooth.
(Administrate Sales option), you can set the Sale Price to A shopper with items in his basket can step into the
the List Price minus a defined discount. checkout page either from the basket content page or
FreeTrade supports cross-selling. For example, if you from the menu. FreeTrade shows the complete content
are selling mobile phones, cross-selling allows you to of his basket, along with a form for the shoppers
offer headphones, hands-free kits, and cases for a address. If the shopper is already registered and logged
phone model on that phones detail page. This can in, his address will be auto-filled. Form submission will
definitely increase sales. If you want to cross-sell (show transfer him to a billing page where he enters his billing
similar items and/or accessories on the item page), you information. FreeTrade will check the credit card infor-
have to define a relationship (Administrate Item-to-Item mation that a shopper enters here before transferring
Relationships option). Once a relationship has been him to the last step: the confirmation page. This verifi-
defined, youll need to specify all of the items in the cation is handled by the
relationship (Administrate Departments and Items). modules/utility/validateCard script. Note
In order to actually show related items to the shop- that this verfication only checks that the credit card
per, youll need to edit the modules/screen/item number is valid; no charges occur. If the card is
file. On line 240, you will find:

March 2003 PHP Architect www.phparch.com 25


FEATURES FreeTrade, e-commerce for developers

acceptable and the shopper confirms the order, able to use a third-party credit card processor, you
FreeTrade will save it into the database. could store the credit card information in the database
for a day or two until you process the order. At that
Extending FreeTrade point, you can mask all but the last 3 or 4 of the credit
card number digits, or you could just delete the credit
FreeTrade is meant for developers. It is very extensi- card information. I will give an example of how to
ble and flexible. The following is a practical example of make this work using MySQL.
how easy it is to extend the FreeTrade system to meet First, open the
your specific needs. modules/database/mysql/invoice file and add
By default, FreeTrade stores credit card information in the function in Listing 2.
the database table invoice_billing for each individual Now make a new file,
order. This is a problem, in my opinion, even when modules/action/CLEAN_INVOICE_CC, containing
using dedicated servers. Redundant data in a database the code from Listing 3.
is generally never good news, if not for reasons of effi- Finally, add a button for credit card cleaning in the
ciency, than for the problems data redundancy can modules/screen/edit_invoice file with the fol-
cause in the area of maintaining data integrity. There lowing:
are a few ways to avoid this problem. The best way is
to use a third-party real-time credit card processor and print(StartForm(
not collect credit card numbers at all. If you are not "edit_invoice",

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


'post',
'CLEAN_INVOICE_CC' , FALSE,
array("invoice_ID"=>
Listing 2 $_REQUEST['invoice_ID'])
/* )
** Function: cleanInvoiceCreditCard() );
** Input: INTEGER invoice, REFERENCE error
** Output: printSubmit(localize('Clean Credit Card' ));
** Description: removes credit card info from endForm();
** invoice_billing
*/
function cleanInvoiceCreditCard($invoice, &$error) This is a good example of a simple extension to the
{
global $DatabaseLink;
original FreeTrade behaviour. The store administrator
will now be able to remove the credit card number as
$invoice = (integer)$invoice; soon as it is no longer needed.
$Query = "UPDATE invoice_billing SET ";
By default, FreeTrade has no module for credit card
$Query .= "CreditCardNumber = \"[REMOVED]\" "; processing. FreeTrade is a developer product, and the
$Query .= "WHERE Invoice = $invoice"; authors assume that every developer will use the
if(!($DatabaseResult = mysql_query($Query,
method provided by the individual processor, as the
$DatabaseLink)))
{ parameters may vary from processor to processor. This
$error = mysql_errno() . ": " approach is fine for experienced developers, but begin-
. mysql_error(); ners may have problems, especially if the credit card
return(FALSE);
} processor has poor documentation. In order to add
return(TRUE); credit card processing, youll need to obtain the
}

Listing 3
1 <?php
2 /*
3 ** File: CLEAN_INVOICE_CC
4 ** Description: removes credit card billing record for an invoice
5 */
6
7 include_once(DATABASE_MODULE . "invoice");
8
9 $_REQUEST['invoice_ID'] = (integer)$_REQUEST['invoice_ID'];
10
11 if(!cleanInvoiceCreditCard($_REQUEST['invoice_ID'], $error))
12 {
13 $ActionResults[] = $error;
14 return(FALSE);
15 }
16
17 ?>

March 2003 PHP Architect www.phparch.com 26


FEATURES FreeTrade, e-commerce for developers

processors documentation and change the tion. If you want a well-writen script, with lot of features
modules/action/SUBMIT_ORDER file. and customization possibilities, give it a try.

Wrapping Up
About The Author ?>
There are a few things I would like to see included in
Vladan Zirojevic is a Serbian web developer, educated in Computer
FreeTrades future versions; for instance, better documen-
Science. He worked on more than 30 various PHP projects both as proj-
tation, modules for credit card processing, better search ect leader and part of the team. Some of them www.24sata.com,
functions, better reports, bulk import solution, and an www.poljubac.com, www.trebinje.com, www.mobilnimagazin.com...
image upload function. However, I do find FreeTrade to Currently, he is employed as a Senior Web Developer in SEENETIX,
be a usable product for developers in its current state. Belgrade. He can be reached at: vladan@webmaster.co.yu
FreeTrade is a great Open Source product, but its not
Click HERE To Discuss This Article
for everyone. It is a framework, not a plug-and-play solu- https://www.phparch.com/discuss/viewforum.php?f=7

A few words with Leon Atkinson Pixels (www.smashingpixels.com) who did the graphical
design work.
Leon Atkinson (http://www.leonatkinson.com), author of
FreeTrade have been a big fan of PHP and MySQL. What he
A great example no longer viewable is Restoration
is best known for is the book Core PHP Programming, first
Hardware. This site grew so successful, it outgrew FT. Its the

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


published in 1999. It was the first book in English about PHP.
nature of FT that once built, a site slowly drifts away from the
In 2001, he wrote Core MySQL book too. We asked him few
original codebase. Where as some software projects attempt
questions about FreeTrade.
to be a closed box with lots of knobs for customization, FT
attempts to be the engine you place in your own box. A FT
What are your plans about FT in next period, after FT3
site is craftwork rather than something that comes off an
final release?
assembly line. Another early site that demonstrates this drift
is www.dantz.com, which still uses FT at its heart.
Customers inspire new features in FT. Im an independent
Because anyone can grab the source code and put up a
consultant working with several Web development business-
site without any help, sites appear without me knowing. I
es. My partners bring project to me. I use what I have and
recently discovered Thrasher Magazine runs a version of FT
donate new code back to FT when appropriate. So, in a cer-
(http:// www.thrashermagazine.com). Sometimes
tain sense, new features are out there, beyond the horizon.
people post links to their own projects to the mailing list,
(Low visibility as people like to say regarding the economy).
such as VentureOut in New Zealand:
There are a few ideas floating around, waiting to be imple-
http://www.ventureout.co.nz/.
mented. Sean Farley, a frequent contributor, is working on
implementing an affiliate program. That is, a system that
Do you have any idea about how many FT e-commerce
allows the site operator to track sales referred from other
sites exists?
sites.
Amazon.com, being the pinnacle of online catalogs,
I dont have any hard numbers. I suspect its something
inspires new functionality. In fact, customers often make
like 100.
remarks like, I want the checkout process to be like
Amazon.com. However, its important to keep in mind that
In your oppinion, what is most often problem for
while Amazon.com is a great example of a mega-store, ben-
novices in FT, regarding e-commerce site development?
efiting from millions of dollars of development, FT is suited
for smaller online stores.
Most questions we get are about setup. Its assumed youll
For the past several months Ive been working on the third
find the configuration/global file and read the comments,
edition of Core PHP Programming. All the new features
but some people dont.
appearing in PHP inspires me to work them into FT. The
Then there are issues with handling credit card numbers,
database interface is always an interesting topic in open-
particularly with hosting sites on shared servers. A successful
source projects. Being flexible means more people can use
online store deserves a dedicated server with most of its ports
your project, perhaps contributing. On the other hand, I dis-
locked down. I find it hard to accept that a site that cant
like sacraficing performance to a lowest-common-denomina-
cover dedicated hosting costs needs to process credit cards,
tor approach. Thats why we have separate database mod-
but there are other methods. I try to encourge these lower-
ules. PostgreSQL support has been complete for some time
end sites to go with another payment solution, such as
now. Lately Ive been considering coding a module using the
PayPal.
dbx_* functions.
Do you have any particular suggestion for PHP devel-
I saw your A.G. Ferrari
opers planning to use FT?
(http://www.agferrari.com) site, its great. Do you
have some other examples of good FT sites?
Participate in the mailing list. There are several people
who have used FT for quite awhile and will answer questions
Thanks. I have to give due respect to Clear Ink
quickly.
(www.clearink.com) who ran the project and Smashing

March 2003 PHP Architect www.phparch.com 27


Use PHP?
Love PHP?

Live
PHP is an Open Source
PHP! (for three days)
scripting language with serious
technical muscle. No wonder its the

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


language of choice for Yahoo, Inc. and
over 8 million domains worldwide. Whether
youre a PHP pro or completely new to the language,
PHPCon East 2003 will take you to the next level.

Meet and mingle with the experts during two conference days

PHPCon East 2003 Speakers include:


Rasmus Lerdorf, Opening Keynote Speaker and Inventor of PHP
Zeev Suraski, Closing Keynote Speaker, CTO of Zend
Zak Greant, MySQL.com
Shane Caraveo, ActiveState, Inc.
Luke Welling and Laura Thompson, Tangent Technologies
George Schlossnagle, OmniTI

Technical learning at all levels

PHPCon has a full day of hands-on, technical


tutorials that offer something for everyone, including:
Beginning PHP
Developing MySQL Applications with PHP CHECK OUT THE PROGRAM
Performance Tuning PHP & REGISTER TODAY!
XML with PHP
PHP Web Services with SOAP http://www.php-con.com/pa
PHPCon East 2003
Park Central New York Hotel
New York City, New York
EAST2003 April 23 - 25, 2003

Tutorial and conference sessions are subject to change.


REVIEWS

phpLens
Published by Natsoft
REVIEW

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


Create, Paginate, Edit, and Search.

A nd now for the $64,000 question (someone should


really update these maximsthe money is not that
impressive anymore!): What happens when you intro-
The Cost:
$120 - $3,600 (US)
duce your PHP script to a database? If youre lucky and
have plenty of time (and money) on your hands, the Requirements:
PHP 4.06 or later
answer could be a well-built, dynamic website. If time Zend Optimizer
comes at a premium, however, you might end up with MySQL or Oracle
PHP4 sessions must be enabled
something thats all too common in the IT world: an
unmeetable deadline and, even worse, an unhappy Download Page:
client. http://www.phplens.com/lens/product/#download
This is where software like phpLens comes into play. LogiCreate Home Page:
The phpLens system is a complex, but not complicated, http://www.phplens.com
application server that can be used to build dynamic,
data-driven websites. Heard that one before? Hang on
to your hats. Database Accessibility
phpLens is a completely integrated, object-oriented
platform. It includes the ability to edit and manage The phpLens engine is written entirely in PHP and is
your HTML templates (Smarty, or otherwise) directly based on the well known ADOdb database abstraction
from a web browser, which makes creating different library developed by John Lim (who, incidentally, is also
views of your data easy and convenient. Figure 1 the technical lead of the phpLens team). Anyone who
shows one of the examples provided on the phpLens has ever used ADOdb knows how seamlessly it works
website - there are almost thirty examples there - where across a multitude of database systems, including
a complete calendar template has been created and MySQL, Microsoft SQL Server and Oracle, to name a
populated using a simple Smarty template and no few.
more than twenty lines of code. Incredibly, the same Perhaps one of the most impressive features of this
system can produce something as divergent as a bar software is its data manipulation functionality. Editing
graph, shown in Figure 2, while still using fewer than datasets is a breeze in both the standard and Hot
thirty lines of code.

March 2003 PHP Architect www.phparch.com 29


REVIEWS phpLens Review

editing modes. The standard mode refers to editing a full authentication system. This is left to the develop-
records one-per-page. Hot editing refers to a er to implement using their own choice of tools, such
method where multiple records can be modified at the as Smarty.
same time using an interface similar to that of a typical Finally, the phpLens documentation includes a step-
electronic spreadsheet. The data editing engine auto- by-step procedure for securing the folder in which
matically recognizes the data type of each field, includ- phpLens resides, without impeding its functionality.
ing enumerative types (managed through drop-down
lists). Its also possible to specify very complex relation- Documentation and Support
ships between tables.
phpLens comes with an excellent set of user documen-
Security Features tation. By user I actually mean developer, since
phpLens doesnt force its adopter to utilize a particular
phpLens offers both simple and sophisticated security end-user interface.
features. Some of the simple features include the abil- The entire documentation set is available online. It
ity to password-protect the dynamic editing functions, follows a very natural path, from installation all the way
and to automatically filter user input for potential dan- down to the smallest security details. In addition, the
gerous HTML and SQL statements. phpLens website includes a complete reference of all
One of the more sophisticated features is the check- the objects, properties and methods used by the pack-
summing of records to ensure that the data is not trans- age.

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


formed in-transit by a third party. Another is the finger- Although support for the product is offered on a
printing of data, using the md5 digest algorithm, in commercial basis, the phpLens website includes an
order to prevent a malicious user from creating ficti- astounding number of examples. Also available are a
tious records without the proper authorization. FAQ area and a very active peer-support forum (all
It should be noted that phpLens does not implement developed using phpLens, of course).

Figure 1

March 2003 PHP Architect www.phparch.com 30


REVIEWS phpLens Review

Packaging and Limitations to its object-oriented design. Incidentally, if youre con-


cerned that it might be difficult to find a hosting
phpLens comes in three flavours: Basic, Advanced and provider that uses the Zend Optimizer, the phpLens
Enterprise. The Basic version supports all the data website includes a list of ISPs who do.
manipulation and presentation layer features, with the
exception of creating and editing records. The The Bottom Line
Advanced and Enterprise versions, on the other hand,
support all of the features. The only difference between phpLens is not an expensive product. Considering the
the two is the number of database systems they each standard set by other similar applications, it offers an
support. excellent set of functionality for a very reasonable price.
Because it is based on pure PHP code, phpLens runs If you need to set up a complex, data-driven website
on pretty much any platform PHP runs on. The only and youre in the game for the long term, phpLens is
requirement is the presence of the Zend Optimizer, as worth serious consideration. Its object-oriented struc-
all of the phpLens scripts and include files are encrypt- ture is a good bet for the future, and will easily fit into
ed using the Zend Encoder. This means that you wont the PHP 5 philosophy once the new version of the PHP
be able to view and modify phpLens source code, platform becomes available.
although this shouldnt be much of a problem thanks
php|a

Figure 2

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)

March 2003 PHP Architect www.phparch.com 31


FEATURES

Blazing Site
Performance Using
Objects and Sessions
FEATURES

By Peter Moulding

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


Object Oriented Programming (OOP) is in! PHP 4 works with OOP, and PHP 5 will make it perfect. Likewise, PHP
makes using sessions easy. Sessions pass state information from one Web page to another. You can place almost any-
thing in a session, including objects. By marrying the two and storing objects in your sessions, you can take advan-
tage of run once, use everywhere processing.

use sessions, PHP adds a file or database read at the


I n this article Ill look at ways to match the best fea-
tures of objects with the best features of sessions, to
build fast and efficient dynamic Web sites. Ill talk at
start of your script, and a file or database write at the
end of your script. The read should be instantaneous
great length about performance considerations, and because the data should be cached in memory from
look at ways to keep your session data small, and your the previous script execution. The write will slow down
sites performance blazing. All of this theory will be sup- your script, but it usually occurs after you send your last
plemented with a wealth of practical examples (and chunk of data to the visitors browser, so the visitor
some neat little tricks) which I hope youll find useful. should never be impacted by the write time.
Objects are based on classes, and after looking at the
programming books in the local bookshop, it seems Oracle, MySQL or Files, Oh My!
that there are around 287 ways to write classes. In this
article, I will skip the style information and go straight Session records can be stored in files or databases.
to the core structural issues relevant to building your File access is fast on most systems. Microsoft NTFS
classes. The information provided is useful for writing writes files in 4KB chunks, while Linux ext2 uses 8KB
all classes, whether used in sessions or not. chunks. In effect, when you use files there is little differ-
The code included in this article has been tested with ence between writing a short session record containing
Web sites using PHP 4.2.x and 4.3.0. Some examples only a 32 bit session ID versus writing a 2KB session
are stripped-down classes representing techniques used record containing a large object. If your files are on a
in large-scale commercial sites. Others will show you RAID device, the RAID device might be writing 64KB
what does not work. The examples are tested using ses- chunks. This means you can go crazy with storing big
sions stored in files, as well as MySQL and Oracle data- objects in the session, without worrying too much
bases. You might decide to change your session config- about the consequences.
uration, or switch databases, after reading this article.
REQUIREMENTS
Session Performance
PHP Version: 4.3 or Above
O/S: Any
One of the first questions you might ask is, How do
Additional Software: N/A
sessions perform when filled with objects? When you

March 2003 PHP Architect www.phparch.com 32


FEATURES Blazing Site Performance Using Objects and Sessions

Databases store rows in pages. Pages are chunks of the object is deserialized out of the session. If we can
disk, usually 4KB long. If your session data is 50 bytes minimize the amount of code that needs to be loaded
long, a page stores 80 rows. If your object blows the and compiled in order for an object to be deserialized,
session data out to 2KB, a page stores just 2 rows. we gain efficiency.
Databases are more space efficient than files. To this end, I prefer to split code into a structure
However, from a performance perspective, a page write based on usage. One block is the code used at the
is the same as a file block write, so a database should start, then a block used in the middle and, finally, a
not slow down your sessions. block used at the end. It is a bit like splitting a web page
MySQL is slower than files on some systems and a lit- into header, body and footer, or a file access operation
tle faster than files on other systems, but generally there into open, read and close. This approach is a good first
should be almost no measurable difference between a step towards cutting large blocks of code into small,
50 byte session row and a 2KB session row. manageable chunks, and has the added benefit of
Furthermore, the time difference is linear, which means building memory efficient objects. When thinking
that adding an extra object produces little difference. about storing objects in sessions, memory usage effi-
Although MySQL lets you choose from three different ciency also means session usage efficiency.
table types, the default native table is the best choice as PHP classes offer a class constructor to perform the
it has no transaction processing overhead. initial work to be performed by the class. Unfortunately,
Oracle is different. Oracle has 4KB text fields, so any- the constructor code remains with the class throughout
thing larger has to go into a CLOB (Character Large its life. If our constructor code is huge, we can consid-

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


Object Binary). Tuning Oracle for optimum perform- er putting the bulk of the constructor code into a sep-
ance is painful, and tuning the use of CLOBs is even arate class. In the upcoming example classes, the class
more difficult. Oracle sites report overheads of 50% or objective_foot has all of the construction code in
more, unless you use techniques specific to Oracle. To the constructor. objective_foot2 is a reconstructed
me it seems silly to use database abstraction classes to version of objective_foot where the constructor
drive Oracle PL/SQL, or anything else that can work contains just a single include of
only on Oracle. Oracle is great for transaction-based objective_foot_start. The
processing, but sessions do not need transactions! If obective_foot_start class includes all of the com-
you have an Oracle expert on board, you can place ponents needed to build the data for the object.
your session records in Oracle. Otherwise, leave your The idea here is that the expensive part of the class,
sessions in files. the constructor, is only needed once (on the first page
You may also consider leaving your sessions in files if of the site). Why carry it around with us when all we
your database is on a different box from your Web serv- want is the data? Splitting the code into separate use
er and you cant use a local database for sessions. once and use on every page classes will make both
Whatever you do, do not place your sessions in a data- compilation and caching more effective.
base on another machine, or even on a disk shared Place all of your difficult-to-write code into the use
across the network, as the added network and system once class. Keep the use on every page class sim-
complexities can increase overhead by 400% or more. ple, easy to understand, and easy to maintain. Once
your use everywhere class actually is used every-
Optimizing Code Structure for where, making changes will be difficult. You will reduce
Sessions maintenance problems by organizing your code based
on usage.
When you place an object into a session, only the There is another objective for splitting your code:
data from the object gets stored in the session. When providing a layer of abstraction to afford yourself the
the session data is written out, the object is passed flexibility of handling future changes in the logic sec-
through serialize(), which squashes the object tion of your code. The use on every page class
variables into a single string. Code does not take up should contain just data. File processing and other spe-
space in the session, so why would we want to focus on cialized logic code should be in another class so that it
keeping the code small? can be easily altered. Next week you might have to
Basically, we want small code because it has to be replace file processing with databases, or text input
compiled at some point. On many web sites, code is might become XML. If you can split your code along
compiled every time a script starts. Imagine a class with these logic vs. data lines, you gain a level of abstrac-
several thousand lines of code being compiled for every tion where the users of the use on every page code
web page. An optimizer will cache the compiled code, work only with data, and are not affected by changes
but not every ISP uses an optimizer, and optimizers to the logic.
cannot cache all scripts.
When we store an object in a session, the class code
will still need to be compiled again on each page when

March 2003 PHP Architect www.phparch.com 33


FEATURES Blazing Site Performance Using Objects and Sessions

An Example or Two The Example Classes

I would like to show you a full content management objective_html is the base class for all of our
system, but the code listing would flood this magazine other HTML classes. It provides a common $html vari-
and squeeze out the interesting stuff! The following able and the get_html() method. The class is stored
examples represent a real set of code that reads XML in a file named objective_html.class in the sites
and database sources to build page content, and are include directory and is shown in Listing 1.
minimal examples I use when teaching PHP. The next few classes are derived from the objec-
In actual web sites, the code builds input from many tive_html class and are shown in Listing 2 through
sources, and the initial class constructor grows into 6.
multiple includes, wrapped in miles of logic. Individual The objective_foot class includes all the previ-
inclusions can have thousands of lines of code which ous classes in one big constructor and is shown in
find, read and decode input. For this discussion, all we Listing 7.
need are a few inclusions with a sample line of code in The Result
each.
Lets pretend we have the task of creating a common Listing 8 ties it all together. The image in Figure 1
footer for all the web pages at petermoulding.com and shows the fruits of our labor. This might seem like a lot
we are told to use existing formatting classes. This of work for this small result, but remember that the
footer is a copyright notice centered on a blue back- examples are representative of code delivering a whole

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


ground.
Youll notice that there are a number of small classes
Listing 2: objective_font class
in this example and you might wonder why. We want
an example where there is a big chunk of code used 1 <?php
2
once, and one small object which is used repeatedly. In 3 require_once('objective_html.class');
this example, all of the classes are used when the visi- 4 class objective_font extends objective_html
tor requests the first page from the site. For every other 5 {
6 function objective_font($text, $color)
page request, only the class containing the formatted 7 {
HTML is needed. 8 $this->html ='<font color="'.$color.'">'
9 . $text . '</font>';
10 }
Listing 1: objective_html class 11 }
12
1 <?php
13 ?>
2
3 class objective_html
4
5 {
Listing 3: objective_link class
6 var $html; 1 <?php
7 2
8 function objective_html($html) 3 require_once('objective_html.class');
9 { 4 class objective_link extends objective_html
10 $this->html = $html; 5 {
11 } 6 function objective_link($text, $href)
12 7 {
13 function get_html() 8 $this->html = '<a href="' . $href . '">'
14 { 9 . $text . '</a>';
15 return($this->html); 10 }
16 } 11 }
17 } 12
18 13 ?>
19 ?>

Listing 4: objective_td class


1 <?php
2
3 require_once('objective_html.class');
4 class objective_table extends objective_html
5 {
6 function objective_table($text, $cellpadding, $width)
7 {
8 $this->html = '<table cellpadding="' . $cellpadding . '"'
9 . ' width="' . $width . '">' .$text . '</table>';
10 }
11 }
12
13 ?>

March 2003 PHP Architect www.phparch.com 34


FEATURES Blazing Site Performance Using Objects and Sessions

web page constructed from 30 or more sources. Getting Objects into the Session
Now lets see what the code looks like when we apply
the optimizations we talked about earlier. The objec- When you manually start the session in your code
tive_foot_start class, shown in Listing 9, contains with session_start(), placing objects in the ses-
all of the constructor code that can been removed from sion is easy. On your first page, you can use the follow-
the objective_foot class. If any of the code in this ing code to create an objective_foot object in the
class prevents your optimizer from caching the com- session.
piled code, you will lose caching on only one page (the
first one). require_once('objective_foot.class');
Listing 10 shows the objective_foot class with all session_start();
$_SESSION['foot'] = new objective_foot();
the constructor code replaced by one print $_SESSION['foot']->get_html();
require_once(), including the
objective_foot_start class. When this class is
compiled for your second and subsequent pages, the On the first page, the require_once() and the
compilation time is reduced and there is more chance session_start() can go in any order. At the end of
your optimizer can cache this simple class. the first page, PHP will run $_SESSION[foot]
Now, lets see how to put these objects into the ses- through serialize() to produce a string for storage
sion. in the session record.
Life is different on the second and subsequent pages.

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


Figure 1: Output from objective_foot class

Listing 5: objective_td class


Listing 6: objective_tr class
1 <?php
2 1 <?php
3 require_once('objective_html.class'); 2
4 class objective_td extends objective_html 3 require_once('objective_html.class');
5 { 4 class objective_tr extends objective_html
6 function objective_td($text, $align, $bgcolor) 5 {
7 { 6 function objective_tr($text)
8 $this->html = '<td align="' . $align 7 {
. '" bgcolor="' . $bgcolor . '">' 8 $this->html = '<tr>' . $text . '</tr>';
. $text . '</td>'; 9 }
9 } 10 }
10 } 11
11 12 ?>
12 ?>

Listing 7: objective_foot class


1 <?php
2
3 require_once('objective_html.class');
4 class objective_foot extends objective_html
5 {
6 function objective_foot()
7 {
8 require_once('objective_link.class');
9 $link = new objective_link('Peter Moulding', 'http://petermoulding.com');
10 require_once('objective_font.class');
11 $font = new objective_font('Copyright ' . $link->get_html() . ' 1996 - 2003', 'white');
12 require_once('objective_td.class');
13 $td = new objective_td($font->get_html(), 'center', '#000033');
14 require_once('objective_tr.class');
15 $tr = new objective_tr($td->get_html());
16 require_once('objective_table.class');
17 $table = new objective_table($tr->get_html(), 10, '100%');
18 parent::objective_html($table->get_html());
19 }
20 }
21
22 ?>

March 2003 PHP Architect www.phparch.com 35


FEATURES Blazing Site Performance Using Objects and Sessions

The session_start() on the second page will grab further to solve the problem of including unnecessary
the string from the session record and run the string classes on every page.
through unserialize() to recreate the object in the Close your eyes and visualize a complete, perfect
$_SESSION[foot] variable.. Remember that content management system written to do absolutely
objects require code, and code is not stored in serial- everything you want (and everything you will ever
ized objects. The unserialize() function has to read want). The code is optimized to include the bare mini-
the objects class code and add the code back to the mum number of classes on every page. The trouble is
object, which means that you have to be sure to that the bare minimum includes hundreds of classes,
include the class before using session_start(). This with most classes used only on a few pages. In front of
works: your session_start() are 200 lines of
require_once(). This would normally be quite a
require_once('objective_foot.class'); problem with automatically started sessions.
session_start(); Here is a way to prevent including every class on
print $_SESSION['foot']->get_html();
every page which also solves the problem of
session_start() happening before your code gets
but this does not work: a chance to include any classes.
On your first page, write:
session_start();
require_once('objective_foot.class'); require_once('objective_foot.class');

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


print $_SESSION['foot']->get_html(); $foot = new objective_foot();
$_SESSION['foot'] = serialize($foot);

The next question is what to do when your ISP sets


PHP to automatically start sessions and will not change (Note that if you want to use this method and you
the setting for you. You should consider changing your dont have sessions automatically starting, you will
ISP. :-) need a session_start() call at the beginning.)
There is something less dramatic than changing your
ISP, and it is something you might want to do anyway Listing 10: objective_foot2 class
to lower your overhead. I mentioned before that you
1 <?php
might want to restructure in order to minimize the 2
code you include on every page. You can go one step 3 require_once('objective_html.class');
4 class objective_foot2 extends objective_html
5
Listing 8: Example usage of objective_foot class 6 {
7 function objective_foot2()
1 <?php 8 {
2 9 require_once('objective_foot_start.class');
3 require_once "objective_foot.class"; 10 $foot = new objective_foot_start();
4 11 parent::objective_html($foot->get_html());
5 $foot = new objective_foot(); 12 }
6 print $foot->get_html(); 13 }
7 14
8 ?> 15 ?>
Listing 9: objective_foot_start class
1 <?php
2
3 class objective_foot_start extends objective_html
4 {
5 function objective_foot_start()
6 {
7 require_once('objective_link.class');
8 $link = new objective_link('Peter Moulding', 'http://petermoulding.com');
9 require_once('objective_font.class');
10 $font = new objective_font('Copyright ' . $link->get_html() . ' 1996 - 2003', 'white');
11 require_once('objective_td.class');
12 $td = new objective_td($font->get_html(), 'center', '#000033');
13 require_once('objective_tr.class');
14 $tr = new objective_tr($td->get_html());
15 require_once('objective_table.class');
16 $table = new objective_table($tr->get_html(), 10, '100%');
17 parent::objective_html($table->get_html());
18 }
19 }
20
21 ?>

March 2003 PHP Architect www.phparch.com 36


FEATURES Blazing Site Performance Using Objects and Sessions

On your second page, write: could populate your session with variables like:

require_once('objective_foot.class'); $_SESSION['foot']['database']
$foot = unserialize($_SESSION['foot']); $_SESSION['foot']['text']
$_SESSION['foot']['xml']

(Once again, if you dont have sessions automatically


starting, you will need a session_start() call after If $x = xml, the following code would bring in the
the require_once().) correct object for the current page (assuming that the
All you are doing is replacing manual sessions and classes are called objective_foot_database,
automatic serialization with automatic sessions and objective_foot_text, and
manual serialization. Because you control the unserial- objective_foot_xml).
ization, you can include the class before
unserialize() . You also get to choose which require_once('objective_foot_'. $x .'.class');
$foot = unserialize($_SESSION['foot'][$x]);
objects are unserialized for a given page. You include
only the classes you need and unserialize only the relat-
ed objects. Data, Not Code
Do not worry about the objects you do not unserial-
ize on a specific page because they will float from page Some people push Object Oriented Programming as
to page in the session record for as long as you want. a way of storing data together with the processing

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


When you are finished with an object in the session, required for the data. There are times when it is better
simply unset it like so: to keep all data out of your class (and in some cases
keep all code out of classes). The best practice is to sep-
unset($_SESSION['foot']); arate the data class from the access class. The data
object in the session stores the minimum identifiers
required to access the data, and the access method is
There are a few benefits and a couple of traps to
in a separate class. This leads to the well-proven prac-
watch out for when performing your own serialization.
tice of separating code from data.
One trap is deciding when to create the object. There
Think of a simple class to perform calculations using
is no reason to create an object before you have data
Pi, which has no exact value. Around 150 BC, Ptolemy
for the object. If you do not create all your objects on
said Pi had the value 3.1416, but by 1600 Van Ceulen
the first page, test subsequent pages for whether the
was up 35 decimal places after the 3. When you create
object exists before using unserialize(), like so:
a class to calculate the area of circles and the like, you
want to keep the value of Pi out of your class so that
If (isset($_SESSION['foot']))
{
your class is not restricted to the accuracy of the value
$foot = unserialize($_SESSION['foot']); you choose. The person using your class can then add
} their own value to give them the order of accuracy that
they want.
Another trap is letting PHP automatically convert You might then extend the base, code only class
$_SESSION[foot] to $foot through the regis- with classes adding nothing more than the value of Pi
ter_globals feature. Make sure your php.ini sets to various degrees of accuracy. By 1873, Pi was up to
the register_globals directive to off. If your 527 decimal places (actually, it was 707 decimal places,
ISP will not turn off register_globals, change of which 527 were correct). 527 places is beyond PHPs
$_SESSION[foot] to integer mathematics, and so youd need to switch to
$_SESSION[foot_string] so that bcmath - http://www.php.net/manual/en/ref.bc.php.
register_globals will not set your code off on the The approach of separating data from code, and then
wrong $foot. adding the data to the code by extending the code
class, does not fit our design targets for objects stored
in sessions, because you end up with all the code
Selective Object Usage
included in the data object. How do we marry the two
When you control unserialize you also open another approaches?
option. Pretend our content management system has The answer is to make the code class use the data
to read articles dated 2001 from text files, articles dated class by reference. The tiny data class produces a tiny
2002 from XML files, and later articles from XML in a data object to sit in the session. The code class then
database. You could have three versions of an object in uses the data class when needed. If you have Pi in class
the session. For instance, if our example pi, and your calculations in class calc, your initial page
objective_foot class came in three versions, you will set Pi using the following code.

March 2003 PHP Architect www.phparch.com 37


FEATURES Blazing Site Performance Using Objects and Sessions

the name. If and when you feed the name to a second


require_once('pi.class'); software application, move the name out of both sets
$_SESSION['pi'] = new pi(); of code into your special session object.

The pages performing calculations can pass the pi When Size is Important
object into calc via calcs constructor.
After reading this article, you are ready to attack your
require_once('calc.class'); code and push all your objects into sessions, but
require_once('pi.class'); remember the 4 or 8KB disk block size I mentioned ear-
$calc = new calc($_SESSION['pi']);
lier. Use that size as an arbitrary limit to curb your
enthusiasm. I sometimes put a 10 MB string into a ses-
This approach fits a site with many discrete values sion to pass the string from one page to another, but I
shared over all pages. The value of this approach do not leave the 10 MB in the session, since that would
increases if each data item requires complex and slow down every page. Here are some guidelines you
unique code to access the data. On a commercial site, can start with to loosely govern the space consumed by
you might use this approach when you have to retrieve objects in sessions so you can keep your sessions light.
a currency conversion rate from one source, and a com- Place the following code in a test page.
modity price from another site. You might want to get
the current rate when a person logs on, but then keep <?php

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


session_start();
the rate constant during several pages of calculations, a $_SESSION['x'] = 'string';
task ideal for objects in sessions. ?>
Now that weve seen how to effectively store and use
data objects in the session, lets look at another exam- Now run the page and look at the session file. The
ple. code adds the following 13 byte string to your session.
Abstraction or Distraction. x|s:6:string;

ADOdb and other PHP software products aim to give You can see that one byte is used for the variable
you a level of abstraction between your code and the name. Clearly you do not want to give your frequently
rest of the world. ADOdbs aim is to free you from writ- session-stored variables long names.
ing code specific to a database. Most abstractions Place the same variable into a class and look at the
struggle with configuration issues. In the database area, result. Listing 11 shows a test class and the object cre-
MySQL and PostgreSQL use standard dates, while ation.
Oracle uses something different. To deal with this, you Below you can see the 46 byte string result from the
are faced with a few options. You could write PHP code sessions file. The class name and the variable name take
to format your dates for a specific database, use up space, so keep both names short.
ADOdbs special date functions, or use a configuration
option to make Oracle use a modern date format. How test|O:10:test_class:1:{s:1:x;s:4:d
can objects stored in the session help? ata;}
The date format string in ADOdb is a prime target for
a small, frequently used data object that you can set You can make your code as elaborate, or weird, as
once and then load into a session. Your database name you like because the code does not take up space in the
and other configuration details could also go in the session. Listing 12 shows a class with an unnecessarily
same object. The advantage increases when individual long bit of code in the middle. The class is followed by
users have separate requirements. As soon as your web the object creation.
site goes international you will get visitors using coun- The code produced the following string in the session
try specific formats for dates, times, currencies and file. Note that none of the literal values or work fields
postal codes. All of these items fit the object-in-session use space in the session.
ideal of small size, frequent use, and specificity to each
user. t|O:1:t:1:{s:1:x;s:4:aaaa;}
Some data items, however, are just distractions. A vis-
itors country code will be useless if your site has no OK, You Can Add Code
country-specific code. In order to use these little data
objects, look for items that are used after the user logs The previous examples feature mainly data in the
on, and items shared among software. If the database objects stored in the session. What can you do if you
name is only ever used by ADOdb, let ADOdb handle have really slow code access, do not have a code opti-

March 2003 PHP Architect www.phparch.com 38


FEATURES Blazing Site Performance Using Objects and Sessions

mizer, or just want to show off to friends? You can place


your code in the session! This is a class act that could Note that this will only work when you control the
save you access time if you have to retrieve code from unserialization. If you can access the
a remote network. $_SESSION[c_class] variable, the session has
Put the following statement in a test page. already been started and the object has been unserial-
ized. Unfortunately the object would not have been
$_SESSION['c_class'] = " properly unserialized because its class was not yet
class class_in_session declared (a quick print_r() on $_SESSION will con-
{
function class_in_session() firm this). Its a little like the chicken coming before the
{ egg. If you control unserialization, however, you can
print('class_in_session: declare the class with eval() and then unserialize the
it works!');
}
object.
}";
One of the first questions you
Now you have a classs code stored in your session. If might ask is, How do sessions
that class just came across a slow link from your server
in Ivigtut, Greenland, you really want to save rereading perform when filled with
the code for every page. While this example sounds
stupid now, the move to web services means we are
objects?

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


using code on a remote server that may in turn use a
The File Open Blues
remote server serving from an even more remote serv-
er. You may never know how many servers are in the
Storing class code in sessions reminds me of another
chain between you and the real data source.
way to save time. The overheads mentioned in this sec-
So, how do you use the class locally? Try the next
tion are true of almost every operating system ever
line.
built. Ill mention a script including 100 classes, but if
that seems too many for you, prepare to be shocked by
eval($_SESSION['c_class']);
Small Incremental Class programming. The fully nor-
malized code for a content management system would
eval() evaluates the string as PHP, exactly the same use more than 1,000 SIC classes, with most included on
as if you had saved the string in a local file, then includ- every page. You are about to suffer the File Open Blues.
ed the string via include(). File access takes O + (N * R) seconds where O is some
Now you can place your object in the session using tiny amount of time to open the file, N is the number
the following code. of file blocks read, and R is the block read time. Reads
from RAID are faster than normal disk, and reads from
$_SESSION['c_object'] = new class_in_session(); cache beat RAID. Whatever you use for your files, one

On subsequent pages you must create the class Listing 12: More complex object
before you access the object, so use the following code serialization example
before you access the object.
1 <?php
2
eval($_SESSION['c_class']); 3 session_start();
4
5 class t
Listing 11: Simple object serialization example 6 {
7 var $x;
1 <?php 8 function t()
2 9 {
3 session_start(); 10 $z = '';
4 class test_class 11 for($i = 1; $i <= 4; $i++)
5 { 12 {
6 var $x = 'data'; 13 $z .= 'a';
7 14 }
8 function test_class() 15 $this->x = $z;
9 { 16 }
10 } 17
11 } 18 }
12 19
13 $_SESSION['test'] = new test_class(); 20 $_SESSION['t'] = new t();
14 21
15 ?> 22 ?>

March 2003 PHP Architect www.phparch.com 39


FEATURES Blazing Site Performance Using Objects and Sessions

thing is constant: O is greater than R. Opening a file the first files to have their directory cached. You can
costs more than reading a block because the operating place all your classes in the session without even the
system has to find the file, check access privileges and overhead of opening a class collection file.
sometimes log the open. File opens are the curse of Some ISPs have fast session file access but pathetical-
object oriented programming. ly slow access to libraries and databases. You can get
If you did not have file opens you could place every your classes and objects to the front of the access
class in a unique file of the same name. Your objec- queue by placing both in the session record. Of course,
tive_foot class would go in a better approach is to change your ISP. The best
objective_foot.class. PHP could have a php.ini approach is to try this technique first, list your coding
setting that says to include class foot from directory accomplishment on your resume, then change your
/classes/, another to tell PHP you are using the exten- ISP.
sion .class, and another to tell PHP that the new opera-
tor should automatically include the class. The code: Conclusion

require_once('objective_foot.class'); Session records have a long history dating back to


$foot = new objective_foot(); database-driven online systems used in the late 70s.
Back then, the equivalent to Object Oriented
would be reduced to: Programming was named modular programming.
Programmers experimented with storing modules with

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


$foot = new objective_foot(); data in session records. They proved that the technique
was useful, but not practical with the programming
languages then in use. PHP makes the technique so
Lets jump back to reality. If you take 20 classes, place simple that even I can do it! Storing objects in sessions
them in separate files, then run a script requiring all 20 is a simple, proven technique for squeezing maximum
classes, your script will run like a dog with a broken leg. performance from small amounts of frequently-read
Try the same script including 100 classes, and no mat- user-related information.
ter how small the classes, the script will run like a dog
with 3 legs broken. Now copy all the classes into one
big include file and test again. Pow! You are back to the About The Author ?>
speed of light. Each file open costs you several times Peter Moulding started building computer systems about the time when
more overhead than each read. That is why just about people realised mainframes could be used as online information systems
every language allowing modular code also provides a instead of just number crunching. He helped large companies discover
way to include collections. GML (The grandparent of HTML and XML), SQL, develop user interfaces,
and service level agreements. Then he jumped ship to use Apache and
Like a collection, your session record is a great place PHP on a PC because "I like replacing million dollar mainframes with
to store your 100 tiny classes without the file open $10,000 PCs". Peter experiments with PHP and MySQL at
overhead. Your session will always be opened, so you petermoulding.com.
are already paying that price. Besides, the cost of open-
ing the session is minimal because sessions are usually Click HERE To Discuss This Article
https://www.phparch.com/discuss/viewforum.php?f=8
set up with minimum access controls and are among

Dynamic Web Pages


www.dynamicwebpages.de
sex could not be better |
dynamic web pages - german php.node

news . scripts . tutorials . downloads . books . installation hints

March 2003 PHP Architect www.phparch.com 40


FEATURES

Writing an RSS
Aggregator With PHP
FEATURES

By Marco Tabini

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


Tired of scouring the web for your news? Want to keep abreast of your favorite blogs without using software that
doesnt work the way you want and costs you money? A few lines of PHP could be the solution youve been looking
for.

Weblogs (or blogs, as they are commonly called) have ble for an end user to aggregate them in any meaning-
rapidly become commonplace on the Internet. Even ful way.
though they started as little more than online journals
and diaries, they have rapidly become everyones news XML To The Rescue!
and editorial outlets. Many people have their own
favorite blogs that they visit on a daily basis looking At the end of 1999, Netscape needed a format to dis-
for interesting news bits, opinions, funny or insightful tribute information about the news channels that
stories, and so on. their browser then supported. They came up with an
Possibly the most significant feature of blogs, howev- XML system based on (and compatible with) the
er, is the concept of a newsfeed. A newsfeed is a data Resource Definition Format (RDF) created and main-
file that contains information about the articles pub- tained by the W3C, and called it RDF Site Summary
lished by a blogs authorsort of a ticker edition of (RSS).
the blogs contents that can be picked up remotely and In 2000, weblog pioneer Userland Software picked
provide an at-a-glance overview of what is available on up RSS and greatly enhanced its features, distancing it
it. from RDF (although maintaining the XML angle) and
Newsfeeds are nothing new, really. News organiza- including some of the functionality from Userlands
tions have been using them in one form or another to own scriptingNews format. Although Netscape eventu-
deliver information to their clients for decadesfirst ally abandoned their RSS efforts, Userlands version of
through telex and now through the Internet. Some RSS, now dubbed Rich Site Summary, has been used
organizations, like the infamous PointCast, even used consistently as a means to syndicate a websites con-
newsfeeds to deliver information to the end users desk- tents.
tops. In all these instances, however, newsfeeds were
REQUIREMENTS
delivered on a one-off basis, using proprietary formats
that varied significantly from provider to provider. As PHP Version: 4.1 and Above
such, their usefulness was somewhat limited by the lack O/S: Any
of a common standard that would have made it possi- Additional Software: XML Parsing Extension

March 2003 PHP Architect www.phparch.com 41


FEATURES Writing an RSS Aggregator With PHP

My personal opinion of XML is that its the Pokmon into play. An RSS news aggregator is, simply put, an
of computer science (boy, this is sure to earn me some application capable of polling the information syndicat-
flames)I used to think that someone got out of bed ed by an arbitrary number of RSS sources and consoli-
one day and thought: lets take a simple conceptsay, dating it into a single news stream.
comma delimited filesand turn it into a monster so Aggregators have the power to revolutionize the way
complex that we can build a whole industry around it. we access and absorb news from the world that sur-
For a while, XML was hailed as the Next Big Thing and rounds us. Instead of having to continuously scour the
you could find it everywhere, just like Nintendos three- Web looking for news stories, one only needs to access
frame-a-minute cartoons. When I first found out that his news aggregator and have all the information he
RSS was based on XML, therefore, I cringed at what lay needs at his fingertips.
ahead. For me, adopting a news aggregator has meant
Luckily, I was wrong. RSS is an excellent example of spending less time finding news and more time reading
an application for which XML is a perfect choice, given it. Added to the often openly frank and unfiltered
the wide variety of different computer systems that nature of blogs, aggregating news gives me the oppor-
need to use it. Its greatest advantage is its simplicity; tunity to both receive information from my favorite
the contents of an entire website can be easily digested news sources on a daily basis and compare my opinions
into a single RSS file that contains but a few XML ele- with those of other people who share my ideasor
ments, and the code for generating an RSS script is very whose opinions are antipodean to mine.
simple to write in pretty much any language. Unfortunately, my experience with news aggregators

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


has been less than a pleasant one, as I never seem to
Aggregating RSS find one that satisfies my needs. As such, Ive come up
with a simple aggregator of my own, written in PHP,
With a common format to syndicate news and blog that can be easily modified to suit pretty much any
contents, the concept of news aggregator has come requirement.

Figure 1: What's Inside an RSS Feed?

An RSS feed is essentially a simple XML file that contains the following elements:

A Channel specification, which, in turn can contain several sub -elements, such as:
o A Description of the news source
o A Title of the feed
o A Link to the feed's homepage
An arbitrary number of Items, one for each story that the feed carries. Although there is
no preset limit to the number of Item elements in a feed, the specifications recommend
that no more than fifteen be returned. Each item can contain:
o A Title of the item
o A Description
o A Link to the item's referenced page

For example, the following is a simplified example of php|a's RSS feed:


<channel rdf:about="http://www.phparch.com/">
<title>php|architect - The Magazine for PHP Professionals</title>
<link>http://www.phparch.com/</link>
<description>The Monthly PDF Magazine Dedicated to PHP</description>
<items>
<rdf:Seq>
<rdf:li rdf:resource="http://www.phparch.com/news.php?id=92" />
</rdf:Seq>
</items>
</channel>
<!-- Start RSS-Items -->
<item rdf:about="http://www.phparch.com/news.php?id=92">
<title>Writing Efficient PHP Code</title>
<link>http://www.phparch.com/news.php?id=92</link>
<description>IBM releases a new tutorial on writing better PHP</description>
</item>
<!-- End RSS-Items -->
</rdf:RDF>

March 2003 PHP Architect www.phparch.com 42


FEATURES Writing an RSS Aggregator With PHP

The RSS Format ture, extracting the information it contains. The parser
then calls several user-provided functions to deal with
The specifications for the RSS format can be found at the contents of the file. Contrary to what many people
http://web.resource.org/rss/1.0/spec. An seem to think, the goal of the parser is not to load an
RSS news feed is, essentially, a simple XML file that, at XML file into memory. The parsers role is only to break
a minimum, contains the elements shown in Figure 1. down a correctly formatted XML stream into its com-
As you can see, the main container element is Channel ponents, thus simplifying the developers life, who is
(a vague reminder of RSS initial purpose), which con- free to concentrate on interpreting its contents.
tains information about the originator of the news. In A parser is instantiated using the xml_parser_cre-
addition, the feed should also contain an arbitrary ate() function, which takes no parameters and
number of Item elements, each of which provides infor- returns a resource that can later be used to reference
mation for a single news story. Although there isnt a the newly created parser. Once its operations are com-
predefined limit to the number of items that can be plete, a parser can be destroyed by passing its associat-
included in a newsfeed, the specifications recommends ed resource as an argument to xml_parser_free().
that no more than fifteen be sent by an originator. The XML parser engine supports many different
A news aggregator works by reading in the XML file options, which can be set using the
from an arbitrary number of feeds (determined by the xml_parser_set_option() function:
user), interpreting its contents and, finally, output them
to the user in an aggregated format. xml_parser_set_option (

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


resource parser,
Getting Started int option,
mixed value);
The news aggregator that I have written relies prima-
rily on PHPs ability to read remote files and parse XML In our case, the only option of interest is
contents. As a result, you will need to have both the XML_OPTION_CASE_FOLDING, which causes the pars-
fopen() wrappers and the XML parsing engine enabled er to change the case of all the XML elements and
in your installation of the PHP interpreter in order for attributes it encounters so that they are all in uppercase
the aggregator to work properly. characters. This makes interpreting the contents of the
Reading remote files through HTTP from PHP is a rel- file much easier by using a simple comparison.
atively simple operation. All you really need to do is As I mentioned earlier, the parser allows its caller to
open an URL location as if it were a file and read its con- interpret the contents of an XML stream by using sev-
tents. For example: eral callbacks. Considering the structure of an RSS file,
we will be primarily interested in the XML elements
themselves, so that we can determine when an item
<?php
$f = fopen (http://www.phparch.com, r); begins and ends, and the contents of the Description
element.
if ($f) The xml_set_element_handler() function is
while ($a = fread ($f, 10000))
echo $a; used to specify the two callbacks that the parser should
use when it encounters the beginning of an element
fclose ($f); and its end:
?>

xml_set_element_handler (
These five lines of code will open a connection to the resource parser,
php|a website, read the contents of its main page and string element_start_function,
string element_end_function);
output it to the caller. If youre using PHP 4.3 or high-
er, you can even specify HTTPS as your protocol of
choice! The element_start_function and
element_end_function parameters contain the
Parsing XML names of the functions that the parser should execute
when a new element begins and ends respectively. For
My compulsive allergy to XML makes writing a news example:
aggregator in PHP a very pleasant experience. In fact,
PHP features a complete XML-parsing system that can xml_set_element_handler (
be used to effortlessly interpret the contents of even the $parser,
"StartElement"
most complicated XML data stream. "EndElement"
In PHP, XML information is handled by a parser, )
which reads the raw XML input and analyzes its struc-

March 2003 PHP Architect www.phparch.com 43


FEATURES Writing an RSS Aggregator With PHP

This would require that StartElement() and Although I wont use this feature in the code Im pre-
EndElement() had been previously declared as fol- senting as part of this article, I should also point out
lows: that, instead of function names, you can also pass an
associative array to xml_set_element_handler()
StartElement ( as the value of either element_start_function or
resource parser, element_end_function. If you do so, whenever the
string name,
parser encounters a particular element, it will call the
array attributes);
function associated with it in the array. For example, if
EndElement ( you pass this array as the value of
resource parser, element_start_function:
string name);
(
A => a_begin,
In both cases, the [parser] and [name] parameters
IMG => img_begin
hold a reference to the calling parser and the name of )
the element that is starting or ending. As far as
StartElement is concerned, the [attributes] parameter the parser will invoke a_begin() whenever it finds
contains an associative array of any attributes that are the element <A> and img_begin() whenever it finds
part of the element declaration. For example, the fol- the element <IMG>.
lowing: The character data contained inside an element is

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


passed by the parser to the function specified in a call
<a href=http://www.phparch.com>
to xml_set_element_handler():
would cause the attributes parameter to contain xml_set_character_data_handler (
the array [href => http://www.phparch.com)]. resource parser,
string function_name);
Listing 1
1 <?php The value of function_name indicates the name of
2 a function declared as follows:
3 $feeds = array
4 (
5 'http://blogs.phparch.com/b2rss.xml',
data_handler (
6 'http://www.phparch.com/phpa.rss' resource parser,
7 ); string data);
8
9 // Classes used internally to parse the XML
10 // data Once all the aspects of the parser have been proper-
11 ly set up, it is time to call the xml_parse() function
12 class CItem to cause the XML stream to be parsed:
13 {
14 var $title;
15 var $description; xml_parse (
16 var $url; resource parser,
17 } string data,
18 bool is_final);
19 class CFeed
20 {
21 var $title; As you can see, the is_final parameter can be
22 var $url; used to instruct the parser as to whether the stream
23
24 var $items; ends with the information passed in data. This makes
25 it possible to parse an XML stream as it becomes avail-
26 var $currentitem; ablerather than all at oncethus making the parser
27 }
28 able to nimbly handle large files, as well as files that
29 // XML handlers come from an external or remote source.
30
31 function ElementStarter($parser, $name, $attrs)
32 { Putting It All Together
33 global $currentelement;
34 global $elements; Given the fact that a news aggregator can take on
35
36 $elements[$currentelement ++] = $name;
many different shapes, I thought it best to compart-
37 } mentalize the code that I will be presenting to you as
38 much as possible, so that you will be able to modify
Continued On Page 45... and adapt it to your specific needs without too much

March 2003 PHP Architect www.phparch.com 44


FEATURES Writing an RSS Aggregator With PHP

trouble. update a few global variables to establish the current


As you can see in Listing 1, the first step consists of state of the parser. This is necessary because the parser
creating a few structures that can be used to hold the is essentially a stateless machine that has no knowledge
contents of an RSS feeds while it is being loaded (and of our current position within an XML stream from a
afterward). I use two classes, CFeed and CItem, logical perspective. After all, the parser only... parses
although not exactly in the OOP way, since they are the contents of the file, without trying to interpret and
merely data structures that have no methods (just like understand it.
a struct variable in C). DataHandler(), on the other hand, redirects the
The get_feed() function is used to open and inter- data that is passed to it by the parser to the appropri-
pret an RSS feed. Essentially, its task consists mainly of ate container. Depending on the element that is cur-
instantiating and configuring a parser and pumping rently being parsed, this could mean various attributes
data into it. The real work is done by of either the feed itself or an item.
ElementStarter(), ElementEnder() and Its important to notice here that DataHandler()
DataHandler(). The first two functions simply fully expects to be called more than once by the parser

Listing 1: Continued From Page 44...


39 function ElementEnder($parser, $name)
40 {
41 global $elements;

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


42 global $currentelement;
43 global $currentfeed;
44
45 if ($name == 'ITEM')
46 {
47 $currentfeed->items[] = $currentfeed->currentitem;
48 $currentfeed->currentitem = new CItem;
49 }
50
51 $currentelement--;
52 }
53
54 function DataHandler ($parser, $data)
55 {
56 global $elements;
57 global $currentelement;
58 global $currentfeed;
59
60 switch ($elements[$currentelement - 1])
61 {
62 case 'TITLE' :
63
64 if ($elements[$currentelement - 2] == 'ITEM')
65 $currentfeed->currentitem->title .= $data;
66 else
67 $currentfeed->title = $data;
68
69 break;
70
71 case 'LINK' :
72
73 if ($elements[$currentelement - 2] == 'ITEM')
74 $currentfeed->currentitem->url .= $data;
75 else
76 $currentfeed->url .= $data;
77
78 break;
79
80 case 'DESCRIPTION' :
81
82 if ($elements[$currentelement - 2] == 'ITEM')
83 $currentfeed->currentitem->description .= $data;
84 else
85 $currentfeed->description .= $data;
86
87 break;
88 }
89 }

Continued On Page 46...

March 2003 PHP Architect www.phparch.com 45


FEATURES Writing an RSS Aggregator With PHP

for each attribute. This happens because the parser will interested in, you could simply send them to yourself in
break down the data whenever it encounters a special a nicely formatted e-mail. Similarly, you could change
character (such as &amp; to indicate an ampersand) get_feed() so that it outputs plain text and sends the
and execute the data handler callback separately every news feeds to your favorite PDA or text-enabled cell
time. phone.

Formatting The Output


Once you've read and format-
A call to the get_feed() function will return a
CFeed object whose $items member variable con-
ted the feeds, you could simply
tains an instance of CItem for each news item that is send them to yourself in a nice-
part of the feed. At that point, all thats left to do is to
format its contents as appropriate for the aggregators ly formatted email.
goals.
In our case, this means formatting the feed so that it
can be seen from a web browser. The format_feed() Where To Go From Here
function performs this task by cycling through each ele-
ment of the array and outputting their titles, URLs and An RSS aggregators main goal should be to do the
descriptions to the browser. news gathering for you in a timely fashion. In its pres-

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


Its important to note, however, that this is only one ent form, the code that I have presented in this article
of the possible ways to format and output the feeds. is not particularly convenient in two areas: namely,
Another possibility, for example, is to intercept the there are no interfaces for specifying which feeds to
information delivered by get_feed() by blocking the download or when to download them.
scripts output with a call to the ob_start() function The first problem comes into play when you start
before executing it. This, in turn, would store its output adding or removing feeds from your list. As you can see
into PHPs built-in buffer, from which it could be in Listing 1, the script simply expects to find them in
retrieved by calling ob_get_contents(). the $feeds array that is built starting from line 3.
Once youve read and formatted all the feeds youre Naturally, if you wanted to use the feed in a more nat-

Listing 1: Continued From Page 45...


90
91 // Feed loading function
92
93 function get_feed ($location)
94 {
95 global $elements;
96 global $currentelement;
97 global $currentfeed;
98
99 $xml_parser = xml_parser_create();
100
101 $elements = array();
102 $currentelement = 0;
103 $currentfeed = new CFeed;
104 $currentfeed->currentitem = new CItem;
105
106 xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);
107 xml_set_element_handler($xml_parser, "ElementStarter", "ElementEnder");
108 xml_set_character_data_handler($xml_parser, "DataHandler");
109
110 if (!($fp = fopen($location, "r")))
111 return 'Unable to open location';
112
113 while ($data = fread($fp, 4096))
114 {
115 if (!xml_parse($xml_parser, $data, feof($fp)))
116 return 'XML PARSE ERROR';
117 }
118 xml_parser_free($xml_parser);
119
120 return $currentfeed;
121 }

Continued On Page 47...

March 2003 PHP Architect www.phparch.com 46


FEATURES Writing an RSS Aggregator With PHP

ural way, you may want to build a little interface and, gate feeds via e-mail, you wont even have to do that).
possibly, save your feed list in a database. As I mentioned above, the RSS has revolutionized the
The second problem, on the other hand, only way we take in our news. With its capabilities, PHP is
becomes evident when you start having a long list of the perfect choice for writing an RSS aggregator and,
news feeds (four or five different one should do the hopefully, you will be able to use the code I presented
trick). Because all the feeds are refreshed every time you here in your next project.
execute the script, the aggregator will easily become
About The Author ?>
too slow to be useful if it has to pull data from too
many external sources. A simple solution here would be Marco is the Publisher of (and a frequent contributor to) php|architect.
When not posting bogus bugs to the PHP website just for the heck of it,
to actually pull the data on a fixed schedule, either he can be found trying to hack his computer into submission. You can
through a cron job or a similar method supported by write to him at marcot@phparch.com.
your operating system. You can then store the feeds
directly in a database and pull them out when needed Click HERE To Discuss This Article
https://www.phparch.com/discuss/viewforum.php?f=9
(in fact, if you modify the script to send you the aggre-

Listing 1: Continued From Page 46...


122
123 // Feed formatting function
124

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


125 function format_feed ($feed, $url)
126 {
127
128 if (!is_object ($feed))
129 {
130 ?>
131
132 <p>
133 <b>Unable to load feed at <a href="<?= $url ?>"?>
134 <?= htmlentities($url) ?></a></b></p>
135
136 <?php
137 }
138 else
139 {
140 ?>
141
142 <h1><a href="<?= $feed->url ?>">
143 <?= $feed->title ?></a></h1>
144 <p />
145
146 <?php
147
148 foreach ($feed->items as $item)
149 {
150 ?>
151
152 <h2><a href="<?= $item->url ?>">
153 <?= htmlentities ($item->title) ?></a></h2>
154 <div width=500>
155 <?= htmlentities ($item->description) ?>
156 <hr>
157 </div>
158
159 <?php
160 }
161 }
162 }
163 ?>
164 <html>
165 <head>
166 <title>RSS Feed</title>
167 </head>
168 <body>
169 <?
170 foreach ($feeds as $feed)
171 format_feed (get_feed ($feed), $feed);
172 ?>
173 </body>
174 </html>

March 2003 PHP Architect www.phparch.com 47


Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)
FEATURES

Exploring XSLT
Processing Options
Within PHP
FEATURES

By Stuart Herbert

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


XSLT is a W3C standard for turning XML into HTML, or even another XML document. Thanks to Sam Rubys Java
extension for PHP, and the work of JSR-063, taking full advantage of XSL is very easy

Introductions the TRaX interface defined in JSR-063


I became interested in using XSLT from within PHP
when I was designing docXP, a Javadoc clone for PHP implementing XSLT processing using that
programmers. docXP generates an XML description of interface
PHP source code, and then uses XSLT to transform that
XML into HTML. where to get good XSLT processors from
I found that PHPs XSLT support was not robust
enough for my purposes. Rather than give up on my I hope youll find this information helpful.
project, I decided to find a way to combine PHP with
Javas many excellent implementations of the XSLT Before We Start
standard.
In this article, Im going to share with you the To make full use of the examples listed in this article,
approach I discovered for processing XSLT, by covering: you will need a copy of PHP 4.3.0, with the XSLT and
Java extensions compiled in, and with Sablotron sup-
what XSLT is, and why its sometimes useful port enabled. Any version of PHP 4.1.x or greater
should work, but I have not tested my code with them.
the various ways of performing XSLT pro- For Windows users, the pre-compiled PHP 4.3.0 bina-
cessing (Sablotron, calling Java apps from ries for Windows includes both extensions, and the
the command line), and the problems Sablotron library. Make sure that you uncomment the
(robustness, performance)
REQUIREMENTS
the Java extension for PHP
PHP Version: Java 1.4.x, PHP 4.3.0 with Java
how to configure the Java extension for O/S: Any
Windows and Linux Additional Software: XSLT and Sablotron support
enabled

March 2003 PHP Architect www.phparch.com 49


FEATURES Exploring XSLT Processing Options Within PHP

following lines in your php.ini file: content. The advantage is that the look and feel of the
website can be changed over time without having to
Listing 1 change the code that generates the dynamic content.
;;;;;;;;;;;;;;;;;;;;;; One example website that uses this technique is the
; Dynamic Extensions ; Gentoo Linux website http://www.gentoo.org/.
;;;;;;;;;;;;;;;;;;;;;;
; Here, they use XML to publish the same tutorial con-
; If you wish to have an extension loaded tent to the web as HTML, and to other output formats
; automaticly, use the following syntax: such DocBook (see Gentoo Linux).
;
; extension=modulename.extension
Ive created PHP classes that generate an XML
; description of PHP source code. Listing 2 contains an
; For example, on Windows: actual XML document created by my classes. Im inter-
;
; extension=msql.dll
ested in using XSLT to turn the XML into HTML. Listing
; 3 contains the XSLT file I wish to use on the XML doc-
; ... or under UNIX: ument in Listing 2. You can find these listings in the
;
; extension=msql.so
listings folder that comes with your copy of php|archi-
; tect.
; Note that it should be the name of the
; module only; no directory information
; needs to go here. Specify the location
XSLT Processing From PHP
; of the extension with the extension_dir

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


; directive above. The XSLT Extension
I started out by looking at what built-in support PHP
;Windows Extensions has for XSLT processing. PHP 4.3.0 comes with built-in
;Note that MySQL and ODBC support is now support for processing XSLT templates, thanks to the
;built in, so no dll is needed for it. XSLT Extension and the Sablotron library.
;
The XSLT Extension, added in PHP 4.1.0, provides a
extension=php_java.dll single API for accessing all of the XSLT processing
extension=php_xslt.dll engines that PHP supports. Listing 4, taken from my
docXP source code, shows how to use the API.
The basic flow is very straight forward:
During the article, Ill show you how to configure the
Java extension to work with your copy of Java. I recom- 1. Call xslt_create() to obtain a handle to
mend downloading the J2SDK-1.4.1-01 from Suns an XSLT processor. The handle must be
website http://java.sun.com/. passed into all the other xslt_ functions.
Ill introduce you to other useful downloads when
theyre discussed in the article.
$h = xslt_create();
At times in the article, I refer to classes being single-
tons and facades. If youre not familiar with object-ori-
ented jargon, you probably wont know that these are 2. Call xslt_process() to perform the XSLT
examples of design patterns. Ill explain what each pat- processing.
tern is when we come across it. Addison-Wesley pub-
lished the most famous book on the subject, called If your XML and XSLT files are on disk, and
Design Patterns. If youre interested in learning more, you wish the output to go to disk, then you
the Addison-Wesley book is a good introduction to the can do:
subject.
xslt_process($h, "myxmlfile.xml",
What Is XSLT? "myxslfile.xsl",
"myoutputfile.html");
XSL is the XML stylesheet language from the W3C. It
consists of three parts: XSL Transformations (XSLT),
documented at http://www.w3.org/TR/xslt; At the other extreme, if $xml contains your XML
XML Path Language query language (XPath), docu- document, $xsl contains your XSLT document, and
mented at http://www.w3c.org/TR/xpath; and you want the output to go to $output, you would do
XSL Formatting Objects (XSL-FO), documented at it this way:
http://www.w3c.org/TR/xsl.
XSLT allows us to transform one XML document into
one (or more!) XML documents. It is often used to
transform XML into HTML on websites with dynamic

March 2003 PHP Architect www.phparch.com 50


FEATURES Exploring XSLT Processing Options Within PHP

$args = array ("/_xml" => $xml, Until then, I still need a way of performing XSLT pro-
"/_xsl" => $xsl); cessing using PHP. If PHPs built-in functionality cant
$output = xslt_process($h, "/_xml", be relied upon to do the job (yet), I need to use exter-
"/_xsl", null, $args); nal XSLT processing engines instead.
3. Call xslt_free() to release the handle to Calling Other XSLT Processor Engines
the XSLT processor. The next thing I did was to download a stand-alone
XSLT processor that I could run manually on the com-
xslt_free($h); mand-line. I chose Instant Saxon, available from
http://saxon.sourceforge.net/, mainly
You can use xslt_process() to process XML and because its written by Michael Kay. I use one of
XSLT held in files on disk, strings in memory, or some Michaels books XSLT Programmers Reference from
combination of the two. http://www.wrox.com/books/1861003129.htm
- as my desktop bible on the XSLT standard.
PHP and Sablotron Listing 5, taken from my docXP source code, shows
PHP 4.3.0 only supports one XSLT processing engine - how to call Saxon in this way.
Sablotron. Created by The Ginger Alliance, Sablotron This approach has one very obvious problem. Having
is written in C, and is freely available for both Windows PHP execute a stand-alone XSLT processor is many
and UNIX operating systems. If you are using PHP on times slower than using Sablotron. Theres the over-

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


Windows, make sure that you download the latest ver- head of the temporary files on disk (or tmpfs for canny
sion of Sablotron directly from the Ginger Alliances Linux users), and theres the fork()/exec() overhead of
website. The pre-compiled PHP binaries do not always executing the XSLT processor as a separate process.
ship with an up to date version of Sablotron. I wouldnt recommend it to anyone, unless perform-
Ive found Sablotron to be very fast. Unfortunately, ance really did not matter at all. But it proves the point
Ive also found PHP to fatally crash from time to time that we can go beyond what PHP offers if we really
when using Sablotron on Windows XP. (See need to.
http://bugs.php.net/22147) Now, fatal crashes Instant Saxon is part of a larger project Michael
are a big deal for me, as an engineer who makes a liv- Kays Saxon XSLT processor for Java. Saxon implements
ing using PHP. And heres why. Diagram 1. contains a set of classes that other Java programs can call on. It
captured dialog boxes from one such crash of PHP also has the advantage that its been around for years,
4.3.0 for Windows, as downloaded from and is as standards-compliant and robust as they come.
http://www.php.net/. Take a moment to really If we could call those classes from PHP, that would be
pay attention to the text in the dialog boxes. There are great. That would give us XSLT processing using one
two key phrases I want you to think about. of the most respected XSLT processing engines around.
If we could call those classes without the performance
1. If you were in the middle of something, the problems of using a command-line XSLT processor,
information you were working on might be lost. that would be better still. Wed be able to do all the
XSLT processing we want, without having to use either
2. We are sorry for the inconvenience. the XSLT Extension, or the Sablotron library.
Well, I found out that we can do all this. And we have
Your customers will not appreciate fatal errors in the Sam Ruby to thank for it.
middle of something, and may find the inconvenience
bad enough to vote with their mouse and take their Introducing The Java Extension
business elsewhere. Sam Ruby contributed a very useful Extension to PHP
Do forgive me for this, but Id like to see PHP do bet- 4.0. He wrote a very simple Extension that would allow
ter on this. The XSLT standard was first published in PHP programs to create and use Java objects as if they
were PHP objects. All we have to do is create a
draft form on 18th August 1998, and finally approved
new Java() object, and the rest is done by smoke and
on 16th November 1999. PHP 4.3.0 was released three mirrors. Its simplicity at its best.
years later, on 27th December 2002. Isnt it time that See Listing 6 for an example of the syntax.
PHP could be relied upon to support this standard? At Curious as to how it works? Heres an overview of
least without crashing? I think so. Heck Id settle for what happens:
some XSLT transforms not working quite right so long
as PHP didnt crash any more. Bugs like that can be When we use the Java() keyword for the first
worked around. All one can do for now is hope that its time, the Java Extension starts up a Java Virtual
being worked on as we speak. Machine (or JVM) in the background, using a
programming interface called Java Native

March 2003 PHP Architect www.phparch.com 51


FEATURES Exploring XSLT Processing Options Within PHP

Interface (or JNI). The JVM runs inside our PHP A quick read of the Java Extensions documentation in
process. Once loaded, the same virtual the PHP manual is enough to put most people off tak-
machine is re-used time and time again until ing the Extension out for a spin. This part of the other-
our PHP process ends. We can compile in the wise excellent PHP Manual really stands out for being
Java Extension or load it as a shared object difficult to understand, and for a long list of comments
and if we dont use the Java() keyword at all, by PHP users who just cannot get the Extension to
the Extension does nothing. work.
I think that this is a shame. I found it straight forward
The Java Extension then uses Javas reflection to configure the Java Extension (after reading the PHP
capabilities to examine the Java object were source code!), and Sam did a nice job of making the
trying to create. A PHP object (of type java) is Extension as easy to use as possible. I hope this article
created, and given the same functions as the goes a long way to helping other PHP users take full
Java object. advantage of this powerful and practical Extension
and to make sure of it, Ill show you how to configure
We just call the PHP object, and the Java the Java Extension for Windows, and for Linux.
Extension automatically passes everything
through to the JVM and back again. Its worth Configuring the Java Extension for Windows
noting here that all parameters to functions are To use the Java Extension on Windows, you have to cor-
passed by value. Well return to this point later rectly setup a number of configuration options in your

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


in the article. php.ini file. Ignore what the PHP manual says. You only
have to setup two options.
So far, so good. This is great for people like me, who
want to do XSLT processing from inside a PHP com- Php.ini file setting Purpose
mand-line script.
Set this to point to the DLL of the
Its also great for people who want to use XSLT as java.library
Java Virtual Machine you wish to use
part of their website provided you have compiled PHP
as a module for your web server. The overhead of start- Set this to be the CLASSPATH you
want the Java Virtual Machine to use.
ing up the JVM occurs just once per web server process java.class.path The CLASSPATH must include the
the very first time the Java() keyword is used . And, php_java.jar file, or the Extension
because web servers such as Apache try to re-use their will not work.
processes as much as possible, this means that the Java
Extension very rarely starts up or shuts down.
Listing 7 contains working settings from my own
If youre using PHP as a CGI program, then youll
php.ini file on Windows.
probably have the overhead of starting the JVM every
Lets go through each of these in more detail.
time your CGI script executes. You may find that the
overhead is just too much for a busy web site. But why
1. java.library
not give it a try, and find out for yourself?
The Java Extension works by running a Java Virtual
Machine (or JVM) inside your copy of PHP. To do this,
Configuring the Java Extension
the Extension needs to know which virtual machine

Listing 7: php.ini settings

; PHP is installed into c:\php


; JDK 1.4.1_01 is installed into c:\j2sdk1.4.1_01
;
; These settings in the php.ini file work for me

[Java]

; this is the virtual machine to use

java.library = c:\j2sdk1.4.1_01\jre\bin\client\jvm.dll

; this is the classpath to use. Yours will vary

java.class.path = "c:\php\extensions\php_java.jar;c:\java\jars\saxon-6.5.1\saxon.jar;c:\java\jars\saxon-
6.5.1\saxon-jdom.jar;c:\devel\php\docXP\java"

March 2003 PHP Architect www.phparch.com 52


FEATURES Exploring XSLT Processing Options Within PHP

to use. In JDK 1.4.1-01, which is what Im using to have one, and that you know where it is.
write this article, the DLL containing the JVM is in the If you do not have a copy, it is created in ext/java
jre/bin/client/jvm.dll file on disk. We have to set the when you compile PHP 4.3.0 using the with-java
java.library option to point to the JVM we wish to use. option.
2. java.class.path To use the Java Extension on Linux, you have to cor-
The Java Virtual Machine needs some help in talking rectly setup a number of configuration options in your
to PHP. The Java Extension, written in C, does a lot php.ini file. Ignore what the PHP manual says. You
of the work. The rest of the work has been written in have to setup three options.
Java, and is compiled into the file php_java.jar. By
default, this is in the extensions directory. We have to Php.ini file setting Purpose
add the full path to the php_java.jar file to the
java.class.path option in the php.ini file. Set this to point to the directory con-
extension_dir taining the libphp_java.so PHP exten-
sion.
Remember also to add any other JAR files that you
need!
Add the following line to load the
Although the PHP Manual does mention other php.ini Java Extension.
extension
options, they are not required on Windows to use PHP
4.3.0 with JDK-1.4.1-01. From reading the PHP source extension=java.so
code, it appears that some options are required for sup-

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


porting older Java releases, and others are there to give Set this to point to the shared library
the JVM a hint in unusual circumstances. java.library containing the Java Virtual Machine
you wish to use.
Configuring the Java Extension for Linux
I use Gentoo Linux myself. Out of the box, Gentoo
Set this to point to the directory con-
Linux does not (at the time of writing) compile or install java.library.path taining the compiled libphp_java.so
the Java Extension. Gentoo Linux uses scripts called PHP extension.
ebuilds to install packages such as PHP. Ive submitted
updated PHP CLI and Apache module ebuilds to fix Set this to be the CLASSPATH you
this, which are attached to want the Java Virtual Machine to use.
http://bugs.gentoo.org/show_bug.cgi?id=15574 java.class.path The CLASSPATH must include the
php_java.jar file, or the Extension will
and not work.
http://bugs.gentoo.org/show_bug.cgi?id=15650.
These new ebuilds also automatically configure your
php.ini file for you. If you use either of these ebuilds, Listing 8 contains working settings from my own
you can skip the rest of this section. php.ini file on Gentoo Linux.
If you use another Linux distribution, check with your Lets go through each of these in more detail.
Linux vendor to see whether they provide pre-compiled
PHP 4.3.0 with the Java Extension enabled. 1. extension_dir
Before you can configure your copy of PHP, it is The Java Extension can only be compiled as
important to have a quick look at two things first: adynamically loadable PHP extension. PHP
needs to know which directory to look into
1. Find the PHP extension file java.so (normal- to find such extensions.
ly found somewhere under
/usr/lib/php/extensions). Create a 2. extension
symbolic link to this file, and call the symlink PHP needs to know which extensions to
libphp_java.so. dynamically load. You can list as many
extensions as you need. We need to tell
> cd /usr/lib/php/extensions/?? PHP to load the Java Extension.
> ln -s java.so libphp_java.so
3. java.library
Under Linux, the Java Extension does not work unless The Java Extension works by running a Java
the extension file is called libphp_java.so. The Java Virtual Machine (or JVM) inside your copy of
Virtual Machine itself throws a PHP. To do this, the Extension needs to
java.lang.UnsatisfiedLinkError exception. know which virtual machine to use. In Suns
JDK 1.4.1-01, which is what Im using to
2. Find the file php_java.jar, which should have write this article, if your Java compiler is
been installed with PHP. Make sure you /opt/j2sdk-1.4.1-01/bin/java, the shared

March 2003 PHP Architect www.phparch.com 53


FEATURES Exploring XSLT Processing Options Within PHP

library containing the JVM is in the Listing 6


/opt/j2sdk-1.4.1-01/jre/lib/i386/libjava.so file <?
on disk. We have to set the java.library $stack=new Java("java.util.Stack");
option to point to the JVM we wish to use. $stack->push(1);

#
4. java.library.path # Should succeed and print out "1"
Under Linux, the Java Virtual Machine #
$result = $stack->pop();
$ex = java_last_exception_get();
5. java.class.path if (!$ex) print "$result\n";
The Java Virtual Machine needs some help in
#
talking to PHP. The Java Extension, written # Should fail - note the "@" eliminates
in C, does a lot of the work. The rest of the # the warning
work has been written in Java, and is com- #
$result=@$stack->pop();
piled into the file php_java.jar. By default,
$ex=java_last_exception_get();
this is in the extensions directory. We have if ($ex) print $ex->toString();
to add the full path to the php_java.jar file
to the java.class.path option in the php.ini #
# Reset last exception
file. #
java_last_exception_clear();

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


Remember also to add any other JAR files ?>
that you need!
If you get this error:
Although the PHP Manual does mention other php.ini
Fatal error: Unable to create Java Virtual
options, they are not required on Linux to use PHP
Machine in <PHP file> on line <X>
4.3.0 with JDK-1.4.1-01. From reading the PHP source
code, it appears that some options are required for sup-
this means that your java.library setting in php.ini
porting older Java releases, and others are there to give
does not point to a Java Virtual Machine. The Java
the JVM a hint in unusual circumstances.
Virtual Machine is a DLL on Windows, and a .so file on
Linux.
Testing The Java Extension
If you get this error:
Weve completed all the configuration steps now for
the acid test. Does it work? Fatal error: java.lang.NoClassDefFoundError:
Listing 6 comes with the source code of PHP, and can net/php/reflect in <PHP file> on line <X>
be found as the ext/java/except.php file. If you run this,
you should get the output shown in Listing 9. this means that your java.class.path setting in
php.ini does not include the php_java.jar file. The
Listing 9 php_java.jar file lives in the extensions sub-directo-
1 ry of your PHP installation.
java.util.EmptyStackException All being well, youve now got everything working,

Listing 8: php.ini setting on Gentoo Linux

; php.ini settings for Gentoo Linux & Java Extension


; taken from /etc/php4/php.ini
;
; change the directories to suit your Linux flavour
;
; Stuart Herbert, 2003/02/12

; the Java Extension is available only as a dynamically-loaded


; extension

extension_dir = /etc/php4/lib
extension = java.so
; settings for loading the Java Virtual Machine

[java]
java.class.path = /etc/php4/lib/php_java.jar
java.library = /opt/sun-jdk-1.4.1.01/jre/lib/i386/libjava.so
java.library.path = /etc/php4/lib

March 2003 PHP Architect www.phparch.com 54


FEATURES Exploring XSLT Processing Options Within PHP

and we can start figuring out how to call our favourite Introducing The TRaX API
XSLT processor from within PHP.
Recommended reading:
Using The Java Extension In The
Rest Of The Article The latest documentation for the TRaX API can be found at
http://jcp.org/aboutJava/communityprocess/r
eview/jsr063/.
To use the code listed in the rest of the article, youll
need to download and install an XSLT processor written Make sure you get the latest PDF document. The TRaX API
in Java. For this article, I am using Saxon v6.5.2, avail- documented in the original v1.1 standard is not supported by
able from http://saxon.sourceforge.net/. You XSLT processors such as Saxon. I spent several frustrating
hours finding this out the hard way!
need to add the saxon.jar and saxon-jdom.jar JAR files to
the java.class.path option in your php.ini file. Listing 8.
shows what that looks like on my machine.
At this point, we could just dive in, and write a piece Unlike PHP, Java programs consist entirely of classes
of PHP code to drive our favourite XSLT processor. In arranged in a strict hierarchical order called packages.
fact, this is what I did at first. But what happens when Each class must belong to a package.
we want to change XSLT processors? The PHP code Classes in the TRaX API can be found in the
that we would have written would no longer be valid, javax.xml.transform package :
and would need changing. It would be much better if

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


we could write a PHP <=> XSLT interface just the once,
Package Contents
and then re-use it with any XSLT processor. Well, we
can, if we follow the work of JSR-063. The classes required for per-
javax.xml.transform
forming XSL transformations
Using The Java Extension To
Perform XSL Transformations Classes for interfacing with a
javax.xml.transform.dom
DOM XML parser
JSR-063 A Standard Way To Perform
Transformations In Java Classes for interfacing with a
Java Specification Requests (JSRs) are the way in which javax.xml.transform.sax
SAX XML parser
ordinary developers in the Java community can request
additions to the Java language and/or libraries. The
process is a mixture of public participation and expert javax.xml.transform.stream Input and output classes
working groups. You can find out more about the
process at http://jcp.org/introduction/owverview.
Theres no direct equivalent in the PHP world (mores These classes do not ship with the Java Development
the pity), although I suppose that PEAR comes close in Kit (JDK). They have to be implemented individually by
some ways. each XSLT processor. You can see an example imple-
JSR-063 is a request for a standard API for performing mentation of the TRaX API in the source code to Saxon.
XML processing. Java programmers will already be
familiar with the results the JAXP API for XML parsers. Performing XSL Transforms using the TRaX API
JAXP provides a standard API for accessing, and using, For our simple needs, we do not need to know every-
XML parsers from different vendors. This allows Java thing about all of the classes inside the
developers to change their XML parser without having javax.xml.transform package. We are primarily con-
to change their code. cerned with two classes:
The working group has also defined a standard API
for accessing, and using, XML transformation (little t) 1. javax.xml.transform.TransformerFactory
engines the TRaX API. (I dont know what TRaX is a class that generates objects (called
stands for its not explained in the JSR-063 docu- Transformers) that perform XSLT process-
ment.) TRaX is designed to know nothing about the ing.
actual transformation that takes place. It is not limited
to XSL Transformations. Theoretically, theres no rea- 2. java.xml.transform.Transformer is the
son why it couldnt be used to access a translator from class that (as far as we are concerned) per-
English to another language, for example, if such a forms the XSLT processing.
beast existed.
I decided to use the TRaX API to perform XSLT pro- It couldnt be simpler. We use TransformerFactory
cessing from within PHP. to get ourselves a Transformer, and then we use the
Transformer to do our XSLT processing. Well, this is

March 2003 PHP Architect www.phparch.com 55


FEATURES Exploring XSLT Processing Options Within PHP

Java, and Java wouldnt be Java if things werent just a PATH environment variable):
little more complicated than they first appear.
The TransformerFactory is a great fan of java org.gnqs.docXP.jsr63 <XML input
Christopher Lambert films there can be only one! It file> <output file> <XSL template
is a singleton class. Only one TransformerFactory file>
can exist inside the JVM at any one time. This is
achieved by two things: 2. You can call it from PHP, to process files
already on disk. See Listing 12. for an exam-
1. The constructor for ple.
TransformerFactory is package protect-
ed. Only code belonging to 3. You can call it from PHP, to process XML
javax.xml.transform can create a already stored in a string, and get the result
TransformerFactory object. back in a string. See Listing 13. for an
example.
It goes without saying that none of our code
belongs to the javax.xml.transform
package. Run Listing 10. to see what hap- XSLT allows us to transform
pens when you try to create a
TransformerFactory object from PHP. one XML document into on

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


The error message you get may vary,
depending on your version of PHP and your
(or more!) XML documents.
version of Java. It is often usef to transform
2. Classes that do not belong to XML into XHTML on web-
javax.xml.transform can obtain a
TransformerFactory object by calling
sites with dynamic content.
the static function
TransformerFactory::newInstance()
.
It also goes without saying that, from PHP, we cannot It is very easy to enhance the jsr63 class if the inter-
call static methods directly. We can only call static face doesnt quite meet your needs.
methods on a successfully created Java() object. And In more detail, heres how the class works:
weve already proven that we cannot successfully cre-
ate a TransformerFactory object. public static void main (String[]
Were left with just one way forward. We need to cre- argv)
ate the TransformerFactory object from inside a Ive added this method simply to allow us to
Java class. So thats what I did next. I wrote my own call this class from the command-line (as
class in Java to act as a faade to the TRaX API. explained above).
The first thing this method has to do is create
A Faade For Accessing TRaX From PHP a copy of our faade to use. Java methods
A faade is a false-front; it pretends to be one thing, but with the [static] qualifier can only call other
underneath it is something else. We need a Java class static methods, and methods on objects
that pretends that using TRaX from within PHP is sim- created within the method.
ple, but underneath has all the gory detail that we cant Then all the method does is pass the work
deal with directly. onto doTransformFromFiles().
Take a look at Listing 11. It contains the source code
for a Java class that is very usable from within PHP. public boolean
doTransformFromFiles (String
<Listing 11. org.gnqs.docXP.jsr63.java> a_szInFile, String a_szOutFile,
<this file lives inside listings/org/gnqs/docXP/ The direc- String a_szXSLT)
tories are important> This method allows us to take a XML file on
disk, apply the contents of a XSLT file, and
Before we look at how the TRaX API itself works, lets place the result in an output file.
look at what my jsr63.java class offers. To do this, we have to create a
StreamSource object for each input file
1. You can run it from the command line the XML file, and the XSLT file and a
(assuming that the class is in your CLASS- StreamResult object for the output file.

March 2003 PHP Architect www.phparch.com 56


FEATURES Exploring XSLT Processing Options Within PHP

According to the JSR-063 document, the easi- the call stack.


est way to do this is to just pass the filenames In my faade, I trap any exceptions thrown by
to the constructors of each class, and let it the TRaX API, and simply return false to
sort matters out for itself. When we do this, indicate an error.
both StreamSource and StreamResult
treat our filenames as URIs. As you can see, it took a fair bit of work to understand
Once we have our inputs and outputs, we the TRaX API, but the resulting code is very small and
pass control to doProcessXSLT(). very simple. I think it would be very helpful if there was
a standard library of facades like this one for PHP <=>
public String Java interaction. You can read about my attempts to
doTransformFromStrings (String set one up in PEAR in the pear-dev mailing list archives
a_szXML, String a_szXSLT) at http://marc.theaimsgroup.com/?l=pear-dev.
This method allows us to take a piece of XML
already loaded into a string variable, apply Where To Get Good XSLT Processors From
some XSLT already loaded into a string vari- Throughout this article, Ive been using Michael Kays
able, and get the output back returned as a Saxon XSLT processor.
string. Another excellent (and compatible!) processor is
A word on the interface. The Java Extension Xalan, from the Apache project. You can get Xalan
passes all variables by value. This means that from http://xml.apache.org/xalan-j/index.html.

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


our Java code gets a copy of the XML and A quick search on Google using TRaX java didnt turn
XSLT not the original. There will be a per- up any other compatible processors, but that doesnt
formance overhead as a result with very large mean theyre not out there. Id love to hear from read-
XML and/or XSLT strings. ers who do find other XSLT processors to use freely or
It also means that the only way to return data commercially available.
from the Java Extension to PHP is via the
return() mechanism. Theres no way to Final Thoughts
modify a parameter passed into the method. Today, Ive explained how to take Java code, and
This method demonstrates a useful design exploit it from within PHP. The article has focused on
abstraction introduced in Java 1.1. To make XSL processing. We do not have to stop there.
the Transformer object read and write from The Java world contains high performance, highly
strings, we wrap the strings in robust implementations of useful standards. The PHP
java.io.reader and java.io.writer world simply does not. Were always lagging behind.
objects that pretend to be files. From my experience inside big business, companies are
Again, this method uses doProcessXSLT() to choosing Java over PHP because Java implements all of
perform the real work. these standards, and PHP does not. This gives rise to
the perception that PHP is a toy an amateur project
public boolean doProcessXSLT that cant possibly take the place of Java in the enter-
(StreamSource a_oIn, StreamResult prise. As PHP programmers and advocates, we know
a_oOut, StreamSource a_oXSLT) better. Unfortunately, many professional programmers
This is where the work is done, and where we never get the chance to find out.
use the TRaX API. The Java extension is experimental. The warning in
We call TransformerFactory.newInstance() the PHP manual is clear. We use it at our own risk. The
to obtain a TransformerFactory object. point I wish to make to the authors of PHP is also clear.
You will remember from above that it is this We lose this extension at our own risk.
step that we cannot do from PHP.
Next, we call
TransformerFactory.newTransformer() to
obtain a Transformer object. As part of the
call, we pass in the StreamSource for our About The Author ?>
XSLT. Stuart Herbert has designed and implemented solutions for major com-
Finally, we call Transformer.transform() to panies such as Eurostar, Vodafone, and Hewlett-Packard, and has been
apply the XSLT to our XML, and get our out- the lead maintainer of the Generic NQS project since 1994. A former
put. Systems Manager with Orange UK, Stuart is now embarking on a career
mixing PHP freelancing with teaching Tai Chi Chuan in Cardiff, Wales.
When errors occur in Java, exceptions are You can contact Stuart via stuart@gnqs.org.
thrown. Think of them as a second return
type with a special purpose. Exceptions have Click HERE To Discuss This Article
to be trapped and handled, or be passed up https://www.phparch.com/discuss/viewforum.php?f=10

March 2003 PHP Architect www.phparch.com 57


FEATURES

The Story of the


Bouncing Ball and XML
FEATURES

By Sam Smith

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


Flash has long been in use as a client-side solution to create livelier, more animated web applications. Learn how to
use PHP and XML to exploit some of the newer utilitarian features of Flash MX and ActionScript and create powerful
yet elegant interfaces.

It all started with a bouncing ball. We were really Flash intro class. This short seminar illustrated the ease
excited to get some money from the Youth Sports and with which nice graphics and animation can be imple-
Recreation Commission, a Detroit non-profit, for a mented. However, the instructors also pointed out
new web site. This pilot project was to see if the web how the new Flash MX scripting language,
could help foster inner-city sports activities, and at the ActionScript, had been enhanced to the point that
same time get the kids interested in using the web some of their customers were developing the entire
interactively. The kids could upload photos, keep application, at least on the client side, with Flash and
track of scores, register for events and have a good ActionScript. Some examples were shown, but I
time playing as well. The application would require mainly left wondering if that was just marketing
access to a database, containing tables for players, enthusiasm.
games and events, and a roster that showed players Anyway, the Flash package arrived and I began get-
and events. ting familiar with its animation capabilities (which are
So, wWe were all set to duplicate an earlier project neat and fun). I was also assessing how well
that used PHP server-side scripting and MySQL only ActionScript (AS) could support general client-side
no client scripting at all. The application worked pret- programming and server communication. I was pret-
ty well, and we knew how to do the project quickly. ty impressed, and also pleased that the learning curve
However, it was going to be BOORRRING. How could was not so great since it had similarities to PHP. One
we do something to get the kids attention? small difference, illustrated in the table below, is that
About that time someone was telling me about the The variables do not have a $ in front, so that to
Macromedias new release of Flash MX, and how eas-
ily good animation can be accomplished with this sys- REQUIREMENTS
tem. Ah-hah! I could see it right away a bouncing PHP Version: 4.0.6 and above (4.1 recommended)
ball going across the screen. Now, that would have to Additional Software: Smarty Template Engine
be better than no movement at all. So off I went to a (http://smarty.php.net)

March 2003 PHP Architect www.phparch.com 58


FEATURES The Story of the Bouncing Ball and XML

form a string you use. The table compares the syntax used to implement business-class applications that
used in each to perform equivalent tasks: required forms, input fields, database access and so
forth. An example is the reservation system at the
PHP ActionScript Broadmoor center in Colorado (https://www.entery-
ourinformation.com/broadmoor/onescreen.cfm). It
$obj = "ball"; obj = "ball";
turns out that ActionScript has been made very power-
ful, to the degree that you can programmatically insert
$outString = "the $obj outString = "the visual objects onto the screen display rather than place
bounces"; "+obj+" bounces";
all the objects on the screen in using the menu system
that iswhich is also available. This is referred to as
run-time programming of the visual elements under
This syntax permits PHP to intermix strings and vari-
control of your ActionScript, rather thanas opposed to
ables more readily, but this difference is not much of a
design-time layout of the visual components.
problem (except that you forget the $ when going
There are also adequate provisions in ActionScript to
back and forth!). Another tricky area is ActionScripts
develop forms on the client, do local editing of input
rules for variable scoping, where the PHP rules (IMHO)
fields, and other processing to support interaction with
are much more logical and less prone to error. In AS, if
a PHP program running on the server. The way this
you do NOT declare a variable as local it is known to all
works is that values on the screen are, as you would
functions, whereas in PHP you must specifically declare
expect, represented as ActionScript variables. These
a variable as global - far safer. It was weeks into pro-

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


variables may then be sent to and received from the
gramming that I discovered that the i in for
server using the methods of the LoadVars object.
(i=0; i<j; i++) in every function was using the
However, in this case the variables are all sent as indi-
same global i! There are other differences, but I con-
vidual elements, with no structure. I would prefer to
cluded that the language was perfectly adequate for
package the variables into a structure and send the
general processing as well as handling its main focus:
whole structure as one entity back and forth to the
managing visual objects in space and time to produce
server (which, as we will see, is one of the things the
animation. In this regard, you can produce a trajecto-
use of XML will accomplish).
ry for an object, indicate a start and end time, and an
All of the elements were available within Flash to han-
object will follow this trajectory with its movement on
dle both the screens with animation and the screens
a frame-by-frame basis calculated for you.
with input forms and data display. In fact, a new
I drew in my trajectory, and without too much trou-
DataGrid component was released with the
ble I got what I was looking for. The ball bounced
Macromedia Resource Kit, facilitating the display of tab-
across the screen, following a trajectory just the way it
ular data. This is just what is needed for showing data-
was supposed to, et voila! We had animation.
base table information. So I decided to use Flash for
Now, what about the player and game database?
more than the bouncing ball. Whether this is the best
The site still had to function like a regular web-based
way to do this remains to be determinedprobably
business application in this regard. By this time, I was
depends on the individual project, but as things
pretty enamored with ActionScript and I had recovered
worked out I became more confident it was a good way
from my earlier religious aversion to client-side script-
to go for us. As you will see at the end of the story, I
ing. I was willing to see if Macromedias claims for a
think others may want to do the same thing, using PHP
fully-functioning Flash implementation could be cor-
inas the server and ActionScript in the browser.
rect.
So this story begins with the bouncing ball, and ends
The Road to XML
showing how we eventually ended up with a client
application implemented entirely in Macromedia Flash,
One of the first things that needed to be finalized was
with a PHP-based web server programback end.
how to tie the Flash process in the browser to the web
Furthermore, we found that the use of XML for the data
server, its scripting language and the database.
interchange was an effective way to go, with an XML
Actually, the database can be implemented in several
message structure that can serve as a general client-
different ways without impacting the rest of the story,
database exchange vehicle. Over the course of the rest
but for this project we used our familiar friend, the
of the article, Ill illustrate how we got there.
MySQL database.
It might be best here to give a rough idea of how the
Is there More to Flash than
Flash Player, which resides within the browser, and the
Bounce?
rest of the system fit together, because there is some-
thing of a layering of processes and data objects. One
The short class on Flash MX that I attended highlight-
thing to remember is that neither the PHP nor the
ed the fact that its new ActionScript had already been
ActionScript sees what actually goes out over the net-

March 2003 PHP Architect www.phparch.com 59


FEATURES The Story of the Bouncing Ball and XML

work, and in each side of the transmission (browser and Response XML document once it is returned
server) an envelope is wrapped around your data at and loaded. There is no wait right here and
the sending end and stripped away at the otherby the do nothing until this finishes command. This
recipient, with some data stream transformations tak- is not a problem as long as you restrict the
ing place along the way. Rather than complicate the user from further input (creating in effect a
story at this point, please see the sidebar section What pause where nothing happens). This asyn-
happens to your XML on the Network at the end. I chronous operation does allow you to let the
found it just about impossible to debug the system user continue on with other activity, but then
without being able to see (with the help of a network it does getgets complicated since you would
Sniffer type device) what was actually being sent back have to keep track of where the user is upon
and forth. For instance, when the XML string is sent receipt of data and whether anything has
from the browser to the server, it goes out as an HTTP occurred that would impact the processing of
POST transfer, and to get the data retrieved in the the received data (eg. you send in an update
PHP code with minimal change, I found that it was eas- on one person and then move to another per-
iest to directly access the $_POST array and re-con- sons record).
struct the incoming XML string from the contents of Once received, the XML object can be manip-
that array. ulated to extract data. This is, admittedly,
However, the benefit of going through this process of tedious, as there is not a really neat way to
sending and receiving the XML message is that you access elements by name. However, this

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


deal with the entire set of data exchanged between process is simplified by utilizing a standard-
browser and server as one piece of transmission data. ized XML document structure to send
In the PHP script this data packet appears as a string requests and receive responses. The process-
that is processed through the XML built-in functions ing of received data to display on the screen is
that are discussed below. In the Flash Player, the XML simplified by use of the new Flash DataGrid
string is automatically converted into an XML Object, component, included in their developer
and can be accessed via methods of the object or by resource kit. This is a very convenient method
accessing the array structure within the XML Object. for handling and displaying tabular data.
Using XML permits each end to process the data in dif-
ferent ways, as long as each end correctly interprets the The implementation of XML within ActionScript
XML structures content. looked like it was usable without much work, and we
In the end, we felt that using the XML approach was decided to go with it. Once the decision was made to
the best way to go, because it provides a general, use XML in the client, two remaining steps were:
repeatable method for performing this function not
only for the client and browser we were using, but it 1. What XML document structure would be best
was also usable if we changed the implementation of for this database-intensive exchange?
either the client or server. XML would be new for us,
but it turned out that the use of XML in Flash is straight- 2. How would XML be processed in PHP within
forward, as far as sending and receiving an XML mes- the server?
sage in ActionScript. Heres a quick overview of how
things are handled: An additional question, which turned out to be a bit
The XML object is readily created in Flash, of a surprise, was how do you get the XML data, once
either on an element-by-element basis or, received back from the server, to be decoded and ready
much more easily, by simply creating the XML for display?. Forming, sending and receiving the XML
string and declaring it an object. The string document was straightforward from ActionScript, but
then becomes an XML structure without hav- getting the data out of the XML structure in a usable
ing to deal with each element and tag individ- form was not. The details for describing how this is
ually. done is more in the realm of ActionScript coding, and
Once the XML object is created, it is sent to will not be described here other than through the
and received fromcan be exchanged with the source code listing.
server byusing the xml.SendandLoad method.
Executing this procedure is explained below. Building the Document the
The bottom line is that it is easy, with the one XEBXML Structure for Database
proviso that the actual Send and Load opera- Exchange
tions are asynchronous operations. This
means that the client process needs to control In the middle of this method of tying the client and
or prevent user input while awaiting the Load server together is the actual XML document. To get a
to complete, and to be able to handle the snapshot explanation of the XML syntax, it might be

March 2003 PHP Architect www.phparch.com 60


FEATURES The Story of the Bouncing Ball and XML

best to start with an example, and the example is what A decision must be made regarding what to imple-
we are actually using: the XML document for this appli- ment as a TAG (or element, the TAG being the iden-
cation, which we designated with a TAG to identify the tifier of the element) and what to implement as an
entire XML message,: XEBXML. ATTRIBUTE, as you can implement the solution either
First, any XML document consists, like HTML, of Tags way. The first such distinction is the identification of a
and Attributes: Request from a Response. To group this piece of data
along with other fields common to the TRANSACTION,
<TAG Attribute-Name=Attribute-Value> a XEBHDR tag was used, and within this tag several
Data </TAG> attributes are used to define the transaction: RRTYPE is
the Request/Response Type, containing these values:
The <TAG></TAG> combination can be combined RRTYPE=request or RRTYPE=response. Other
into one bracket: <TAG. />. attributes chosen for the header are APPLN, TRANS,
So iIn our case, we are creating a specific XML struc- and TBLSTEP indicating the application sending the
ture for sending database information and commands, document, a transaction code, and a database Table
with the following outer syntax (all the rest of the Name. Within the header is also another tag, XEBIN-
document is between tags): FO, that contains attributes for the operation being
executed (WHERE SQL clause, etc.).
<XEBXML> .. the XEBHDR and XEBDATA To bring all database information together and per-
elements are here.. </XEBXML> mit flexibility of the format for other, possibly non-tab-

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


ular, data structures, the data content portion of the
To get a better idea of the structure of the XEBXML xebxml document is placed into the XEBDATA tag, with
document, or message, Figure 1 gives a graphic display tabular fields placed into zero, one or more XEBTABLE
of the components within the overall message. It con- tags.
sists of a header (XEBHDR), which in turn contains an After considering several alternatives, the structure
informational component (XEBINFO), and a data ele- for describing table information ended up with the fol-
ment (XEBDATA). lowing:
We envision the same message structure going from
client to server as goes from the server to the client. We The data is organized into column-major
need an indicator to distinguish the messages, so we order, with all row values for a given column
have a field (an Attribute rather than a separate contained within a column heading. (This is a
Element), termed a Request, and for data returned convention that seems to me better than
from the server to the client, termed a Response. The row-major because within a row every col-
Request will need to describe the database operation, umn name must be repeated, which is really
table identifier, SQL information including the WHERE, verbose. Row-major is, for instance, how the
and ORDER clauses, as well as the field names desired. Flash Data Provider array [actually an object]
The Response will need to contain one or more rows of is structured and while it works fine it is a lot
returned fields, with field names and values identified. of repetition of field names in every row.)

Figure 1:

March 2003 PHP Architect www.phparch.com 61


FEATURES The Story of the Bouncing Ball and XML

The database columns use as their tag the Note that the XEBINFO is implemented as a single
actual column name, thus the tag for the tag <XEBINFO />, rather than
Email column is <Email>. <XEBINFO>.</XEBINFO>.
Once we get the XML request ready to send to the
Fields (rows within columns) are identified server, which actually happens in response to a user
with the <FLD> tag. interface button or request, both the send and
response are accomplished with the ActionScript code
Consider the following database table, email_list: in Listing 1.
It is important to note that the delay between the
Name Email request and response is indefinite, during which time
the script keeps on running, so during this period you
John john@acme.com need to ask the user to wait or block any user input dur-
ing the wait period. Or, if you are brave or really good
Jane jane@baker.com let her keep typing (which will work but you need to be
cautious)!
Harry harry@acme.com

Using XML permits each

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


The resulting tabular data within the xebxml docu-
ment would be: end to process the data in
<XEBDATA> different ways, as long as
<XEBTABLE>
<Name> each end correctly inter-
<FLD>John</FLD
<FLD>Jane</FLD> prets the XML structure's
</Name>
<FLD>Harry</FLD>
content.
<Email>
<FLD>john@acme.com</FLD>
<FLD>jane@baker.com</FLD>
(A very interesting thing to realize is that with
<FLD>harry@acme.com</FLD>
</Email> ActionScript the client is basically telling the server
</XEBTABLE> what to do, and in a standard PHP and no-client-script
</XEBDATA> implementation the server is in charge. And if you
think about it a little further, the ActionScript is really
In summary, the xebxml document looks like this: not in charge either, since except for the initial set-up it
is completely event-driven, responding either to user
<XEBXML> input OR to data arriving back from the server. Whos
<XEBHDR APPLN=ehoops RRTYPE=request in charge here, anyway? Ultimately, the user interface.)
TRANS=getm TBLSTEP=email_list> The XEBXML message structure is general and exten-
<XEBINFO WHERE=Name = Jane/> sible. It can represent tabular database information
</XEBHDR> from any table organized as columns and rows, and by
<XEBDATA> defining another TAG in the XEBDATA element, other
<XEBTABLE>
data structures could be supported without changing
<Name>
<FLD>John</FLD> the rest of the document structure.
<FLD>Jane</FLD> Now, lets look at what the requirements are for pro-
<FLD>Harry</FLD> cessing the XML message in the Server PHP code.
</Name>
<Email> Code for PHP Supporting the Back
<FLD>john@acme.com</FLD> End the xebxml PHP Class
<FLD>jane@baker.com</FLD>
<FLD>harry@acme.com</FLD> The processing of both requests and response
</Email>
xebxml documents in PHP consists of essentially con-
</XEBTABLE>
</XEBDATA> verting the XML elements to variables and arrays, or,
</XEBXML> more precisely, into an array structure that is specifical-
ly used to directly reflect the database tables contents.

March 2003 PHP Architect www.phparch.com 62


FEATURES The Story of the Bouncing Ball and XML

Then, once the array is filled, moving the data from the resent data from database tables in a consistent man-
array into the database can be done with standard PHP ner. PHP arrays are very flexible, and Ive found that for
routines (module xebDB_cls.php). In this project, database access an Associative Array of Arrays is most
the MySQL database system is used, but the technique convenient. That is, the major array is an associative
is general and in fact the database procedures are array in which the keys are column names, and the
implemented in a PHP class, which would permit value for each key is another array that containscon-
another database to be used within this class without taining one value for that column for each row. In this
changing the surrounding code. scenario, the table referenced above (email_list) would
Lets first look at how we used the PHP arrays to rep- be stored column-major, row-minor, in an associative

Listing 1: ActionScript Code

function xebxmlRequest_getTable() {
gxpage.fill_bot(Please wait for Event Data);
//Construct the XebXML request and set for initial read
//XebXml is an object, which contains an XML object and
//other stuff; the setHdr and setData methods take parameters
//and put them into the XEBXML request
//getm is the database operation, eevents is the table name
var myxebXml = new XebXml(gxebAppName);

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


myxebXml.setHdr(getm, eevents, ok, , );
myxebXml.setData(, );
// Access database to retrieve all events
var xmltmp = new XML(myxebXml.xmlstr);
// We only have one response outstanding at a time, so a
// global structure is used to contain the response data,
// since we will need this in a different function
_global.gxebxmlResp = new XML();
//receiver object for xebxmlResponse
// the following says when the object gxebxmlResp is notified
// that it has been loaded, fire off the function
// xebxmlResp_gettable
gxebxmlResp.onLoad = xebxmlResp_getTable;
// this is what weve been waiting for: Go to the xmltmp
// object containing the XEBXML request, send it to the
// indicated php script, and store the resturned result in
// the object gxebxmlResp
xmltmp.SendAndLoad(ghostURL+/xebxmlDB_Common.php, gxebxmlResp);
}
function xebxmlResp_getTable(success) {
//
// *** call populate datagrid to show the table above on datagrid on screen

if (success) {
// trace(Response XML - Success true);
if (gxebxmlResp.status != 0) {
trace(Error: Bad XML status+gxebxmlResp.status);
}
var xebxmlResp = gxebxmlResp.childNodes[0];
// the childNodes array contains the elements
if (xebxmlResp.childNodes[0].attributes.RRTYPE == response &&
xebxmlResp.childNodes[0].childNodes[0].attributes.CODE == ok) {
//trace(Well formed xeb);
//this fills and displays the datagrid
populateDG(0);
// normal case - no error
} else {
// not a well-formed XEBXML message
//etc for error processing
}
} else {
// Error - In this case a bad XML load
}
}
/*
* Function: populateDG() this is the display function
*/

March 2003 PHP Architect www.phparch.com 63


FEATURES The Story of the Bouncing Ball and XML

array (one entry per column) of arrays (each element of the application, but within the xebxml umbrella a
which is a field, one entry per table row). number of standard transactions are available that take
Ive found it helpful to encode the array structure in care of most needs. These are:
its name, and use the following convention for an
Associative Array (Aa) of Arrays (A). Using this conven- getm Get Multiple Rows
tion, the table above would be represented as: gets Get single Row
add Add a single Row
arrAaATable[Name]= array{John, Jane, upd Update one or more fields in a Row
Harry}; del Delete a Row
arrAaATable[Email]= array{john@acme.com,
jane@baker.com, harry@acme.com};
These transaction codes are implemented in a data-
base access class (listing xebDB_cls.php), which cur-
rently uses MySQL but could use other databases very
There are a number of tools within PHP for handling
readily.
XML documents. For this project, the built-in functions
The results of the access transactions getm and gets
used were the following:
are stored in the associative array of arrays structure
referenced earlier, arrAaATable. This table contains the
$xml_parser = xml_parser_create();
// creates the xml_parser results of the SQL inquiry. To move this data into the
xebxml Response document, the arrAaA2XML func-

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


xml_set_element_handler($xml_parser, tion is used (Convert an Associative Array of Arrays into
"startElement",
"endElement"); XML). The result of this function is a string containing
the XML content of the xebxml document, which is
xml_set_character_data_handler($xml_parser, returned to the browser as a Response.
"characterData");

$result = xml_parse_into_struct($xml_parser, Putting it All Together


$xml_string,
$xml_values_array,
$xml_tags_array);
Our story has covered a lot of ground, and it might
be good first to recap what is being done in this over-
all system. We are using Flash code in the browser
(Flash Player 6) to handle all data display and user
Now, the nice thing about using these routines is you
input, in addition to creating animation images. We
dont have to understand how they work! The set_ele-
are collecting any data needing to be exchanged
ment and set_character functions perform basic opera-
between the browser (Flash Player) and the web server
tions, and the xml_parse_into_struct will convert the
into an XML structure that is optimized for database
XML string ($xml_string), containing XML element
access under the arbitrary XML tag name XEBXML,
tags, values and attributes, into two arrays, one for
and sending it to the server. In the server, we are using
values and one for the actual XML tags. The con-
XML functions for converting the XML elements into a
tents of these arrays takes some getting used to, and
PHP array structure that mirrors the database table
since I found the only way to access the individual ele-
structure, and then calling a set of database update
ments was through accessing these arrays, their con-
routines that either store the array data into the data-
tents (for the XML representation of the table above)
base or extract database information and put it into the
can be printed by the source code script
array structure. Going from the server to the browser,
(xebxml_parseStruct.php), and a listing of the
the array-to-xml functions are used to get data back
output (print_r output) is shown in listing
into an XML document format, and when received at
(xebxml_parsePrint.txt).
the browser and handed to the Flash Player, Flash con-
There are open source PHP programs that improve
verts this XML string into its XML Object, which per-
the access to these arrays by element name, but I used
mits accessing and displaying the data as ActionScript
the built-in XML routines for this operation.
variables. A refinement used to more readily display
the tabular data within the Flash Player is Macromedias
Database Processing and
new DataGrid Component. .
Returning Results
The full processing cycle, showing the code modules
in both the client and server, are as follows:
At this point, we have received the xebxml document
containing a request from the browser, and decoded it
1. An inquiry screen is displayed in the brows-
into variables and arrays. The next step is to perform
er, triggering a server Request to be generat-
the database operations, based on the xebxml TRANS
ed (by the xebxml.as module, the .as being
attribute. This can be any transaction code required by
an ActionScript suffix).

March 2003 PHP Architect www.phparch.com 64


FEATURES The Story of the Bouncing Ball and XML

ActionScript. That is, the Flash Player will


2. The new Flash MX ActionScript have converted the XML string into an XML
XML.SendandLoad method is called, and the object by the time your ActionScript is
xebxml request is sent to the server. (Later, tapped on the shoulder and told its here!.
in step 6, the Load part of this method
will receive the XML information that comes 7. Database values are extracted from the XML
back from the server, and convert it into an Object and placed into an array called a
XML Object, and this branch to the function Data Provider, which is then displayed on
you designate in the SendandLoad method.) the users screen very conveniently using the
new ActionScript DataGrid Flash UI (User
3. The xebxml request is received by the server Interface) Component.
PHP script, it is decoded into an array of ele-
ments and values, by PHP routines begin- This sequence of processing is shown in more detail
ning xml_. These output variables are (using an animation sequence, of course!) at the web
then moved into an array structure that per- site www.datasmith.info/xebway/.
mits easy database updating. If we now look at efficiency, speed and module size,
the picture is reasonable. Not lightning fast but not
4. The PHP database routines are called to exe- terribly slow. The size of the client module is quite
cute the transaction identified, storing the compact (42 kBytes, including graphic images). The

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


resulting data in an associative array of increase in latency incurred through the use of XML
arrays, in the event of a read operation would be interesting to calculate, but it is my impres-
(gets, getm) or moving data from the asso- sion that the actual XML processing is minimal com-
ciative array of arrays into the database pared to the transmission and database access opera-
(upd, add). tions, which would be required regardless of the struc-
ture of data passed back and forth.
5. For a read operation, the array structure
filled by the database routine is converted to So Was Using XML Worth the
beto an xebxml-formatted XML document. Effort?
In effect, the PHP code performs all database
operations in response to transaction codes This was my first project with Macromedia Flash MX,
contained in the XML messages sent from so it is hard to say whether this was the easiest or most
the Flash process. efficient way to implement the project. However, it
does illustrate that XML inter-communication between
6. After an indefinite time from being sent a novel client system (whether it be Flash or some other
from the browser, tThe xebxml response client) and an existing server using PHP can be done in
message is received by the browser and a clean manner, taking advantage of the XML tools in
(through the Load step of SendandLoad) the respective systems. But most importantly, by hav-
placed into an XML object, accessible to ing as a common denominator the XML document

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. Heres 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.

March 2003 PHP Architect www.phparch.com 65


FEATURES The Story of the Bouncing Ball and XML

that is passed back and forth, you have complete free- coupled with the use of ActionScripts XML Object, you
dom of implementation on either side of the document are pretty far along the way toin having a full solution.
exchange. Finally, by following the conventions used in this article,
Plus, Flash MX is pretty neat, and the latest embodi- which I call XEBXML, your database exchange can be
ment of Macromedia ActionScript is very powerful. It very easily implemented in a straightforward manner.
lacks debugging tools and does not warn you if you call
non-existent variables or functions, so you have to be About The Author ?>
careful. But it appears to be fast, compact, and capa- Sam Smith is a development manager at DataSmith, Inc., and lives in
ble. Grosse Pointe, Michigan. He received his MS in information science from
In summary, I would say that the marriage of Flash Georgia Tech in 1969, which makes him, in his own words, 'ancient' by
MX and PHP is one that will thrive. Without a lot of internet standards. Sam's other main technology interest is protocol
standards.
overhead or needing a comprehensive development
environment (other than the cost of Flash), you can get
a lot done, whether using XML or not. However, with Click HERE To Discuss This Article
https://www.phparch.com/discuss/viewforum.php?f=11
the availability of even minimal XML support in PHP

What happens to your Control: no-cache..


XML on the Network < XEBXML>
<XEBHDR APPLN=ehoops RRTYPE=request

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


Getting data between the Flash ActionScript and TRANS=login TBLSTEP=epersons >
the PHP script in the server is an interesting process. <XEBINFO CODE=ok ERRMSG= WHERE=ccn
Interesting means frustrating until you find a solu- ORDER=ccn />
tion! Anyway, when ActionScript performs the </XEBHDR>
SendandLoad operation on the XML object, there has </XEBXML>
already been a transformation from your XML infor-
mation expressed as a string, to being an object with The web server will then receive this HTTP packet
the elements converted to a complex set of arrays and will hand it to the server script in what appears to
(visible in the debug window of Flash). Your strings be a language-dependent manner. In the case of PHP
(both in ActionScript and in PHP) will need to escape the XEBXML document (after some experimentation)
the double-quotes (use \ rather than ), which get appears most nearly in its original form in the global
stripped before the data is sent over the network, but variable $_POST. Even in this case, I had to make a
then re-added before being presented to PHP. Also, conversion due to a tricky change that occurs some-
in some cases the attributes can come out in a differ- where along the line. The values XEBHDR APPLN
ent order than in the original string - not a problem, above ended up in the PHP $_POST variable as XEB-
but it highlights that the information gets taken apart HDR_APPLN with an underscore between the TAG
and put back together again in transit. XEBHDR and the ATTRIBUTE APPLN, and this in turn
So, if we start with the following string in is the key to an associative array. The following PHP
ActionScript (note absence of $ on variable name): code is what I ended up with:

xmlstr = < XEBXML><XEBHDR APPLN=\ehoops\ if (@$_POST["<XEBML><XEBHDR_APPLN"])


RRTYPE=\request\ TRANS=\login\ {
// the quoted string is a key
TBLSTEP=\epersons\ >
// take away the _ and then affix the
<XEBINFO CODE=\ok\ ERRMSG=\\ // value of the $_POST array entry
WHERE=\ccn\ ORDER=\ccn\ /> </XEBH- $str = "<XEBXML><XEBHDR APPLN="
DR></XEBXML>; .$_POST["<XEBML><XEBHDR_APPLN"];
$gRequestString = stripslashes($str);
}
It goes out over the network as an HTTP packet (the else //..Theres an error.
XML is shown for clarity on separate lines), with the
data between POST and no-cache added by the
browser: Note that there is a fair amount of discussion in the
Flash PHP forum at Macromedia about the best way
POST /xebxmlDB_login.php to do this, and a better variable than $_POST may
HTTP://www.ehoops.org/xebxmlDB_Login.php..C have been identified for getting the naked XML
ontent-Type: application/x-www.form-urlen- string! That underscore is a complete enigma to me
coded..Content-Length: 154..User-Agent: but it was there consistently, so I performed the fix
Shockwave
above.
Flash..Host:..www.ehoops.org..Cache-

March 2003 PHP Architect www.phparch.com 66


TIPS & TRICKS
TIPS & TRICKS

Tips & Tricks


By John W. Holmes

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


Cant Give Up Your Perl? and forth between any Apache modules during the
same request. So, you can have your PHP script set a
So you were lured to the simplicity and power of PHP note that your Perl script can retrieve. Some users in the
like the rest of us, but you hate to give up those perfect, contributed notes section of the manual report that
cryptic Perl scripts youve written? Well, who said you they are using a method that involves
have to? apache_note() for talking to mod_layout and
We all know that we can use include(), mod_usertrack. One user even gives a simple example
require(), etc. in our scripts to include one PHP file for showing how PHP and Perl can talk to each other,
into another. If youre using Apache as your web server as shown in the listings below.
you can also make use of the virtual() function to
call scripts that are not PHP, such as Perl. You pass the From PHP file:
path to a script to virtual() and its output is <?php
returned to your script. You can even pass variables in apache_note("name", $HTTP_COOKIE_VARS["User"]);
the query string as shown in below. // Put all post variables into apache notes
while (list($key,$value)=each($HTTP_POST_VARS))
<?php apache_note($key, $value);
virtual('/path/to/script.pl?id=45&size=large');
?> // Call perl script
virtual("/perl/some_script.pl");
$result = apache_note("resultdata");
?>
Now, youll be able to parse the query string within
your Perl script to get the values of $id and $size
into your script. This makes for an easy way to use those From Perl file:
still useful Perl (or any CGI language) scripts that you # Get Apache request object
my $r = Apache->request()->main();
havent quite ported to PHP.
# Get passed data
Make Apache Keep Your Notes my $name = $r->notes(name);
my $more_data = $r->notes(more_data);
So you tried the above and now youre wondering
# some processing
how you can have your Perl script send data back to
PHP, right? The apache_note() function can handle # Pass result back to PHP
this for you. Its actually useful for sending data back $r->notes(resultdata, $result);

March 2003 PHP Architect www.phparch.com 67


TIPS & TRICKS

Let Me Do My Printing! ever portion of your site you want to cache in the out-
put buffering functions and capture their output. This
Annoyed that some functions just output text and can be used to cache elements such as drop down
dont let you capture it into a variable? Me to. If youre boxes that may take a while to create, or for the entire
using a solution such as virtual() or include() page. To cache a dynamic page to a static one, you
and you really need to get the result of that function could use a method such as that shown in Listing 1.
into a variable, how do you do it? Output buffering. That will take a test.php page and cache its output as
Normally when you hear about output buffering, its test.html.
someone telling a newbie to use it to get rid of their
header() errors about the headers already being
sent. While not very useful in that instance, buffering
If you're using Apache...
can be useful in others. By starting an output buffer
before your
you can also make use of
virtual(), include() or even print_r() func- the virtual() function
tion, you can store the contents of the buffer into a
variable and further process it or display it elsewhere. to call scripts that are not
An example of how this can be used is shown below.
PHP, such as PERL.
<?php

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


ob_start(); Search Friendly URLs
include('example.php');
$result = ob_get_contents();
ob_end_clean(); This is a topic thats mentioned every once in a while
?> on the PHP-General mailing list that bears repeating
here. If youre using Apache as your web server, you
Now, whatever the outcome of example.php was, its can force it to parse a file without an extension as a PHP
captured in the variable $result by file. James McGlinn offers this tip to our readers:
ob_get_contents(). The function ob_end_clean Many search engines balk at the prospect of spider-
then clears the text that was in the buffer, since you ing links which contain such characters as & or ?, and
have it in a variable, anyway. outright refuse to store your links. While this is proba-
bly a good thing in most cases, sometimes you need a
Cache That Please way around this. Heres one way of passing values to
your script without using characters which might jeop-
Methods using output buffering can create an effec- ardize your chances with the search engines.
tive cache for your program, as well. Simply wrap what- First, create a PHP script file without an extension.
Include some lines to parse out your variable values
Listing 1 from the Apache variable
<?php $_SERVER[REQUEST_URI]. One way of doing this
//start buffer is by using the strtok() function (Listing 2):
ob_start();
?>
<html> Listing 2
<head><title>My Page</title></head> <?php
<body> $values = array();
<?php $args = strtok( substr(
//create dynamic page here $_SERVER['REQUEST_URI'],1)
?> , "/" );
</body> while( $tok = strtok( "/" ) )
</html> $values[] = $tok;
<?php print_r( $values );
//get contents of buffer ?>
$contents = ob_get_contents();

//create static page name from .php page name The problem now is that without an extension on our
$file = substr($_SERVER['PHP_SELF'],0,-3).'html'; filename, Apache doesnt know to call the PHP inter-
//open file pointer and write contents preter to parse our file. Add the lines shown below to
$fp = fopen($file,'w'); your Apache configuration, replacing scriptFilename
fwrite($fp, $contents); with the name of the script youve just created:
fclose($fp);

//send contents of buffer to browser <Location /scriptFilename>


ob_end_flush(); ForceType application/x-httpd-php
?> </Location>

March 2003 PHP Architect www.phparch.com 68


TIPS & TRICKS

Reload Apache and voila, you will have easy access to ple times. This is a perfectly valid method that may be
any values you wish to pass to your script, and search suited to your application. However, for a solution
engines will quite happily spider away. If all goes well, thats not going to take up additional memory by cre-
the script in Listing 2 will produce the output shown in ating an array (which could become quite sizable for
Figure 1 if called with a URL such as large result sets), you can simply use the
www.yourdomain.com/scriptFilename/another/php/ar mysql_data_seek() function to reset the result set
chitect/tip. back to the first row and cycle through your result set
again.
Figure 1 PHP offers the pg_result_seek() function for
Array PostgreSQL and the mssql_data_seek() function for
( Microsoft SQL that perform the same functionality as
[0] => another the mysql_data_seek() function. Im sure the APIs
[1] => php to other databases offer these functions as well.
[2] => architect Congratulation to James McGlinn and Cesare
[3] => tip DAmico for their tips being selected for publication.
) They will receive a free months subscription to
PHP|Architect! If you have a tip for other PHP
Professionals that cant be found in your common FAQ,
send it in for the chance to win your own subscription.
Resetting Record Sets

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


Cesare DAmico submitted this simple tip that
seemed to be a hot item on the PHP-General mailing About The Author ?>
list for a while. When youre running queries against the John Holmes is a First Lieutenant in the U.S. Army and a freelance PHP
database and you need to cycle through the result set and MySQL programmer. He has been programming in PHP for over 4
more than once for some reason, there are a couple years and loves every minute of it. He is currently serving at Ft. Gordon,
ways you can do it. Most people would put the result Georgia as a Company Commander with his wife, son, and another one
on the way.
into an array and use that later on in your code multi-

March 2003 PHP Architect www.phparch.com 69


BOOK REVIEWS
BOOK REVIEWS

For Your Reading


Pleasure

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


PHP Professional Projects ies, which dont look particularly enlightening.
By A. Wilfred, M. Gupta, et al. There are plenty of examples of this kind avail-
able for free on the Internet, and some of them
Published by Premier Press (although not nearly as many as I would person-
868 pages ally like to see) are done well enough that its
US: $49.99 actually possible to learn lots of interesting con-
Canada: $77.95 cepts from them.
Surprisingly, all this doesnt diminish the value
UK: 36.99
of PHPPP one bit. Teaching by example is a tried
and true tradition; after all, one example, just
It took me a while to
like a picture, is worth a thousand words, and
decide how to best
this is particularly true of something as complex
describe this book.
as software development. And theres no doubt
Superficially, its concept
that this book follows this maxim with a
is rather simple: PHP
vengeance: the authors cover all the aspects of
Professional Projects
each case study, from the basics of the HTML
(PHPPP) is a collection of
language to the intricacies of developing com-
PHP-related case studies,
plex applications (including a chapter on how to
including creating a reg-
use CVS). To give you an example of the level of
istration form, manipu-
completeness of PHPPP, this is one of the few
lating data from files and
books where I have been able to see the com-
with MySQL, writing a catalogue and shopping
mand-line version of PHP used and discussed in
card application and handling user authentica-
a meaningful wayand the book antedates the
tion.
existence of an official PHP-CLI but at least a
A book whose only goal is to provide case
couple of minor versions.
studies on PHP, however, would have very little
To be sure, PHPPP is not free of a few flaws.
reason to exist, particularly with these case stud-

March 2003 PHP Architect www.phparch.com 70


BOOK REVIEWS For Your Reading Pleasure

For one thing, the case studies do not follow a rather smaller book than the one-thousand
common thread. While this makes for nice and page monsters that publishers constantly try to
independent sections of the book that one can break bookstore shelves with. Believe it or not,
read without being forced to follow a particular this actually comes as a very welcome change as
order, it also limits its overall effectiveness, as far as I am concernednot because it means
there is a certain degree of overlapping at all lev- fewer pages to read for this review, but because
els. it means that the authors (and the publishers)
In addition, the listings are neither formatted have decided to provide their readers with the
for syntax highlighting (something that is very information they actually need.
useful even in a black and white book), and Even though it is only slightly less than five
there is no line numbering. As a result, the read- hundred pages, PPWS takes full advantage of its
er is often left wandering through pages and narrow topic and goes to a very high level of
pages of PHP code without the ability to prop- detail. The book begins with a description of
erly reference them back-and-forth with the what web services are and why (and when) they
remainder of the text. are useful, followed by an overview of XML and
Finally, the case studies make the assumption its role in the world of data interchange.
that register_globals is ona rather common The remaining seven chapters deal with topics

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


flaw (especially considering that this book was ranging from the popular (and very convenient)
written when PHP 4.0 had just become avail- XML-RPC and SOAP protocols, all the way to
able) that is nonetheless quite annoying. WSDL and UDDI, useful for designing and dis-
All things considered, PHPPP is not a bad covering web services. An entire chapter is ded-
book, but my feeling is that it would probably icated exclusively to security in the context of
work better as the textbook in a PHP course web services; together with the routine security
than as a standalone resource for someone who information that can be found in any PHP book
wants to improve his or her PHP skills. (secure mode, firewalls, and so forth), the
author focus on the most part on the XML secu-
Professional PHP Web Services rity modela topic that is too often ignored.
Wroxs PHP books are becoming more and
By J. Fuller, H. Fuecks, et al. more specializedboth a sign of how complex
Published by Wrox Press a topic PHP really is and how dedicated this
477 pages publisher is to our world. Yet, I cannot get over
USA: $49.99 the fact that a book with so many authors (this
one has seven!) is not as easy to read as one
Canada: $77.99
written by a single person, as the various styles
inevitably clash against each other. Overall, I
If youve read my arti-
think that PPWS fills an important niche that
cle on building an RSS
had been overlooked by the publishing commu-
aggregator, you know
nity. Although its a bit pricey, this book should
that I dont have any
be on your shelf if youre serious about enter-
great feelings of love for
prise development with PHP.
XML. Its not that I dont
think of it as a useful
[Marco: In case you were wondering, I was try-
technologymore prop-
ing to see how many instance of the word use
erly, I think of it as a use-
(or one of its derivatives) I could slip past our edi-
ful technology thats so
tor before he started censoring me out. If you can
grossly overused that it
count four instances of the word, I won.]
outgrows its usefulness1.
When Wrox sent us a copy of their new book
Professional PHP Web Services (PPWS), there- ?>
fore, I noted with a satisfied glance that it is a

March 2003 PHP Architect www.phparch.com 71


exit(0);

Let Me Throw
The First Stone
By Marco Tabini
exit(0);

As you have probably read on the pages of this issue of php|a, this month were launching a
new grant program for PHP-related projects. Never a person known for his humility, I personal-

Licensed to 63883 - Joseph Crawford (info@josephcrawford.com)


ly think that this is a wonderful initiative.
The true potential of PHP is too often hampered by a sense of impending doom that rears its
ugly head from time to time and that the community seems to be inflicting ontoupon itself. Its
true, we dont have a company the size of Microsoft or Sun Micro throwing its bottomless mar-
keting resources behind our language , but that only makes each accomplishment even more
relevant.
Thus, I was very disheartened by some comments that I received recently from people I pre-
viously had a lot of respect for. Their point of view seems to be that PHP is a good frontend
scripting language, and that it will never amount to anything more. Scalability? Backend opera-
tions? Nah, you need a true language, like C or (cough) Java, to take care of that stuff.
Well, then, what are we doing here? Our goal is accomplished. Lets close down the PHP team,
turn php|architect into The PHP Session & HTML Formatting Magazine and go home happy.
If all we want out of PHP is a tool for generating HTML pages in a dynamic fashion, theres no
reason to bother with trying out new stuff and improving the language.
Except, of course, that I dont buy that for even one minute. PHP is, and will remain, a great
tool for frontend operations, and php|a will continue to expand on that aspect of its functional-
ity. PHP, however, has a lot more potential as an all-purpose language, if only we can remove this
huge chip from our collective shoulders and stop thinking that Perl is better if youre writing shell
scripts, that Java is the only right choice for handling backend operations and that C is a high-
er-performance language. These assertions may well be true today, but dont have to remain true
forever.
To get an idea of just how self-damaging this mode de faire is, try this on for size: a compiler
for PHP (one that actually outputs a native binary executable) and a complete GUI-manipulation
extension. I dont know about you, but this sounds like a formidable opponent to Visual Basic to
meone that can actually sport Write Once, Run Everywhere as a legitimate business goal and
not as a marketing ploy.
Our Grant Program is, therefore, our way to reinvest in the community that we serve by pro-
viding concrete financial support to those projects that have the potential to improve PHPs pro-
file and capabilities.
If you will allow me a bit of (bad) poetic license, were throwing the first stonenot the bib-
lical kind, but the kind that makes ripples in the pond. To be sure, its a small stone, but the
waves may reach far enough for someone to come up with an idea thats going to bring PHP
one notch higher.

March 2003 PHP Architect www.phparch.com 72

Anda mungkin juga menyukai