www.phparch.com
FEBRUARY 2005
VOLUME IV - ISSUE 2
This copy is registered to: livia carboni jackson
[email protected] Plus: Security Corner, Reviews, News and much more...
LEARNING PHP WAS NEVER THIS MUCH FUN
php|Tropics Moon Palace Resort, Cancun, Mexico. May 11-15 2005
Come learn PHP in Paradise with us (and spend less than many other conferences)
Ilia Alshanetsky - Accelerating PHP Applications, Marcus Boerger - Implementing PHP 5 OOP Extensions, John Coggeshall - Programming Smarty, Wez Furlong - PDO: PHP Data Objects, Daniel Kushner - Introduction to OOP in PHP 5, Derick Rethans - Playing Safe: PHP and Encryption, George Schlossnagle - Web Services in PHP 5, Dan Scott - DB2 Universal Database, Chris Shiflett - PHP Security: Coding for Safety, Lukas Smith - How About Some PEAR For You?, Jason Sweat - Test-driven Development with PHP, Andrei Zmievski PHP-GTK2 For more information and to sign up: http://www.phparch.com/tropics Early-bird discount in effect for a limited time!
At php|tropics, take the exam and The Magazine For PHP Professionals
Get Zend Certified ...and we'll pay your fees!
TABLE OF CONTENTS
php|architect
TM
Departments
6
Features
Editorial
I N D E X
Blogging and the Damage Done
10 7
What’s New!
49
Test Pattern
PHP Under Fire by Christian Wenz
20
HTML_QuickForm and Smarty by David Perrin
The Myth of Reusable by Marcus Baker
29 55
Review
by Alex Pagnoni
The ZEND PHP Certification Experience by Peter B. MacIntyre
Enterprise Development with the iConnect Architecture
40
The PRADO Framework by Qiang Xue
68
Security Corner
PHP Security Consortium by Chris Shiflett
58
Secure Communications with PHP and SSH by Sara Golemon
72
exit(0);
Welcoming PHP to the World by Marco Tabini
February 2005
●
PHP Architect
●
www.phparch.com
4
EDITORIAL
Blogging and the R A N T S
TM
Volume IV - Issue 2 February, 2005
Damage Done
E D I T O R I A L
php|architect
H
ow can you tell if a technology has reached critical mass? Easy: you look at how much damage it’s causing. Pretty much every human invention is neutral in itself; any benefits (or drawbacks) come from the way it’s used: atomic energy can be used to power a city or destroy it; cars make it travel over distances that were unthinkable just a hundred years ago possible, and are happily destroying our planet with their polluting emissions. Blogs have finally reached the “do and die” level of usage. They started as glorified online diaries. Comments mutated them into electronic sounding boards. RSS turned them into a self-publishing tool. The corporate world has rapidly misused them into propaganda weapons. Running your own blog, as many of our readers and authors do, is, in many cases, a liberating experience. Personally, I like the idea of throwing my ideas out there in a free-form format and let other people take them apart, comment on them and, possibly, even use them. Many people, it seems to me, have pretty much the same attitude with regards to how they run their blogs. Where they go wrong, in my opinion, is in the fact that they continue to think of blogs as personal diaries, which is often incompatible with the way their readers see them. For my part, as much as I love writing on my blog, I often find myself being overly cautious about what I write in it—because, no matter what I think, people see it not as “Marco’s blog,” but as “php|architect’s blog.” This severely limits, in my mind, the range of topics I can cover and how I can cover them. In fact, this problem has come up more than once when people have criticized me either for dissing our competition or for failing to disclose ties with companies that were mentioned in my posts one way or another. In both the instances I recall, the comments were misplaced—I didn’t dis the competitors, but simply expressed a personal preference, and the ties between us and another company had nothing to do with my posts. Still, I respect the fact that, to many people, my blog is a way to peek into the less-public face of php|architect, which is just fine but requires a bit more thoughts than “I’ll write about what I had for breakfast.” Some people—and many companies—have yet to figure out what the limits of blogging are. Thus, we see employees of corporations posting proprietary information and getting fired for it (and justly so), and employees posting completely harmless information and getting fired for it anyway (in a decidedly unfair manner). What I find really funny is the fact that so much fuss is being made about blogs when they are, in fact, nothing more than the tip of the iceberg. Clearly, the problem is somehow connected to the fact that a weblog reads like a friendly chat but
Publisher Marco Tabini
Editorial Team Arbi Arzoumani Peter MacIntyre Eddie Peloke
Graphics & Layout Arbi Arzoumani
Managing Editor Emanuela Corso
News Editor Leslie Hill
[email protected] Authors Marcus Baker, Sara Golemon, Peter MacIntyre, Chris Shiflett, Alex Pagnoni, David Perrin, Christian Wenz, Qiang Xue
php|architect (ISSN 1709-7169) is published twelve times a year by Marco Tabini & Associates, Inc., P.O. Box 54526, 1771 Avenue Road, Toronto, ON M5M 4N5, Canada. Although all possible care has been placed in assuring the accuracy of the contents of this magazine, including all associated source code, listings and figures, the publisher assumes no responsibilities with regards of use of the information contained herein or in all associated material.
Contact Information:
[email protected] General mailbox: Editorial:
[email protected] Subscriptions:
[email protected] Sales & advertising:
[email protected] Technical support:
[email protected] Copyright © 2003-2004 Marco Tabini & Associates, Inc. — All Rights Reserved
Continued on Page 9
February 2005
●
PHP Architect
●
www.phparch.com
6
NEW STUFF
What’s New!
N E W
S T U F F
php|architect launches php|tropics 2005 Ever wonder what it's like to learn PHP in paradise? Well, this year we've decided to give you a chance to find out! We're proud to announce php|tropics 2005, a new conference that will take place between May 11-15 at the Moon Palace Resort in Cancun, Mexico. The Moon Palace is an allinclusive (yes, we said all inclusive!) resort with over 100 acres of ground and 3,000 ft. of private beach, as well as excellent state-of-the-art meeting facilities. As always, we've planned an in-depth set of tracks for you, combined with a generous amount of downtime for your enjoyment (and your family's, if you can take them along with you). We even have a very special early-bird fee in effect for a limited time only. For more information, go to http://www.phparch.com/tropics.
Maguma Workbench 2.2 Maguma has announced the release of Maguma Workbench 2.2 On 20 January 2005, Maguma published the newest version of Maguma Workbench. Maguma Workbench 2.2 has new features, more stability and a new pricing concept. During the same period Maguma will also publish the Maguma Workbench SDK, for more independence to create new modules for your Workbench. For this reason Maguma has created a competition for developers, to create new plugins. The new features, like the PHP Function list, drag´n´drop and the hotkey F12 (hide/restore tool windows) brings you more efficiency. Patches have also been applied to the PHP Code Parser, Class Wizard, Script Parameter Implementation and more (see Changelog for more details at http://support.maguma.com/?article=changelog220 ) With this release of Workbench we are also making a reality of “Make it your Workbench” which means now the core price of Workbench is only 69 Euro and it’s up to the user to choose which modules/features they want to add to the core product. The core provides the basic features such as Syntax highlighting, Class browsing, and script execution, while the extra modules provide the added features such as local filesystem browsing, debugging, regular expression developer, and much more. So each customer can buy only the function and modules that they need and will use. For more information visit: http://www.maguma.com/
The Zend PHP Certification Practice Test Book is now available! We're happy to announce that, after many months of hard work, the Zend PHP Certification Practice Test Book, written by John Coggeshall and Marco Tabini, is now available for sale from our website and most book sellers worldwide! The book provides 200 questions designed as a learning and practice tool for the Zend PHP Certification exam. Each question has been written and edited by four members of the Zend Education Board--the very same group who prepared the exam. The questions, which cover every topic in the exam, come with a detailed answer that explains not only the correct choice, but also the question's intention, pitfalls and the best strategy for tackling similar topics during the exam. For more information, visit http://www.phparch.com/cert/mock_testing.php
February 2005
●
PHP Architect
●
www.phparch.com
7
NEW STUFF
PostgreSQL 8.0 The folks at PostgreSQL have announced the release of PostgreSQL 8.0. This is the first PostgreSQL release to natively run on Microsoft Windows as a server. It can run as a Windows service. This release supports NT-based Windows releases like Win2000, XP, Win2003. Older releases like Windows 95, 98, and ME are not supported because these operating systems do not have the infrastructure to support PostgreSQL. A separate installer project has been created to ease installation on Windows: http://pgfoundry.org/projects/pginstaller . For more information visit: http://www.postgresql.org
Netdoc 1.25 The people at visiomode.com have announced the release of Netdoc 1.25 Netdoc is a content management system that focuses tightly on content creation and editing. Netdoc 1.25 comes with a complete user privilege system. Websites can be made global readable, only readable to some, or even global writable. These privileges can be applied also to any part of the website, and defining them takes generally only a couple of rules. Netdoc is made with PHP and it stores the data into a MySQL database and uses Apache as a web server. Netdoc is installed on the server and it creates dynamic pages with friendly URLs. The site is updated through the web interface directly on that same server and thus there is no pushing of content or FTP transfer taking place at any time. For more information visit: http://www.visiomode.com
curl and libcurl 7.13.0 curl.haxx.se has announced the latest release of curl and libcurl 7.13.0. Some of the changes include: • • • •
added —ftp-account and CURLOPT_FTP_ACCOUNT added CURLOPT_SOURCE_URL and CURLOPT_SOURCE_QUOTE obsoleted CURLOPT_SOURCE_HOST, CURLOPT_SOURCE_PATH, CURLOPT_SOURCE_PORT and CURLOPT_PASV_HOST added —3p-url, —3p-user and —3p-quote
This release also contains many bug fixes. For more information or to download, visit curl.haxx.se.
Check out some of the hottest new releases from PEAR.
File 1.1.0RC4 Provides easy access to read/write to files along with some common routines to deal with paths. Also pr vides interface for handling CSV files.
File_Passwd 1.1.2 Provides methods to manipulate and authenticate against standard Unix, SMB server, AuthUser (.htpasswd), AuthDigest (.htdigest), CVS pserver and custom formatted password files.
Text_Wiki 0.25.0 Abstracts parsing and rendering rules for Wiki markup in structured plain text.
Net_IDNA 0.5.0 This package helps you to encode and decode punycode strings easily.
Net_UserAgent_Mobile 0.21.0 Net_UserAgent_Mobile parses HTTP_USER_AGENT strings of (mainly Japanese) mobile HTTP user agents. It’ll be useful in page dispatching by user agents. This package was ported from Perl’s HTTP::MobileAgent.
February 2005
●
PHP Architect
●
www.phparch.com
8
NEW STUFF
Editorial Continued from Page 6...
Blogging and the Damage Done is, for all intents and purposes, cast in the Internet stone—as the Romans used to say, verba volant, scripta manent. What is written in a blog “sticks” and has the potential of embarrassing a company by short-circuiting its carefully choreographed PR spin machine. Is this really so bad, though? Consider this: information that goes on a blog stays on a blog. Yes, it can be crawled by a spider, but, eventually, any information that’s deleted at the source will drop off search engines. A posting being propagated by outside sources, such as other blogs, is a bit more problematic, but eventually even those blogs will let it go. Now consider this: Google owns a collection of some ten or more years’ worth of Usenet news. Ten years! Anything any one of us has ever posted on Usenet—as well as hundreds of public mailing lists—has stuck around for all this time (and is likely to stick around for a lot longer). The persistence of Usenet data, to me, is much more troublesome than blogs. There are plenty of posts I’d rather I never made (go ahead search them all and post them on your blog!), but for the most part the thing that bothers me is that I never made a posting with the idea that I was creating a permanent record of my mischief. On top of that, Usenet has been around for a lot longer than blogs—so there’s plenty of silliness going around for everyone. In the end, the corporate world seems to be developing its usual odi et amo relationship with blogs: on one hand, they’re a great tool for public relations, while on the other they are a source of infinite frustration. It’s the age-old problem of public relations: wanting as much third-party publicity as possible, which is often considered as more reliable than company-sponsored fluff, but only if the corporation is in complete control of it. In my neck of the woods, that’s called having your pie and eating it, too, although I suspect someone else may use a slightly cruder term connected to the food by-products of male cattle. But only on their weblog.
Looking for a new PHP Extension? Check out some of the lastest offerings from PECL.
pecl_http 0.2.0 Currently implemented features: • • • • • •
Building absolute URIs RCF compliant HTTP redirects Caching by “Last-Modified” and/or ETag (with ‘on the fly’ option) Sending data/files/streams with (multiple) ranges support Negotiating user preferred language/charset Convenient request functions to HEAD/GET/POST if libcurl is available
colorer 0.1 Colorer take5 is a syntax highlighting and text parsing library, that provides services of text parsing in host editor systems in real-time and transforming results into colored text. For details, see http://colorer.sourceforge.net/ While colorer is primarily designed for use with text editors, it can be also used for non-interactive syntax highlighting, for example, in web applications. This PHP extension provides basic functions for syntax highlighting.
maxdb 1.0 MaxDB PHP is an extension which provides access to the MySQL MaxDB databases. It is compatible with MySQL’s mysqli extension.
vld 0.8.0 The Vulcan Logic Disassembler hooks into the Zend Engine and dumps all the opcodes (execution units) of a script.
February 2005
●
PHP Architect
●
www.phparch.com
9
FEATURE
PHP Under Fire
F E A T U R E
by Christian Wenz
When praising the vast database support that PHP provides, the products that are mentioned most often are the likes of MySQL, SQLite, PostgreSQL and MSSQL, but there are several others. One database that has been rather neglected for a long time is Firebird. It’s time for a closer look.
“
PHP & MySQL” almost sounds like a tautology, but this is a bit unfair in respect to the other databases available. SQLite is a very strong contender, especially if there are many read operations and little in the way of write operations. PostgreSQL gets excellent reviews, but still struggles with the market share. Microsoft SQL Server (“MSSQL”) is usually only available in a heterogeneous network, but a renowned IT magazine benchmarked various web server setups (Apache, IIS, PHP, ASP.NET, MySQL, MSSQL) a couple of years ago, with Apache on Linux and MSSQL on Windows being the winning combination. A DBMS that has been mostly neglected by both developers and the media is the Firebird database. It all began on September 4, 1984, when former DEC employee Jim Starkey sat down and started to implement his own database server. This was later pursued at Borland, and resulting InterBase turned out to be a quite successful product that was well-received by the market (even though Borland tried to rename itself with very little success). The next important step in the development of the software turned out to be July 25, 2000. The same day the fatal Concorde crash near Paris happened, Borland (then probably Inprise) took the source code of their InterBase database system and turned it into open
February 2005
●
PHP Architect
●
www.phparch.com
source. This launched a new project, FirebirdSQL, that developed the database server further based on Borland’s code. The code was issued under the InterBase Public License v1.0, also known as IPL ( http://www.borland.com/devsupport/interbase/opensourc e/IPL.html), which is a variant of the Mozilla Public License. Speaking of Mozilla, when the Mozilla project started working on his standalone browser, they first codenamed it Phoenix. However, since there already existed a motherboard manufacturer with that name, the browser was rechristened “Mozilla Firebird.” This caused some confusion, at least according to the “Firebird-the-database” faction. After some discussions, the Mozilla project changed the name of its browser to the one we now all know: Firefox (which is not a fox, by the way, but a panda—despite the logo). The Firebird database, however, kept its name.
REQUIREMENTS PHP
4.x , 5.x
OS
Linux, Max, Windows
Other Software
Firebird
Code Directory
phpfire
+ InterBase extension
10
FEATURE
PHP Under Fire
The current version of Firebird, as of writing this article, is 1.5.2. The codebase of this release, however, is already called Firebird 2, the next large milestone. The reason for this is that the original codebase turned into open source by Borland was written in C; the major goals for Firebird 2 are a general code-cleaning and, of course, porting the product to C++. The developers hope—among other things—for a serious speedup of the software. The feature list of Firebird 1.5 is already quite impressive, including ACID compliance (including full transaction control), stored procedures (written in PSQL), triggers, UDFs, generators, full SQL-92 Entry Level 1 support and most of SQL-99—some other databases took a lot of time to achieve this. The database is available on all major platforms, including Linux, Mac OS X and Windows. Since Firebird supports an OS-independent data format, changing the platform is rather easy. Installation Firebird is available from its SourceForge project site at http://firebird.sourceforge.net/. The download section features both the source code and binary releases: RPMs for Linux, packages for Mac OS X and both ZIP archives and self-contained installers for the Windows platform. There are two flavours available: the “Classic” version of Firebird works on a file basis, which makes it very easy to deploy but really hard when parallel processes try to access the same database file (and maybe even try to perform write operations on it). But Firebird also comes in a second flavour, called “Super Server,” which implements a server process that operates between the client applications and the database file, caring about things like request processing and threading. For FreeBSD, Mac OS X and Solaris, currently only the classic versions are available; in fact, Mac users are stuck with the older version 1.51 (that’s correct—the current version is indeed 1.5.2, with one more dot). There is also an embedded server available,
Figure 2
Figure 3
Figure 1
February 2005
making it possible to bundle Firebird with other applications (similarly to how SQLite now comes with PHP 5). Windows and Mac users have it quite easy to install Firebird: they can download convenient installers that take care of copying the files to the system (see Figures 1 and 2). For Windows, a ZIP package is also available, so the users can take care of copying the files themselves. Note that the Mac installation hung consistently on one of my test machines—if something like this happens, you will have to go back even further to a previous version. Under Linux, you can either install the RPM file or do some more work yourself. For the latter, the TGZ archive contains the shell script install.sh, which takes care of the installation and copies firebird into /opt/firebird/ (see Figure 3). Among these files is a shell script, called uninstall.sh, that does exactly what it sounds like. After the installation process, the database has a mas-
●
PHP Architect
●
www.phparch.com
11
FEATURE
PHP Under Fire
ter user. It is called sysdba (or SYSDBA, as Firebird does not care too much about upper or lower case when it comes to usernames) and has the default password masterkey. If you’re using the Linux installer, you can enter your own password for this user; on other systems, you should change sysdba’s password immediately after installation for obvious security reasons. In the bin directory of Firebird, you find the gsec tool, which can be used to both change passwords and add or delete users. However, for real convenience, you can use a web-based tool—we’ll look into that later on. There are several client libraries for Firebird, including an ODBC driver and, of course, a PHP extension. However, trying the URL http://php.net/firebird turns up nothing. The reason for this is a historical one: since Firebird was previously called InterBase and PHP has always had great database support, the extension carries interbase in its name and the associated manual page can be reached at http://php.net/ibase. Installation under Windows is easy: the extensions directory (called extensions in PHP 4 and ext in PHP 5) contains the file php_interbase.dll that hooks you up with the client libraries of Interbase, so the only thing you have to do is to modify php.ini: extension=php_interbase.dll
If you are compiling by yourself, be sure to use the compilation switch —with-interbase. You can also provide the directory to the libraries: —withinterbase=/var/firebird/lib. Afterwards, the obligatory call to phpinfo() or php -m (see Figure 4) shows the installed extension. Administration The first steps when administering the newly installed database is, of course, to change the credentials for the master user—and create a new one that takes care of the sample application we are about to develop. This can be done using a console: the bin directory of the Firebird installation contains the gsec tool, which can be used for this task. However, since we are using PHP anyway, this is a good opportunity to use a web-based interface written in PHP. The tool is called ibWebAdmin (“ib” standing for Interbase), and available for free at http://ibwebadmin.sourceforge.net/ (see Figure 5). Before actually using this software, you first have to configure it. After unpacking the archive and copying it onto the web server, the file inc/configuration.inc.php requires some special attention. There, you have to provide some information about the Firebird installation. Most important are the following values: • BINPATH—Directory to the Firebird binaries • SECURITY_DB—Location of the Firebird data-
February 2005
●
PHP Architect
●
www.phparch.com
base that contains user names and other security-related information (usually called security.fdb ) • TMPPATH—Temporary path, where the web server requires read and write access into Windows users have the additional requirement to use forward slashes only in their filenames. Here are some sample values for a default installation under Windows. Linux users can more or less use the values already present in the configuration.inc.php file without making any change. define(‘BINPATH’, ‘C:/Progra~1/Firebird/Firebird_1_5/bin/’); define(‘SECURITY_DB’, ‘C:/Progra~1/Firebird/Firebird_1_5/security.fdb’); define(‘TMPPATH’, ‘/tmp/’);
Also, the PHP configuration requires some special settings. For instance, ibWebAdmin is still using the $HTTP_*_VARS arrays to access form and session data; as a consequence, you must not set register_long_arrays in php.ini to Off, although this is the standard setting in php.ini-recommended. After this is done, however, the software works quite well. You can then first login to an existing database or create a new one. The best way is to first connect to security.fdb and then add a new user. Finally, create a new database file (in our samples: /tmp/fireblog.gdb, your mileage may vary), providing the new user credentials. Then, create a file (cconnect.inc.php) where you provide the username, associated password and the hostname. The latter is a concatenation of the server name, a colon and the (full) path to the database file. This file will be included in all scripts, enabling it to provide the database parameters application-wide.
But back to ibWebAdmin. There, you can quite easily set up your tables using a neat WYSIWYG interface. Under the SQL tab, you can also directly enter the SQL code that creates the tables. The sample application will be a really simple weblog with a comment feature. For this, we need two tables. One contains the blog entries. As mentioned earlier, it’s a very simple blog, so this table only has a few fields: title of the entry, text of the entry, creation date of the entry. For the latter field, we set a default value of ‘now’. In Firebird, this automatically uses the current date and time as default value whenever a new record is inserted. 12
FEATURE
PHP Under Fire
if (false !== ($db = ibase_connect($host, $user, $pass))) { echo ‘Connection established!’; } else { echo ‘Something went wrong ...’; } ?>
DROP table entries; CREATE table entries ( id INTEGER NOT NULL PRIMARY KEY, title VARCHAR(100), content VARCHAR(2000), creation DATE DEFAULT ‘now’ );
As you see, we also have a primary key: the id field. There is nothing like AUTO_INCREMENT or IDENTITY in Firebird, but something even better: Generators. A similar concept exists in PostgreSQL and Oracle. These generators can later be used to create a suitable value for id, even using a trigger or manually, as you will see in a later listing. Here is the code to create a generator that will then be used for the entries table: DROP GENERATOR entries_gen; CREATE GENERATOR entries_gen;
Users can add comments to weblog entries. These entries will also be saved in the database, but in another table. This one is rather similar in concept to the entries table, but also contains a foreign key—as well as its own generator:
If this generates no error, it is time to actually send SQL statements to the database. This is done using the ibase_query() function. This one expects two parameters: the database handle and the SQL statement to be sent. The first script will be a PHP page that allows the maintainer of the weblog to enter data into the entries table. This is the form:
February 2005
●
PHP Architect
●
www.phparch.com
15
FEATURE
PHP Under Fire
once and gets a different parameter each time: while ($row1 = ibase_fetch_object($result1)) { $result2 = ibase_execute($selectcomments, $row1>ID); while ($row2 = ibase_fetch_object($result2)) { printf(‘
<small>%s
’, $row2->CONTENT); }
After each blog entry and the associated set of comments, a small form is printed out where a new comment may be added: print ‘’; print ‘ID . ‘“ />’; print ‘’; print ‘’; print ‘’;
The complete code for this script, select.php, can be found in Listing 2. Figure 6 shows its output. Transactions Firebird also offers decent support for ACID transactions. We could need this in the file insertcomments.php, where the comment forms from the previous sections post their data to. There, we first have to check if an entry with the provided ID exists in the entries table. If so, we can add the comment data to the comments table. However, what happens if a parallel process deletes the blog entry between the SELECT FROM entries statement and the INSERT INTO comments statement? In this case, transactions come in handy. In order
to implement them, you first have to call ibase_trans(), providing the database handle. The return value of this function call is then a transaction handle. You can use this handle for subsequent calls to ibase_query() or ibase_prepare() instead of the database handle. However, at the end of the script all transactions will be rolled back (new behavior under PHP 5) unless they are explicitly committed to the database using ibase_commit(). Therefore, you should always call this function to end your transactions. In order to rollback, you use the function called ibase_rollback(). The insert-comments.php script from Listing 3 implements the aforementioned concept. First, it checks if there is a blog entry with the provided ID by trying to SELECT data and then checking if anything was returned. If that succeeds, the provided content is written into the data source. Finally, ibase_commit() submits the statements within the transaction to the data source. Advanced Features Firebird offers several other features for advanced users. Among them is the support for triggers. Imagine that you delete one blog entry from the database. What happens to all its comments? They have to be deleted as well. For this, a trigger comes in very handy. The following snippet, available in the code archive as a file (ttrigger.sql), gets rid of the useless comments whenever a blog entry gets removed: DROP TRIGGER delete_entry;
Listing 3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
Firebird & PHP
Back to weblog
February 2005
●
PHP Architect
●
www.phparch.com
16
FEATURE
PHP Under Fire
SET TERM !!; CREATE TRIGGER delete_entry FOR entries AFTER DELETE AS BEGIN DELETE FROM comments WHERE entries_id=OLD.id; END!! SET TERM; !!
database. You first have to create a database that contains a field with data type BLOB:
You can directly send this SQL code to the database using ibase_query(), or you use the isql command line tool:
Afterwards, the following steps write binary data into the table:
./isql -u phpa -p aphp /tmp/fireblog.gdb < trigger.sql
From this point on, deleting one entry takes care of all comments. Another nice feature of Firebird is the support for Stored Procedures and UDFs, User Defined Functions. Both are a way to include your own code within the database and then execute it. However, this is outside of the scope of this article—you do not need any special PHP features to use this functionality. However there are some additional PHP functions that can be used with Firebird data sources. For instance, you can create your own user management and do not have to rely on ibWebAdmin: • ibase_add_user()—adds a user to the security database (provided in the first parameter), using the username and password of a user that may add users (parameters #2 and #3) and the user name and password of the new user (#4 and #5). • ibase_delete_user()—deletes a user (parameter #4) from the security database (parameter #1), using the privileges of the user/password combination provided in parameters #2 and #3 • ibase_modify_user()—changes user information, using the same function signature as ibase_add_user() The behavior of these functions, however, has changed in PHP 5 in combination with the introduction of the SuperServer edition of Firebird. Now, you have to use the service manager to connect to the security database and change user information: include ‘connect.inc.php’; $server = ibase_service_attach($host, $user, $pass); ibase_add_user(...); //or ibase_delete_user() //or ibase_modify_user() ibase_service_detach($server);
However, obviously, using a GUI tool is much more convenient. BLOBs Among the data types supported by Firebird are also BLOBs—Binary Large OBjects. They can be used to store binary data—even large amounts of it—in the
February 2005
●
PHP Architect
●
www.phparch.com
DROP TABLE blobdata; CREATE TABLE blobdata(filename VARCHAR(255), data BLOB);
include ‘connect.inc.php’; $db = ibase_connect($host, $user, $pass); $blob = ibase_blob_create($db); ibase_blob_add($blob, $data); $id = ibase_blob_close($blob); $sql = ‘INSERT INTO blobdata(filename, data) VALUES (?, ?);’; ibase_query($db, $sql, $filename, $id);
Listing 4 contains a small application (bblob.php) that lets the user upload a file and writes it directly into the database, using PHP’s HTTP upload functionality. Reading back the binary data from the database is performed using just a few steps as well: simply SELECT the blob column from the table, provide the result as parameter for ibase_blob_info() (to get information about the BLOB field) and to ibase_blob_open() (to open the BLOB). Then, ibase_blob_get() retrieves the BLOB data: include ‘connect.inc.php’; $db = ibase_connect($host, $user, $pass); $result = ibase_query(‘SELECT data FROM blobdata’); $row = ibase_fetch_object($result); $blobinfo = ibase_blob_info($row->DATA); $blob = ibase_blob_open($row->DATA); $blobdata = ibase_blob_get($blob, $blobinfo[0]);
Using Database Abstraction Layers Several well-known database abstraction layers offer Firebird support. Most prominently, the upcoming and long-anticipated PDO (PHP Data Objects
Listing 4 1 2 3 4 Firebird & PHP 5 6 7 20 21 22 23 24 25
17
FEATURE
PHP Under Fire
Interface) layer http://pecl.php.net/package/PDO includes a provider for Firebird data sources http://pecl.php.net/package/PDO_FIREBIRD, but you should be aware that development has not finished yet. Users of Unix/Linux can install the required packages using the PEAR installer: pear install PDO pear install PDO_FIREBIRD
In the not too distant future, this will change to the following: pecl install PDO pecl install PDO_FIREBIRD
Windows users can refer to the following address at http://snaps.php.net/win32/PECL_5_0 for compiled PHP 5.0.x versions of the modules or to http://snaps.php.net/win32/PECL_UNSTABLE for 5.1.x versions. Once you do that (and assuming that everything goes to plan), a unified access to Firebird data sources using the “future” of PHP’s database access becomes available (see Figure 7). As you might have guessed, you will also get lucky if you search PEAR for suitable packages. Both http://pear.php.net/package/DB and PEAR::DB PEAR::MDB http://pear.php.net/package/MDB support Firebird (and InterBase) with special drivers. Thus, as you can see, Firebird is a well-respected and also fullyfeatured alternative to more mainstream DBMS, despite its relatively low penetration in the PHP arena. So do give Firebird a try—and maybe you’ll “fire” one of your other database systems instead!
Figure 7 About the Author
?>
Christian Wenz is author or co-author of over four dozen books, frequently writes for renowned IT magazines and speaks at conferences around the globe. He is Germany’s very first Zend Certified Professional, principal at the PHP Security Consortium and maintainer or co-maintainer of several PEAR projects.
To Discuss this article:
http://forums.phparch.com/199
Award-winning IDE for dynamic languages, providing a powerful workspace for editing, debugging and testing your programs. Features advanced support for Perl, PHP, Python, Tcl and XSLT, on Linux, Solaris and Windows.
Download your free evalutation at www.ActiveState.com/Komodo30
FEATURE
HTML_QuickForm and Smarty
F E A T U R E
by David Perrin
Had it with redundant form validation? Does separating application and presentation code have you all mixed up? Check this article for an overview of the Smarty templating engine, the PEAR HTML_QuickForm form building module and a complete mini application that cleanly divides application and presentation as well as handling comprehensive form validation. Like many web developers, I often work with data submitted from web pages or web service clients into PHP applications. Recently, while working on a validation class to ensure quality user input, I happened upon the PEAR module HTML_QuickForm. As it turned out, a great deal of the validation I needed to do for the project was easily handled by using this module. Additionally, specialty form validation could be accomplished with ease by assigning custom functions to do validation. What a treat! Sometimes, it is humbling to have to throw away a few hours of work to take advantage of something which allows you to do more quickly—with HTML_QuickForm, I didn’t mind. As has been the case when beginning to learn other PHP modules or other programming elements of style, my first reaction to HTML_QuickForm was enthusiasm and intrigue, quickly followed by a fear and apprehension of the learning curve. After working through a couple of HTML_QuickForm examples from the PEAR website and elsewhere (see references), however, it was surprising to see how little code I needed to take care of the tasks I work on most every day. In addition, if you find yourself needing further flexibility and features in validation or presentation, it isn’t hard to integrate HTML_QuickForm with your own custom validation functions and display forms using popular templating
February 2005
●
PHP Architect
●
www.phparch.com
engines like Smarty a nd Flexy. External PHP Libraries Libraries of PHP code can enable developers to accomplish more with less code. There are a number of wellwritten libraries with a variety of uses that are available. In addition to writing less code, by taking advantage of code able to save a great deal of
REQUIREMENTS PHP
4.06
OS
Any
Other Software
MySQL 4, Smarty, PEAR DB
Code Directory
quickform
RESOURCES
i
URL http://smarty.php.net URL http://pear.php.net URL URL
http://www.thelinuxconsultancy.co.uk/smartyguide.html http://www.devarticles.com/c/a/Web-DesignUsability/Using-HTML-QuickForm-To-Manage-WebFo r m s -Pa r t -2 /
20
FEATURE
HTML_QuickForm and Smarty
time. Of course writing applications with libraries isn’t all peaches and cream—it can be difficult, especially at first, to learn a new library. Beyond just learning a new syntax, sometimes you’ve got to dramatically change your way of thinking about solving problems. It is also important to consider portability factors should you need to move your application to other servers when selecting code libraries. I’ve found the biggest obstacle when learning a new tool to be keeping a beginner’s mindset and not getting too frustrated when the code doesn’t immediately produce the results I expect. To give you a demonstration of some of HTML_QuickForm’s capabilities, we’ll create a web registration application that validates user inputs using built-in methods. We’ll also take advantage of validation through regular expressions and custom functions. The Smarty template engine will help keep our presentation logic separate from the application logic. Our database requirements are limited to simple selects and inserts. For demonstration of this mini app, MySQL 4+ suits our needs just fine, but an alternate database server could easily be substituted. PEAR’s DB module is used to pass the few simple database queries to a MySQL database. Should you prefer a different database server, the application should only require a modification of the database connection. First, though, a brief introduction to the tools of Smarty and HTML_QuickForm. Why Use a Template Engine? PHP template engines offer developers an approach to development that helps draw a division between presentation in HTML and application code in PHP. Ever find yourself lost in a PHP script composed of mostly HTML with very little PHP application code? Or find that lines and lines of echo and print statements turn your otherwise clean code into a bit of a kludge? Many articles have been written on the value of separating presentation code of a website (e.g.: HTML, CSS, graphics, and so on) from the application logic. Templating engines offer a mostly painless way to do make this separation happen. Listing 1 // select user and get row $sql = ‘SELECT * FROM tbl_user ‘ . ‘WHERE email = ‘ . $db->quoteSmart($_REQUEST[‘email’]) . ‘ AND password = ‘ . $db->quoteSmart($_REQUEST[‘password’]); $row = $db->GetRow($sql); // display appropriate login message if (count($row) == 0) { echo ‘Sorry, login failed.’; } else { echo ‘Login was a success!’; }
February 2005
●
PHP Architect
●
www.phparch.com
Fortunately, there are many well-documented PHP templating engines out there to choose from. Among the more popular PHP templating engines are PHP Savant, patTemplate, Smarty and PEAR’s HTML_Template_Flexy. Other scripting languages have templating engines too: Mason, for example, is a popular example for Perl, while Cheetah appears to be one of the most selected template engines for Python, and even Ruby has several. It seems that, as PHP’s overall popularity has increased, so has its popularity for use in sophisticated web applications. The greater the sophistication of an application, the more difficult that application can be to maintain when application code is combined with the HTML. Through a bit of a Forrest Gump-like observation, even when I’ve got a good plan, the more lines of code I write, the more complicated that code gets. Lengthy, complicated code is tough to work with. Lengthy, complicated code is even tougher to work with if it is code that you’ve inherited from some other programmer. Clean code goes a long way toward making an application easier to maintain, which, in turn, can improve your present and future relationships with those who may be assuming maintenance duties of your project. That is really the biggest reason why I’m sold on templating engines: they allow you to write clear, simple code. This is done by demanding a level of discipline of the developer in keeping the presentation code contained within the template file. Enough Already, How About an Example? Fair enough. Take a look at Listing 1 for a snippet of mixed application and presentation code. First, an SQL statement is built. The SQL statement looks to select all matching records from a user table where the email and password columns in the table matches the $_REQUEST input. The $_REQUEST values are run through the PEAR DB function quoteSmart(). This function safely formats the input so that it can be used in building SQL statements. quoteSmart() accepts the values from $_REQUEST[‘email’] and $_REQUEST[‘password’] and adds and escapes quotes appropriately. The SQL statement is then run using the $db->GetRow() method, and the results of that query are assigned to the $row variable as an array. If $row has no values, a login error message is displayed, otherwise a successful Listing 2 include(‘mySmarty.php’); $sql = ‘SELECT * FROM tbl_user ‘ . ‘WHERE email = ‘ . $db->quoteSmart($_REQUEST[‘email’]) . ‘ AND password = ‘ . $db->quoteSmart($_REQUEST[‘password’]); $row = $db->GetRow($sql); $smarty->assign(‘row’, $row); $smarty->display(‘template.tpl’);
21
FEATURE
HTML_QuickForm and Smarty
login message is shown. Now, in all truthfulness, this is a very readable and easy to maintain piece of code as it stands. It is possible that, with the addition of some comments, even a nondeveloper would be able to figure what is going on with these few lines. For an experienced developer, even if there were a few hundred lines of code like this, they would likely still be easy to maintain. The case for using templating engines becomes easier to make when both the length and sophistication of the code is greater than this particular example. Try That with a Smarty Template With Smarty, we split the original PHP file into two files: a PHP file (Listing 2) and a template file (Listing 3). The first line of the PHP snippet hides some gory details by including the PHP file mySmarty.php, which creates an instance of the Smarty class. We’ll get into the code for configuring Smarty later. The SQL statement and execution of the SQL statement is identical to the snippet in Listing 1. Following the execution of the SQL statement through $db->GetRow($sql), the $row variable is assigned to the Smarty variable called row with $smarty->assign(‘row’, $row). If you are new to working with classes, it may feel strange to work with different sets of variables. For me, after becoming more familiar with this syntax, it now seems more appropriate to have a set of variables reserved exclusively for the purpose of presentation. The last line of the PHP calls the Smart display method ($$smarty->display(‘template.tpl’)), where the template file template.tpl is passed as an argument. At that point, Smarty reads and interprets our template file. In Listing 3, a test is performed to determine if the $row variable has a count of zero and the appropriate message is then displayed. For those new to Smarty, that gives you a brief feel for the syntax. The Smarty Crash Course at http://smarty.php.net/crashcourse.php offers a great introduction. Listing 4 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
tags are special to PHP. If you prefer, Smarty allows you to change the default curly brace delimiter to something different. We’ll stay with the default delimiter for this example. If you stay with the curly braces, you can keep HTML exempt from Smarty interpretation by adding the special tags of {literal} and {/literal} . Smarty comments can be added in your template code as {* comment *}. The template progresses into the HTML body, where the $form_data.header.regHeader value is displayed. This value was defined earlier in index.php as part of the $form object with the line:
Figure 5
Figure 6
$form->addElement(‘header’, ‘regHeader’, ‘Register today for our upcoming conferences!’);
In index.php, the renderer’s elements were made available to the Smarty template variable $form_data with the line: $tpl->assign(‘form_data’, $renderer->toArray());
Check out Figure 5 for a look at the
February 2005
●
PHP Architect
●
www.phparch.com
26
FEATURE
HTML_QuickForm and Smarty
Smarty debug console window. $form_data has a great bunch of these elements assigned to it. The registration form begins with and builds out the form tag to include the name, id, method and action that were defined in index.php. This example doesn’t use hidden fields, but many web applications rely on them. If we had hidden field form elements to display, they could all be rendered with the single tag {$form_data.hidden} . The rest of the template builds rows of form labels and form elements. The first few lines display the conferenceid select element (I reformatted the code a bit to make it more readable here): {$form_data.conferenceid.label} {$form_data.conferenceid.html}
In this template file, you can see how all form elements—even those of different types—are displayed in a very uniform way. So What About Validation Messages? A-ha! That is one of the best parts. Back in the index.php file, template styles were defined for required and error. Now, the required field elements have an asterisk before their label when displayed in a browser. If an error is encountered, the label’s colour changes and an error message is shown to the right of the form element. Nothing needs to be done to the template file to make this happen. See Figure 6 for an example. Figure 7 shows the result of a successful form submission.
following the form element like this might not be preferred. An alternative is to display the errors grouped together. Errors are available through $form_data.errors as an array. The following template code loops through that array and returns the error messages. {if $form_data.errors|@count > 0 } {foreach from=$form_data.errors item=err} {$err} {/foreach} {/if}
If you are looking to take your application design beyond a simple presentation and application division and wish to categorize components within your application further, you may wish to investigate Model View Controller (MVC) architectures. There are a number of implementations of this design pattern. They share the division of an application into layers of “Model” (for data activity), “View” (for the interface) and ‘Controller’ (for determining what the program should do). There are a number of different PHP application frameworks using this architecture, including PHPMVC, Phrame and Mojavi. Projects with a great deal of application logic can be made clearer by isolating the controller
●
PHP Architect
●
Conclusion Through this mini application, we’ve covered some of the features available with Smarty and HTML_QuickForm. We’ve seen how HTML_QuickForm can be used in the building and processing of forms and how these tools together can help create a project with a separation of application and presentation. PHP is a fantastic language with a talented and generous community. We’ve touched just on a few among the hundreds of libraries available for PHP that can enable you to develop quality applications without completely reinventing the wheel. I’ve found the tools HTML_QuickForm and Smarty have a kind of synergy that can be easily leveraged in producing flexible web forms with clean application and presentation code.
Figure 7
About the Author ?> David lives in Southwestern New Hampshire with his foxy wife, their two cats and their Australian Shepherd. When not at or driving to or from work, he enjoy KEXP Seattle, hope, irony, rural living, web development and the outdoors. He can be reached at
[email protected].
One Step Beyond In some instances, displaying errors
February 2005
code from your application code. In the December 2004 PHP Architect, Lukasz Karapuda wrote an interesting article about his simplified implementation of MVC. I’m working on a side project which uses this adaptation and am finding it makes easier to make feature enhancements and modifications by touching a limited amount of code.
www.phparch.com
To Discuss this article:
http://forums.phparch.com/200 27
NEXCESS.NET Internet Solutions 304 1/2 S. State St. Ann Arbor, MI 48104-2445
http://nexcess.net
PHP / MySQL SPECIALISTS! Simple, Affordable, Reliable PHP / MySQL Web Hosting Solutions P O P U L A R S H A R E D H O S T I N G PAC K A G E S
MINI-ME
$
6 95
SMALL BIZ $ 2195/mo
/mo
500 MB Storage 15 GB Transfer 50 E-Mail Accounts 25 Subdomains 25 MySQL Databases PHP5 / MySQL 4.1.X SITEWORX control panel
2000 MB Storage 50 GB Transfer 200 E-Mail Accounts 75 Subdomains 75 MySQL Databases PHP5 / MySQL 4.1.X SITEWORX control panel
16 95
/mo
900 MB Storage 30 GB Transfer Unlimited MySQL Databases Host 30 Domains PHP5 / MYSQL 4.1.X NODEWORX Reseller Access
NEXRESELL 2 $
We'll install any PHP extension you need! Just ask :) PHP4 & MySQL 3.x/4.0.x options also available
59 95
/mo
7500 MB Storage 100 GB Transfer Unlimited MySQL Databases Host Unlimited Domains PHP5 / MySQL 4.1.X NODEWORX Reseller Access
: CONTROL
php 5 4.1.x
P O P U L A R R E S E L L E R H O S T I N G PAC K A G E S NEXRESELL 1 $
NEW! PHP 5 & MYSQL 4.1.X
PA N E L
All of our servers run our in-house developed PHP/MySQL server control panel: INTERWORX-CP INTERWORX-CP features include: - Rigorous spam / virus filtering - Detailed website usage stats (including realtime metrics) - Superb file management; WYSIWYG HTML editor
INTERWORX-CP is also available for your dedicated server. Just visit http://interworx.info for more information and to place your order.
WHY NEXCESS.NET? WE ARE PHP/MYSQL DEVELOPERS LIKE YOU AND UNDERSTAND YOUR SUPPORT NEEDS!
php 4 3.x/4.0.x
128 BIT SSL CERTIFICATES AS LOW AS $39.95 / YEAR DOMAIN NAME REGISTRATION FROM $10.00 / YEAR GENEROUS AFFILIATE PROGRAM
UP TO 100% PAYBACK PER REFERRAL
30 DAY MONEY BACK GUARANTEE
FREE DOMAIN NAME WITH ANY ANNUAL SIGNUP
ORDER TODAY AND GET 10% OFF ANY WEB HOSTING PACKAGE VISIT HTTP://NEXCESS.NET/PHPARCH FOR DETAILS
Dedicated & Managed Dedicated server solutions also available Serving the web since Y2K
FEATURE
Enterprise Development with the iConnect Architecture
F E A T U R E
by Alex Pagnoni
This article explains about the PHP 5 iConnect architecture and the reasons it has been designed. It also provides a complete example of a simple distributed web application that uses the whole architecture.
I
n recent years, two important changes have occurred in software engineering. The first change is one of scale—in fact, a continuous change since the first program was written. The second one is distribution. More and more applications are expected to run over the Internet, a context that is distributed by nature and that tends to drive huge increases in application load. So, in a more distributed system, there is the need to ensure that the system will be robust enough in the event of partial failures, that it will handle the required load and that it will be scalable when the demand for concurrent use exceeds the original specification. The consequence of these considerations is the need of an architecture that allows systems to face non-functional requirements (capabilities) such as performance, scalability, manageability, availability, security, capacity, extensibility and so on, while at the same time managing and hiding the resulting complexity to developers. Unfortunately, most—if not all—of the PHP platforms available are monolithic and CMS-oriented, and the vast majority of PHP applications have their own underlying platform that reinvents the wheel each time. Also, in PHP “there are many ways to do things,” but at the same time there is no—or, at least, I don’t know of any—particular “recommended” way to perform enter-
February 2005
●
PHP Architect
●
www.phparch.com
prise application development that meets the requirements above. These are the reasons why the Solarix iConnect architecture has been designed and implemented. About the iConnect architecture So, what is the iConnect architecture? iConnect is an Open Source, PHP 5-based, multi-tier and service-ori-
REQUIREMENTS PHP
5.x
OS
Any
Other Software
N/A
Code Directory
iconnect
RESOURCES
i
URL http://www.carthag.org/ URL http://www.solarix.it/ URL URL
http://www.solarix.it/index.php/downloads/downl oads/ http://www.solarix.it/index.php/home/standard/ ?content_page=technology/architecture/index.htm l
29
FEATURE
Enterprise Development with the iConnect Architecture
ented set of containers and components; it is also a collection of specifications and a methodology for building web-enabled enterprise applications. Most of the components are licensed under the Mozilla Public License, which is similar to the LGPL, so you can use the architecture with commercial applications. iConnect was built over years of experience in PHP platforms and applications delivered to small/medium businesses and enterprises. iConnect represents the result of experience in many areas, including best practices, frameworks, design patterns and idioms. iConnect defines a five-tier architecture, as shown in Figure 1: • Resource tier: contains resources such as data/legacy; • Integration tier: logical integration to resources; • Business tier: contains enduring business themes; • Presentation tier: composes content using business information; • Client tier: renders the UI and provides human interaction. iConnect components cover the middle tier (presentation, business, integration), while the resource tier is managed by databases, legacy systems and other applications, and the client tier is left to web agents. iConnect also defines a multi-layered architecture: • Lower platform: the operating system (hides the hardware); • Upper platform: the web and application servers (hides the implementation of the operating systems); • Virtual platform: the APIs and specifications (provides the interfaces to the application server); • Application: the components (business and presentation logic).
architecture (see Figure 2): • Carthag, also known as iConnect Platform: a common platform for the whole architecture, just like J2SE for J2EE; • Web services, data access and a set of connectors for the integration tier; • iConnect Enterprise Application Server (EAS): contains and delivers distributed business logic, in the form of deployable modules, like EJB; • iConnect Web Application Server (WAS): deploys and serves web applications (called webapps), with something similar to servlets (webapp handlers) and JSP (PHP itself); • iConnect Portal Server: organizes WAS web applications, putting together EAS modules and the (portlet-like) presentation logic using a MVC design pattern. As you can see, iConnect is in many ways similar to the J2EE architecture; iConnect, however, tries to deliver a simple set of APIs and specifications and lightweight components. Carthag, the iConnect Platform Carthag is the platform of the whole architecture and sits just over the PHP runtime. Carthag introduces in PHP 5 a way of organizing application files and classes that is similar to Java. The familiar Java concepts of packages, class files and class loaders have been implemented in Carthag; there are other similar projects in the PHP community, howFigure 2
All of the capabilities, tiers and layers are strictly orthogonal. The key to creating durable enterprise architectures is to allow trade-offs among them. iConnect tries to allow such trade-offs by providing a set of building blocks. iConnect building blocks The following are the building blocks of the iConnect Figure 1
February 2005
●
PHP Architect
●
www.phparch.com
30
FEATURE
Enterprise Development with the iConnect Architecture
ever Carthag is PHP 5-native and is already used in production in many customer solutions. Aside from the core (base, bootstrap, class loaders, and class registry), Carthag also features (see Figure 3): • Language and base packages: system, IO, file, data types, exceptions, process, utilities, XML, manipulators, net, archives; • Standards: interfaces, collections, design patterns (DAO, singleton, observing, registry, command, etc.), idioms; • Integration: data access, web services; • Common application frameworks: database abstraction, validation, native language support, sessions, authentication/authorization, logging, templates; • Development tools: unit testing, autodocs, build system, CARs, deployment. As you can see, Carthag is not a Web-only/CMS-oriented platform: it is aimed at all sorts of applications. Classes are organized in hierarchical packages: each package has its own directory and class files, and each class has its own file. You would organize the code by putting classes in their package just like Java, e.g.: the org.acme.myapp.MyApplication class will reside in the org/acme/myapp directory inside the root of your application or, better, inside the classes directory, with the MyApplication.php file name (see Figure 4). As you can understand from the paragraph above, Carthag applications are entirely object-oriented. In order to import a Carthag class in the classpath, you would call:
Each class must declare its own package, so the org.acme.myapp.MyApplication class would declare the following at the top of its class file: Carthag::package(‘org.acme.myapp’);
Carthag contains the logic for bootstrapping an application (both CLI- and SAPI-based) and for loading classes inside a class path. There are various types of Carthag based applications: • CLI applications: they are launched using either the Bash carthag or MS-DOS carthag.bat launch scripts (from now on: carthag); for example: carthag application.php
or carthag org.acme.MyApp.php
• ECA (Embedded Carthag Applications) applications: they are run from the Web by direct call or Figure 4
Carthag::import(‘org.acme.myapp.MyApplication’);
Figure 3
February 2005
●
PHP Architect
●
www.phparch.com
31
FEATURE
Enterprise Development with the iConnect Architecture
by calling PHP classname.php from the CLI (they are called embedded because they embed the logic for calling and bootstrapping Carthag); • CAR (Carthag Application Archive) applications: just like Java’s JARs, CARs are archives (created using tar) launched from the CLI using this syntax: carthag -c application.car
You can find examples of simple Carthag applications in carthag code directory: • helloworld is a very simple hello world example; • helloworld2 is the same as the previous one, but uses Carthag’s pipes to print “Hello world”; • helloworld_web is an ECA application; • helloworld_car is an example of a CAR archived application; • helloworld_app shows a simple application with its own launch script, and features a class organized in a package. I also recommend that you have a look at the readme files included inside each example directory; there, you can find more information and setup instructions. iConnect Integration iConnect Integration is a collection of connectors and other vendor-specific code (some Open Source and some commercial) for accessing resources in the Enterprise Information System, such as legacy system, databases, and so on (see Figure 5). iConnect Platform and PHP already provide data access, web services and other “connectors,” such as
PHP’s Java class access, COM, and so forth; however, they are logically put here. iConnect Enterprise Application Server iConnect EAS is a system for the development and deployment of distributed, component-based business applications. EAS components (called modules) address critical business functions as objectified business logic, featuring encapsulation of business logic into business objects and an object-to-relational mapping persistence system. EAS is the standard component architecture for building distributed, object-oriented business applications in the iConnect architecture. EAS makes possible to build distributed applications by combining components developed by different vendors. An EAS module can be developed once and deployed on multiple platforms without repackaging or source code modification. Developers do not have to understand low-level details, nor do they have to implement features like persistence, distributed logic serving and other complex items. EAS modules are bundled as a .eas archive (in TAR format) that contains two directories: • classes: where all the classes are stored; • META-INF: this directory contains the eas.xml definition file, where information about the main class to call resides. Each EAS module has its own name; its format follows the same convention as Carthag’s package names, e.g.: org.acme.myapplicationeas. Before usage, EAS modules have to be deployed in the EAS server, using the easdeployer script inside EAS’ bin directory: easdeployer deploy myeas.eas
Figure 5
February 2005
●
PHP Architect
●
www.phparch.com
32
FEATURE
Enterprise Development with the iConnect Architecture
Also, the EAS server (which is itself a long-running PHP script) must be started before applications can access remote EAS modules: easserver start
An EAS module must contain at least one class file, and that class must be referenced inside the eas.xml file. This is the class that exposes the surface area of the module and that is remotely accessed by applications. This class must extend the com.solarix.eas.EASObject abstract class (or com.solarix.eas.EASPersistentObject if persistence is needed); each method that should be exposed must begin with the eas prefix, e.g.: MyEASObject::easCalculatePrice() . Once an EAS module has been written and deployed in an EAS server, you should set permissions for accessing it in the conf/users.xml EAS configuration file; you can have multiple accounts, and for each account you should define which EAS modules can be accessed. If you change this file when the EAS server is running, you should refresh its configuration: easserver refresh
Remote applications can then access remote EAS modules as if they were local classes. In order to locate a
remote module, an EAS locator format is defined; you must pass the locator to the EASFactory class when you want to instantiate a remote class. Here’s an example: $loc = ‘eas://myuser:
[email protected]:9000/org.acme.myeas’ ; try { $hw = EASFactory :: getEAS(new EASLocator($loc)); echo $hw->easHelloWorld(); } catch (Exception $e) { echo $e; }
The org.acme.myeas EAS module’s class is defined as following: class HelloWorldEAS extends EASObject { public function easHelloWorld() { return ‘Hello World!’; } public function deploy() {} public function undeploy() {} public function redeploy() {} }
Inside the iconnect-eas/HelloWorldEAS directory in code examples, you can find an example of a simple EAS module. The example doesn’t use persistence, but it shows how to build a simple EAS module just like the one above and how to use it. There are two classes inside the iconnect-eas directory:
Figure 6
February 2005
●
PHP Architect
●
www.phparch.com
33
FEATURE
Enterprise Development with the iConnect Architecture
• LocalHelloWorld.php: an example of calling an EAS module deployed in the local host without using the EAS server; • RemoteHelloWorld.php: an example of calling an EAS module deployed in a remote host (or a local one too) using the EAS server. The readme.txt file contains instructions on usage and deployment. You can find even more detailed examples (some of which also use persistence) inside the iConnect EAS distribution. iConnect Web Application Server The iConnect Web Application Server (WAS) plays more or less the role of Tomcat in the iConnect architecture. It handles the deployment and serving of web applications (called webapps) and offers something similar to servlets (called webapp handlers) and JSP (PHP itself, through a dedicated webapp handler). The iConnect WAS sits over the Apache web server and is tightly coupled with it; unlike EAS, WAS is not a background script and each time a page is called from the Web, a new instance of it is started. Each request is processed and the control is passed to the webapp handler, which, in turn, matches a pattern as defined inside the webapp’s web.xml configuration file, located inside the WEB-INF directory. A webapp may
use more than a single handler and can define multiple matching patterns. Webapps or external applications may provide new webapp handlers aside from the two handlers provided with WAS (the default one, that outputs the requested files, and the PHP one, that runs PHP scripts). Inside the iConnect WAS’ conf/webapp-skel directory, there is a skeleton that can be used to start a new webapp by just copying it to a new place. You can also find an example of the web.xml file inside it. Webapps are usually distributed as common tgz archives. The name of the directory containing the files is also the name of the webapp, and the name of the directory where the webapp resides inside the WAS’ webapps directory. There are two methods to deploy a webapp: • Through the “admin” webapp, using the “Deploy a new web application” button; • Through the “deployer” script located inside the WAS’ bin directory: deploy method webapp
There are multiple methods to deploy a webapp: from an archive, from the filesystem directly, from a web upload, from CVS. External applications can define new deployment methods. Once a webapp has been deployed, it can be called
Figure 7
February 2005
●
PHP Architect
●
www.phparch.com
34
Enterprise Development with the iConnect Architecture
by accessing its directory (or virtual host, if set in Apache configuration) from the web. The web.xml file defines what file should be used as the index document; subsequently, each request must contain the index.php receiver file in the URL. For example, if you have a file named example.php inside the mywebapp webapp, you’d call it from the browser as follows: http://localhost/was/webapp/index.php/example.php
The index.php file is reserved for WAS’s used and should be copied as it is from the webapp skeleton. It calls the real WAS receiver and instantiates the whole application server. As said above, once the index.php receiver has been called, the webapp handler that matches the request receives the com.solarix.was.webapp.WebAppRequest and com.solarix.was.webapp.WebAppResponse classes from the webapp processor. The handler is delegated to process the request (e.g.: with its own front controller) and build the response. The iConnect Portal Server explained later is an example of an external application providing a custom webapp handler with its own front controller. You can check out Figure 6 for more details about the workflow that the WAS server follows when serving a request. In particular, you can see that the business logic is separated from the presentation logic using the MVC design pattern. The logic can reside inside the webapp or inside local/remote EAS modules (especially for complex webapps). Inside the iconnect-was code directory, you can find two examples: • helloworld: a simple example of a webapp that uses the PHP webapp handler to execute PHP scripts; • helloworld2: a simple example of a webapp that executes PHP scripts using classes. iConnect Portal Server The iConnect Portal Server is a data presentation and delivery system for building enterprise portals over the iConnect Web Application Server. Users of the platform have a customized single point of access to the company’s information systems and resources. iConnect Portal Server is suitable for any type of Web application: Internet, Intranet and Extranet. The iConnect Portal Server defines a new webapp handler, as well as a set of concepts and classes for building portlet-like sites. The building blocks of a Portal Server webapp are the grid, themes, modules, slots, blocks, classes, pages and templates (see Figure 7). A module is a collection of pages, blocks and classes
February 2005
●
PHP Architect
●
www.phparch.com
FEATURE (or at least one of them), and can use other module’s pages, blocks and classes. Modules are assembled together in order to build a Web application. Modules are put inside the webapp’s WEB-INF/modules directory, with their own subdirectory called just like the module. Complete modules contain the following directories: • classes: this is where are all module’s classes are stored using the traditional Carthag format (e.g.: classes/org/acme/mymodule1/MyClass.php); this directory is automatically added to the classpath when the module is called; • pages: home of the module’s pages; • blocks: module block definition files and templates are stored here. A block is the application area (the view) for the logic (the model); blocks are independent of each other, but can cooperate and communicate. A block is composed by a definition file, a template that handles the presentation and a class that handles the model. A block’s class must extend the com.solarix.portalserver.page.PortalServerBlock class. Here is a very simple block (from helloworld/classes/helloworld/HelloWorld.php), that only defines a “message” variable:
The block definition file for such a class can be something similar (hhelloworld/blocks/helloworld.xml): helloworld.HelloWorld helloworld.tpl.php
A Portal Server webapp contains at least a grid, which provides a container of slots where blocks are placed; more grid themes can be provided. The grid contains the logic for laying the blocks out. A slot may contain no blocks, a single block or more than a block (in a precise order). Pages define how blocks are organized inside the grid using a specific layout: which blocks to use and where. Pages can be thoughts as sections with the same layout of applications in the site/portal. Depending on the 35
Enterprise Development with the iConnect Architecture
blocks, the same Portal Server page can be used to show different type of contents. Here’s an example of a page (hhelloworld/pages/index.xml) using the helloworld block: <page> <module>helloworld helloworld 2 1 <position>1
column and row refer to the position inside the grid (which slot), while position refers to the position inside the slot. The grid and the blocks use PHP-based templates: each block has its own template, which is included, once transformed, inside the grid template at the position defined by the grid’s logic. A very simple example of a Portal Server template (hhelloworld/blocks/helloworld.tpl.php) matching the previous HelloWorld class follows:
The $message variable comes from the $this>set(‘message’, ‘Hello World!’) statement in the HelloWorld class. Using PortalServerBlock::serArray(), you can also pass arrays that can be traversed using foreach in order to build loops (even nested ones) inside the template. Listing 1 1 2 3 4 5 6 7
8 9 10 11 12 | 13 14 15 16 | 17 18 19 20 | 21 22
23
24 25 26
February 2005
●
PHP Architect
●
www.phparch.com
FEATURE The grid template may look something similar to Listing 1. As you can see, you can use any PHP instruction in the template, since it is, for all intents and purposes, a PHP script. In order to call a Portal Server page, you pass the module containing the page and the name of the page itself to the receiver as follows: http://www.acme.org/index.php/helloworld/index/
To pass GET/POST parameters to modules, you write them prefixed by a string like “modulename_”. So, in order to retrieve a page from an hypothetical content management system whose module name is “content” and that uses content_page for choosing which page to display, you would call: http://www.acme.org/index.php/common/std/?content_pag e=index
Please note that in the example the word page has not the same meaning of Portal Server page. Each block called by the page is responsible for its own area, and calls its own logic; modules allow developers to easily build, maintain and upgrade sites, and also permit simple code reuse by copying (and, when needed, adapting) a module’s directory to another webapp. The typical scenario is to have a repository containing a collection of modules of any type (content management, news, blogs, menus, site mapper, frontend/back-end modules and so on), copying them inside a new webapp and then take care of the custom “collage work.” You can find a simple example, composed of a single block that prints “Hello world,” inside the iconnectportalserver code directory. As always, have a look at the readme.txt file first. A complete iConnect example So, how do all these components fit together? Let’s suppose that we want to display a simple message (the usual “Hello world”) in our fresh new portal, which must be structured in a modular way that can be extended later, and that the message must be received from a remote application. Using the iConnect architecture, the application would be composed of an EAS module, called HelloWorldEAS and a Portal Server-based webapp called helloworldapp. The tools we need are: • Carthag as platform; • The iConnect Enterprise Application Server for the “hello world” business logic; • The iConnect Web Application Server for deploying and serving the site;
36
FEATURE
Enterprise Development with the iConnect Architecture
• The iConnect Portal Server for structuring the site. The core of the EAS module (the helloworldeas.HelloWorldEAS class) is shown in the Listing 2, while the eas.xml file follows here: <easconfig> helloworldeas 1.0 helloworldeas.HelloWorldEAS
Once developed and built, the EAS module can be deployed in the EAS server. It’s now time to take care of the webapp. It would be composed of an helloworld module containing the helloworld.HelloWorld class, the helloworld/hel loworld block and the home/index page. First of all, however, we want to use the Portal Server handler, so the webapp’s web.xml file will be as shown in Listing 3. The code for the HelloWorld class is shown in Listing 4. In the class, you can see the call to the remote EAS module. The template stored in helloworld/blocks/hel loworld.tpl.php is simple:
The class and the template are coupled by the helloworld/blocks/helloworld.xml file: helloworld.HelloWorld helloworld.tpl.php
The last thing to add to the webapp is the page definition (hhome/pages/index.xml): <page> <module>helloworld helloworld 2 1 <position>1
Once the webapp has been deployed, you can access the helloworldapp webapp and you should see the “Hello World!” message printed. You can find the code for the complete example inside the complete_appc code directory.
Listing 4 Listing 2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
1
Listing 3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
<webapp> Hello World with iConnect <description>Hello World example showing full usage of iConnect default com.solarix.portalserver.handler.PortalServerWebAppHandler default /*
February 2005
●
PHP Architect
●
www.phparch.com
37
FEATURE
Enterprise Development with the iConnect Architecture
Conclusion There is a lot of talk about PHP in the enterprise—there are countless “PHP is not Java” or “What is missing in PHP to make it better than Java” threads, and thousands of blog entries have shown up all over the Internet about such topics, especially in the last few months. I hope that iConnect can—at least in part—address that sort of questions and discussions, and bring even more PHP to the enterprise. Even if your webapp is only made up of a collection of simple PHP scripts, you may find iConnect useful for:
• The presence of a defined, flexible methodology for building simple to enterprise modular, distributed applications; • Developing with an architecture based on best practices and design patterns in mind.
• The deployment in different stages/environments (development, integration, system tests, production); • The opportunity to organize your code and easily maintain it; • The benefit of having a common architecture and a set of frameworks under the application, without reinventing the wheel each time (this is also true for different projects from different authors: the result would be better integration— just think about phpBB);
About the Author
?>
Alex Pagnoni is Chairman and CEO of Solarix (http://www.solarix.it), a company that delivers PHP-based solutions over the iConnect architecture. He believes in Open Source and you can contact him at
[email protected] .
To Discuss this article:
http://forums.phparch.com/201
Available Right At Your Desk All our classes take place entirely through the Internet and feature a real, live instructor that interacts with each student through voice or real-time messaging.
What You Get Your Own Web Sandbox Our No-hassle Refund Policy Smaller Classes = Better Learning
Curriculum The training program closely follows the certification guide— as it was built by some of its very same authors.
Sign-up and Save! For a limited time, you can get over $300 US in savings just by signing up for our training program! New classes start every three weeks!
http://www.phparch.com/cert
February 2005
●
PHP Architect
●
www.phparch.com
38
FEATURE
The PRADO Framework
F E A T U R E
by Qiang Xue
Introduction Code-reusability and logic/content separation are two “forever” topics in Web programming. Using template engines and model-view-controller (MVC) frameworks, logic and content can be well separated. However, reusing code, especially for those dealing with user interfaces (UI), seems more than a headache to even very experienced developers. The reason is that there lacks a paradigm that enables handling UI-related Web elements in an object-oriented (OO) fashion. For example, to retrieve the user input of a text field from a Web form, on the server side developers usually access the user input from the $_GET or $_POST arrays using an index that corresponds to the name of the text field on the client side; to determine whether a submit button is clicked or not, developers would check the presence of the corresponding array index in $_GET or $_POST. Reusing such kind of code often requires cutting and pasting in several places, which is very error-prone and depreciates the benefits brought on by the separation of logic and content. Based on this observation, we developed the PRADO framework as a means to solve the problem of repetitive and low-level coding practices in PHP Web application development. The name of PRADO stands for PHP Rapid Application Development Object-oriented. PRADO is a component-based and event-driven framework for Web programming in PHP 5. A component is a software unit that is self-contained and can be
February 2005
●
PHP Architect
●
www.phparch.com
reused with trivial customization. PRADO stipulates a protocol for writing components and implements a set of basic components that represent commonly used Web elements, such as input field, checkbox, dropdown list, and so on. New components can be developed by either inheriting or composing from existing ones. PRADO also implements an event-driven programming scheme that allows delegation of extensible behaviour to components. With PRADO, developers can get the user input of a text field using the expression $textbox->Text, where $textbox refers to the component representing the text field, and Text is a property of the component where the user input data is stored; to respond to a submit button click, developers would write a handler function for the OnClick event of the button component. The handler function will be invoked automatically by the framework when the button is clicked. In summary, developing Web applications with PRADO mainly involves instantiating prebuilt and application-specific component types, configuring them by setting their properties, responding to their
REQUIREMENTS PHP
5.x
OS
Linux/Unix, Windows
Other Software
Apache Httpd, Microsoft IIS
Code Directory
prado
+ SimpleXML extension
40
FEATURE
The PRADO Framework
events by writing handler functions, and composing them into application tasks. Requirements and Installation PRADO is an open-source project that adopts the BSD (revised) license. PRADO requires PHP 5 with the SimpleXML extension. It has been tested to work with Apache HTTP server and Microsoft IIS on Windows XP and Linux. To install PRADO, unpack the PRADO release file to a Web-accessible directory. PRADO comes with complete API documentation and several tutorials and examples. In the following paragraphs, I’ll first introduce several basic concepts of PRADO, and then describe in detail a form validation example to illustrate its usage. After that, I’ll show you how to write reusable components using inheritance and composition approaches. A summary is given at the end.
controller logic. PRADO implements the page controller design pattern in addition to a weak front controller (called application). Each page request is received by the front controller which dispatches the request to an appropriate page controller. The page controller extracts the input data from the request, invokes the requested actions on the model, and determines the correct view to present. A module is a component that groups together a collection of pages serving for a common goal. It is the central repository of logic and data among the pages in the group. Components are related to each other via parentchild relationships. Each component has at most one parent and has an ID that uniquely identifies it among its sibling components. A page is the top-most component without a parent. A component can thus be considered as a tree rooted at the page with the tree nodes
“ PRADO is a component-based and event-driven framework for Web programming in PHP 5. ” Basic Concepts A component is an object defined with a set of properties through which it communicates with other components. Component properties are defined in component specification files. They are like member variables of objects, but are accessed via getter and setter methods. For example, reading the Text property of the TTextBox component will cause the invocation of the getText() method, and writing the property the setText() method. Events are special properties that take method names as their values. Attaching (setting) a method to an event will hook up the method to the places at which the event is raised. For example, the OnClick event of the TButton component is raised when the framework detects the corresponding submit button is clicked (by checking the input data). The method attached to this event will then be invoked automatically by the framework upon the raising of the event. The event-driven scheme allows the user of a component to modify its behavior in a way that may not be foreseen during the development of the component. A derived component class inherits all its ancestor classes’ properties. A control is a component defined in addition a with user interface. Simple controls, such as those representing commonly used Web elements, often specify their UI in code. Controls whose UI is composed from static text and the UI of other controls may use templates to specify their UI layout. A page is a control that implements the basic page
February 2005
●
PHP Architect
●
www.phparch.com
being its descendent components. The parent-child relationship is very important because it enables unique addressing of any component within a component tree. For example, assume the HomePage component is the parent of the Menu component, which, in turn, is the parent of the MenuItem component. Then MenuItem can be uniquely addressed by the ID sequence (HomePage, Menu, MenuItem). In PHP code, this can be $homePage->Menu->MenuItem , written as where $homePage refers to the HomePage component. ID sequences can be used together with property names to address any component property within a component tree. In the above example, the PHP expression $homePage->Menu->MenuItem->Caption would refer to the Caption property of MenuItem. A “postback” is a form submission that is posted to the same page containing the form. If we view a page as a window, a postback would resemble a user action that causes the window to respond to the action. The process of user interaction with a Web application can thus be abstracted as a sequence of postbacks followed by a navigational transition to a different page, then another sequence of postbacks and a navigational transition, and so on. Each postback will cause some update of the page state. “Viewstate” refers to the page state persistent during a sequence of postbacks. Viewstate enables the incremental and interactive update of a page by an enduser. For example, assume a user wants to update his account information on a profile page. He could be so
41
FEATURE
The PRADO Framework
cautious that he wants to make a save each time he updates an entry. Since the HTTP protocol is stateless, the data he entered previously would be lost unless on the server side the data was kept in some persistent medium. Viewstate is just such as a mechanism: it allows the saving of the data during the period of continuous postbacks to a single page. The concept of viewstate differs from that of session in that session data are generally persistent among requests to several different pages, while viewstate data is only related to a single page. A Form Validation Example We’ll now tackle a form validation example to illustrate the usage of PRADO. Validating user input data, known as form validation, is a common task in most Web applications. The process can occur at two places: on the client side with JavaScript and on the server side with server scripting language like PHP. Server-side validation is a must, while client-side validation should be optional. In the case of browsers without JavaScript,
Listing 1 1
February 2005
●
PHP Architect
●
www.phparch.com
only server-side validation is viable. PRADO provides a set of validation components that allow both clientand server-side validation. Using these components, most validation tasks can be accomplished without a single line of code. We will create a registration page to demonstrate how to perform form validation in a component-based and event-driven fashion. The page mainly presents a user registration form (see Figure 1), takes the data supplied by the user, validates them and creates an account if the validation is successful. We will validate the user input against the following rules before creating the new account: • The username must be unique, not matching any existing ones. It must be at least three characters long and contain only word characters. • The email address must be well-formed. • Both entries of the password must be exactly the same. The password must be at least six characters long and contain only word characters. • All fields are required. We will organize our directories and files as shown in Figure 2. The framework directory contains the PRADO framework source code. The pages directory contains the code specific for the example. Both directories should be configured as inaccessible to end-users, since they might contain sensitive data. The index.php file is the main entry script that creates an application instance and invokes its run() method:
The application instance is initialized according to the Figure 1
42
FEATURE
The PRADO Framework
application specification file pages/application.spec, which is in XML format:
tion of a text input field. • TButton: encapsulates the logic and presentation of a submit button. • TRequiredFieldValidator: makes sure that the associated component gets input. • TRegularExpressionValidator: makes sure that
<using namespace=”System.Web.UI.WebControls” /> <using namespace=”Pages” />
Listing 2 The application specification of the example mainly describes what namespaces, namely PHP include-paths, should be used when the application runs. Every PRADO application should have an entry script as well as an application specification similar to what is described above. The logic and content of the register page are given in the page class file pages/RegisterPage.php (see Listing 1) and the page template file pages/RegisterPage.tpl (see Listing 2) respectively. The template format is very similar to HTML, except that PRADO introduces component tags (in the format ) to describe the component presentation within the page layout. Components can be configured in the template by setting their properties and events, just like setting attributes of an HTML element. For example, the following line declares a TButton component that displays a button with the caption Register:
The OnClick property is an event that is attached to the registerUser method. When the button is clicked and the form is posted back, the method will be invoked automatically by the framework. The following component types are used in the template: • TForm: encapsulates the logic and presentation of a form tag. • TTextBox: encapsulates the logic and presenta-
Figure 2
February 2005
●
PHP Architect
●
www.phparch.com
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
User Registration <style type=”text/css”> User Registration Username:
Please choose a username.
Your username must have at least 3 characters and contain only alphanumeric and underline characters.
Sorry, the username has been chosen by someone else. Please try a different username.
Password:
Please enter a password.
Your password must have at least 6 characters and and contain only alphanumeric and underline characters.
Re-type Password:
Please re-enter your password.
Your password entries did not match.
Email Address:
Please enter your email address.
Please enter a valid email address.
43
FEATURE
The PRADO Framework
the associated component gets input that matches the specified regular expression. • TCompareValidator: makes sure the associated component gets input that matches the input of another specified component. • TEmailAddressValidator: makes sure that the associated component gets a valid email address input. • TCustomValidator: invokes a custom function to determine whether the input of the associated component is valid. An input component may be associated with one or several validator components, each validating against a single rule. In case a validation rule breaks, the corresponding validator will show its error message or body (content enclosed between a pair of open and close component tag). Validation failure will also prevent the form submission if client-side JavaScript is enabled. The TCustomValidator component is different from the rest of the validators because it does not explicitly specify a validation rule. Instead, it invokes the method attached to its OnServerValidate event to perform the actual validation. In the example, the checkUsername() method is used for this purpose. Figure 3 shows the sequence diagram of the register page after the submit button is clicked. Upon the button click, on the client side, the browser will run Figure 3
JavaScript validators for each input field if JavaScript is enabled. In case a validation fails, the corresponding error message will be displayed immediately without further communication with the Web server (see Figure 4). If the client-side validation succeeds, the form data will be submitted and a postback to the RegisterPage occurs. The front controller of the framework receives the postback request and creates the RegisterPage page (controller) to handle it. The RegisterPage will go through a postback lifecycle. In particular, it will first restore the viewstate data of the page followed by loading post data into various input components (e.g. TTextBox). The page then conducts server-side form validation by invoking in turn each validator component’s validate() method. In the case of the TCustomValidator, an OnServerValidate event will be raised which will cause the invocation of its attached event handler checkUsername(). Should any of these validations fails, the IsValid property of the page will be set to False. After the validation process, the page will determine which component is responsible for the postback and raise an appropriate event. In the example, the TButton is responsible and its OnClick event will be raised; this, in turn, will cause the invocation of the registerUser() event handler. At the end, the page will render its body content recursively. The rendering result is returned to the browser for presentation. Form validation is often considered a tedious yet important task. In this example, we have shown a component-based and event-driven approach to accomplishing it. The approach is intuitive enough to be mastered by even new developers because it mainly involves the configuration of components in a template. The approach is also very flexible: complex validation rules can be decomposed into atomic ones that can be handled by existing validator components. The TCustomValidator component enables validating against arbitrary complex rules. Writing Reusable Components Writing a component should never be a daunting task. In the form validation example, I already showed you how to write a page, which, by definition, is a component. In this section, we will focus on writing non-page components that may ultimately become part of your component repository to facilitate future Web development.
February 2005
●
PHP Architect
●
www.phparch.com
44
FEATURE
The PRADO Framework
There are two approaches for writing new components: by inheritance and by composition. Inheritance is the ability to reuse and override methods and properties belonging to a parent class. It lets us customize the parent class by implementing something new or different. Composition, on the other hand, allows us to create a new component by grouping existing ones together. Composite components can simplify complex component groupings and manage the inter-subcomponent relationships privately. In the following, I will show you an example for each approach. We will first use the inheritance approach to create a TLabeledTextBox component. The TLabeledTextBox component represents a text input field and displays a label besides it. It inherits from the TTextBox component for its functionality of representing a text input field. Such a component may be used in the form validation example to simplify lines in the page template like Username:
as
We need to create a component class file called TLabeledTextBox.php (see Listing 3), as well as the component specification file called TLabeledTextBox.spec Listing 3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
shown below: <property name=”Label” type=”string” get=”getLabel” set=”setLabel” />
The component specification file is in XML format and defines the properties of a new component. The TLabeledTextBox component introduces a new property named Label, which represents the label to be displayed besides the input field. The property is of string type and is defined with a getter method (ggetLabel()) and a setter method (ssetLabel()), both of which can be found in the component class file. We store the value of the Label property in viewstate meaning that it should be persistent across a sequence of postbacks. To use the new component, the relevant files must be locatable by the framework. For the form validation example, we may simply save the component class and specification files under the pages directory, because pages is added into the PHP include-path according to the application specification. Next, we show how to create a SearchBox component (see Figure 5) using the composition approach. A common task in many content management systems (CMS) is to display customizable blocks on pages. Each block represents some relatively independent content,
Figure 4
February 2005
●
PHP Architect
●
www.phparch.com
45
FEATURE
The PRADO Framework
such as search box, weather or stock information, polls, and so on. Using PRADO, the task becomes very easy because each block can be represented by a component that is, in turn, composed of other components. Our SearchBox component will be made up of a TLabeledTextBox component and a TButton component. The TLabeledTextBox component is what we just developed using the inheritance approach. Here we will use it to represent the input field for a keyword search. The TButton component is used to represent the search button. We will define for the new component a Keyword property that represents the keyword entered by end-users. We will also define an OnSearch event such that the attached event handler can perform actual searching logic when the search button is clicked. Since the SearchBox is a component with UI, we will create it by inheriting from the TControl class, which is the base class for all controls. We need to create a component class file called SearchBox.php (see Listing 4), a component specification file called SearchBox.spec and a component template file called SearchBox.tpl. The content of the latter two files are shown as follows: <property name=”Keyword” type=”string” get=”getKeyword” set=”setKeyword” /> <event name=”OnSearch” />
And
In the specification file, we define the Keyword property, which is of string type and has a getter method called getKeyword() and a setter method called setKeyword(), both of which can be found in the component class file. We also define the OnSearch event as described above. The component template specifies how the SearchBox component is composed of prebuilt components. The TPanel component represents an HTML DIV tag, which serves as the container of the TLabeledTextBox and TButton components. The OnClick event of the search button is attached to the searchBtnClicked() method of the SearchBox class. The method mainly invokes onSearch(), which, in turn, raises the OnSearch event of the SearchBox component. The reason why we implement a seemingly redundant onSearch() method is that we want to give sub-classes of SearchBox a chance to override the onSearch()
February 2005
●
PHP Architect
●
www.phparch.com
method so that they can implement specific search functionality that we cannot anticipate at this time. It is very easy to use the SearchBox component. For example, if we want to add a search box to the register page that we created previously, we only need to update the page template file by inserting the following line at an appropriate place:
Here, the OnSearch event is attached to the doSearch() method that should be implemented in the page class and perform specific searching logic. Summary I have shown you how PRADO components can be used to handle individual tasks like form validation in an intuitive yet object-oriented way. We have also seen that developing a new reusable component is no more complicated than writing a new class. PRADO is designed to fit the needs of developing large and complex Web applications. Besides the basic
Listing 4 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
46
FEATURE
The PRADO Framework
control library for common Web elements, it also provides features like module-based page organization, authentication and authorization framework, internationalization (I18N) and localization (L10N) support, and many others. Some of these features are provided in terms of components, while some are using the handler design pattern which allows very flexible customization. Community participation and contribution play an extremely important role in the development and usage of PRADO. Many developers have adopted PRADO as their programming tool—and they have been actively participating in the PRADO project website at http://www.xisc.com. The PRADO component library is growing fast as many developers are contributing their components to the project.
model of PHP 4 made it very prone to errors in practice. So, driven by the Zend PHP 5 coding contest, I spent about half a month rewriting PRADO in PHP 5. The result turned out to be very satisfactory, thanks to the new object model provided by PHP 5: I did not have to deal with messy references to objects anymore and I could use a lot of object-oriented programming elements.
A Brief History of PRADO The very original inspiration of PRADO came from Apache Tapestry. During the design and implementation, I borrowed many ideas from Borland Delphi and Microsoft ASP.NET. In fact, PRADO can be thought of as a PHP implementation of ASP.NET (not literally and thoroughly, though). The first version of PRADO came out in June 2004 and was written in PHP 4. Although conceptually PRADO was fledged, the weak object
About the Author
February 2005
●
PHP Architect
●
www.phparch.com
Acknowledgement Special thanks to Xiang Wei Zhuo for providing the sequence diagram. And thanks to all PRADO users for their contribution to the project!
?>
Qiang Xue (
[email protected]) is in his final year of PhD study in computer science at Duke University, U.S.A. He got hooked on the Web in the early days back in 1996 and has been using PHP since 2000. His research work mainly involves designing efficient algorithms to solve large-scale computational problems.
To Discuss this article:
http://forums.phparch.com/202
47
TEST PATTERN
T E S T
P A T T E R N
The Myth of Reusable by Marcus Baker
“Is our code reusable?” Achieving “reusable” is often viewed as a holy grail for a piece of object-oriented code. The code is going to be so flexible that you can do anything with it that you would ever want to do. Well, I bet that somebody can think of a use we haven’t thought of yet, so the answer is “no.” Perhaps we should rephrase the question...
“
Is my code reusable?” “Well, I was hoping to use it to calculate the past trajectory of Mercury during the time of Confucius. You mean your class doesn’t do that? Next...” This has to be one of the most common object-oriented questions on the forums (maybe except for the Confucius part), and yet it’s completely the wrong question, as there is no helpful answer. If you point out something that the code doesn’t do, then either it’s not in the problem domain that the class was attempting to resolve, which is fair enough, or the developer adds that feature. The problem with the author trying to define the problem domain is that it is really, really hard. Even if you do define it, chances are no one else’s problem exactly fits it. Every developer has their own clients, each with their own peculiar problems and your domain is certainly not my domain. “Reusable” equates to “useful to everybody,” an impossible goal. “Next...” How about making code more useful by writing that extra feature? Well adding features won’t gain ground either, as that will lead to bloat. Also a big feature list doesn’t correlate to flexibility. For example, here is an imaginary template engine: $template = new Template(‘HTML’); $template->setTitle(‘Hello’); $template->paint();
February 2005
●
PHP Architect
●
www.phparch.com
Our imaginary template engine could output in HTML, XHTML, PDF, Flash, RTF, SWG and MarcusML just by setting a flag. I made up that last one, but who cares? We are getting diminishing returns on our investment pretty fast. Only one actual option has been added, and that’s to change the output format. Adding more formats won’t help the users if all they ever want is HTML. For them, our template engine is all too big and complicated. “Next...” Actually, that last attempt gets us a little closer to what gets classes reused. Let’s replace that flag and instead allow the developer to create the object that will paint the display: $template = new Template(new HtmlWriter()); $template->setTitle(‘Hello’); $template->paint();
REQUIREMENTS PHP
4.x +
OS
Any
Other Software
None
Code Directory
reuse
49
TEST PATTERN
The Myth of Reusable
If we write only the HTML output, but allow other developers to add their own extensions in the form of their own objects, then we have still given them the same options. A code change that is accessible in the interface is called a “flex point.” We have saved time by implementing fewer features, but we have given our users a new capability. They can even use this to create display options we hadn’t even thought of—WmlWriter anyone? They have to do much more work in this case, but at least we have solved half their problem. Listing 1 1
Listing 2 1
February 2005
●
PHP Architect
●
www.phparch.com
So should we be adding flex points? Well, flex points certainly bring something to the table that features alone do not. Let’s look at them. Nil Flex Points Let’s suppose we want to cache a news site to save on database load. Here is our homepage index.php:
The script queries our cache for the page that is identified by the query string entry. If it’s not cached, then we use PHP’s output buffering to capture the content from the news.php page and place it in the cache. Finally, we display the page from the cache. The error handling is not exactly robust, but you get the idea. Now, a cache is a good example of something that needs a lot of tuning for its environment and, therefore, we will want to change the caching rules often. This makes our first attempt at a cache rather unsatisfactory (see Listing 1). The first thing that could be improved is the hard-coded age that determines if the page should be reloaded (at line 22). That’s a minor flex point, but it’s worth making it available. I also want a more drastic example to illustrate the flex point concept, though. Let’s say that our news server, being heavily loaded, tends to throw up errors occasionally. Now, if we cache a screwed up page, we will be stuck with that error for a whole hour, but if we don’t cache it, hopefully it will be picked up on the next request. We would like to be able to choose not to cache a page depending on its content. Unfortunately, we don’t actually know the exact rules for this right now and caches always need a bit of experimentation to get things right. While we do that, we want to be able to swap this feature in and out easily. So how do we implement it? The TemplateMethod Pattern The simplest way to add a flex point is with inheritance. All we need to do is to move the code that we want to change into its own methods so that we can knock them out with a subclass. This shuffling, called “refactoring,” results in Listing 2. For brevity, I am only showing methods that have been modified. The hard-coded time comparison has been moved into its own method
50
TEST PATTERN
The Myth of Reusable
called _hasAged() and a new method, _isCacheable(), has been added as a hook for our error check. These methods are available for subclassing, but are not part of the public interface for client code. For this reason, we use underscores in the names to signal this status. In PHP 5, we would mark these methods as protected. The rest of the class acts as a kind of template which is to be filled out with the subclass. A subclass that checks the content for obvious errors could look something like Listing 3—short and sweet, although we only Listing 3 1
Listing 4 1
February 2005
●
PHP Architect
●
www.phparch.com
made use of one of the flex points. This method is known as “programming by differences.” So, to add flex points we just move the code we want to change into its own protected method, right? In the Eighties, which were pretty much the genesis of object oriented programming on a commercial scale, that was the belief. Unfortunately, reuse by inheritance turns out to be a double-edged sword. For a start, it breaks encapsulation to some degree. It would be very easy to accidentally change a vital variable if you were not familiar with the class. You could also override some methods that you shouldn’t. The final keyword in PHP 5 would stop this, but final is usually too brutal, as it completely prevents extension of a method in ways we just don’t happen to have thought of. These are minor problems, though, that can be solved with good tutorials. A more serious problem is that the flex points are intimately connected. If we want to change the ageing policy, we have to subclass. The trouble is that we have already subclassed. To get all the combinations of behaviour, we would have to subclass both of our two classes so far. If we had three variations on each flex point, we would have to create nine classes with a lot of cut and paste. This is known as a class explosion and is the reason inheritance is known as “a card you can only play once” (Martin Fowler). Inheritance is great for a single flex point, but can lead to a mess when tackling serious complexity. Fortunately, we have a much more powerful reuse mechanism at our disposal—polymorphism. The Strategy Pattern Of course, the simplest way to handle our caching age limit would be to pass it into the constructor. Can we pass the decision to cache the content in the constructor as well? The answer is yes we can and Listing 4 is a demonstration of this. Instead of passing in an integer value, we pass in behaviour in the form of a class. Applying this technique is similar to TemplateMethod, except that here we pull the code that we want to vary off into a separate class. For the original behaviour, I have called the strategy class, the one passed back in, AlwaysCacheForOneHour. The changes look extensive, but actually it is just some code being moved around. However, our original client code now has more work to do. Where we had: $cache = new Cache(‘cache/’);
we now have: $cache = new Cache(‘cache/’, new AlwaysCacheForOneHour());
The extra code can be useful here, though. It makes the cache behaviour very explicit to someone casually read-
51
TEST PATTERN
The Myth of Reusable
ing the code. To change the behaviour, we pass in a different class. Listing 5 is the code that matches the change in Listing 3 for the TemplateMethod example by adding an error check. We are reusing the Cache class and only writing the code that is different, thus we have all the advantages of programming by difference. We can do this because the strategy classes are polymorphic, that is they all have identical interfaces. As before, no change is needed to the Cache class for different caching policies. We call this the “open/closed principle.” A class should be closed for modification, but open for extension. In other words, we want to extend the behaviour by writing new code, not changing the one we have already written. Polymorphism has a lot of advantages over inheritance here. When we write a new strategy, we need to know absolutely nothing about the original Cache class and so we have provided complete encapsulation. We can manage the class explosion problem by passing in multiple strategies. One slight irritation with the strategy is that our original client code had to be modified. It started out very simple, but has had to take on the burden of an extra class even if it didn’t want to make use of it. With our particular problem, we know we need the flex points so Listing 5
“we have a much more powerful reuse mechanism at our disposal— polymorphism.”
The Decorator Pattern Up until now, we have been refactoring our class to add flex points. What if we don’t have control of the original class because the code is in an external library? Can we add flex points to code we cannot even modify? Believe it or not, we can. Listing 6 demonstrates a technique called a Decorator. We apply a Decorator by having it wrap the original object. This means we create our page cache with the line: $cache = new WithErrorRejection(new Cache(‘cache/’));
1
Listing 6 1
February 2005
we know we need that code, but if you only occasionally need the flex point then the forced extra complexity is a consideration. Despite this, the Strategy pattern is the workhorse of object-oriented design and almost always superior to TemplateMethod for all but the simplest of cases.
●
PHP Architect
●
www.phparch.com
The client code, our top level script, is none the wiser that the decorator has been applied. The reason is that the WithErrorRejection decorator has exactly the same interface as the original Cache class. When a method is called on the decorator, it mostly calls the same method on the cache and returns its result. Looking at the code you see a lot of method chaining. This gives us only two intercept points, the beginning of a method on the way in and the end of the method on the way out. In our listing, we use it to add the pattern match for the content errors before deciding to chain the write() method. There are limits to what a Decorator can do. Because the decorator can only add code, but not remove it, we cannot use a decorator to change our cache age limit. That flex point is beyond our reach without modifying the original class. The Decorator pattern is a powerful and flexible idea. The client code only needs to add a decorator if it needs it. It can also add multiple decorators to the same object all at runtime, solving the class explosion problem without adding parameters to the code. The downside is the burden of assembly it places on the client code if there are a lot of decisions to be made. Sometimes, Decorators can spread the decision making process a little too wide, forcing you to hunt down the important code.
52
TEST PATTERN
The Myth of Reusable
So, More Flex Points Then? No. Flex points open options that would otherwise disallow use of your code, but they don’t directly contribute to the solution. The most extreme case is a library with all flex points and no features. The user has to implement everything themselves and is additionally constrained by your framework. This is completely useless for any short term need. Flex points leverage existing features, but should still be added with caution. Every time we add a flex point, rather than implementing a feature, we place four burdens on our users. The first is that they must actually fill in the coding gaps to make use of our flex point. Flex points are more work for them than pre-built features. This leads to the second problem. You’ll have to explain to your user how to use the flex point via the documentation. The third burden is the higher level of developer skill needed to make use of these design patterns, because if you haven’t seen them before, they won’t be obvious. The final problem is that all of the code gets more complicated to write and use. If your library is more complicated than writing simple PHP code, developers will simply write their own PHP code. Libraries should make a task easier, not harder, and a library that is too difficult to use will never be used. The more features and flex points you add, the more the bloat and the harder your code is to use. Paradoxically, choosing not to implement something is often the most useful thing you can do. Your final blend of features, flex points and simplicity will dictate your target audience. Just as there is no one
Have you had your PHP today?
true developer, there is no one perfect library. There is certainly no pinnacle of perfection that could be described as “reusable.” The pinnacle is a finely crafted tool that will be useful to some of the people some of the time. If you manage that then perhaps, just perhaps, somebody will reuse it.
About the Author
?>
Marcus Baker works at Wordtracker (www.wordtracker.com) as Head of Technical, where his responsibilities include the development of applications for mining Internet search engine data. His previous work includes telephony and robotics. Marcus is the lead developer of the SimpleTest project, which is available on Sourceforge. He's also a big fan of eXtreme programming, which he has been practising for about two years.
To Discuss this article:
http://forums.phparch.com/203
http://www.phparch.com
NEW !
ce Lower Pri NEW COMBO NOW AVAILABLE: PDF + PRINT
The Magazine For PHP Professionals
February 2005
●
PHP Architect
●
www.phparch.com
53
Any more, and we’d have to take the exam for you! We’re proud to announce the publication of The Zend PHP Certification Practice Test Book, a new manual designed specifically to help candidates who are preparing for the Zend Certification Exam. Available in both PDF and Print
Written and edited by four members of the Zend Education Board, the same body that prepared the exam itself, and officially sanctioned by Zend Technologies, this book contains 200 questions that cover every topic in the exam. Each question comes with a detailed answer that not only provides the best choice, but also explains the relevant theory and the reason why a question is structured in a particular way. The Zend PHP Certification Practice Test Book is available now directly from php|architect, from most online retailers (such as Amazon.com and BarnesandNoble.com) and at bookstores throughout the world.
Get your copy today at http://www.phparch.com/cert/mock_testing.php
P R O D U C T
R E V I E W
REVIEW
The ZEND PHP Certification Experience by Peter B. MacIntyre
anuary 2005—the beginning of a new year, the time to stretch forth into new adventures and to explore new frontiers. Well maybe it’s not that much of a “Star Trek” kind of thing, but (among other things) that date marks the granting of the first Zend Certified Engineer designation to someone in Atlantic Canada (me!). Yes, I am both proud and excited to be the first person in my region of the country to earn this Zend designation. Incidentally, I am also the first person on staff at php|architect to be certified. But of course, that can only be said with the following caveat: Marco, our publisher and magazine founder, is on the Zend Certification Advisory Board and has helped to craft the PHP exam itself. So he is one of the “grandfathers” (can I say that and still have this article published?) of the certification process at Zend. So, what is my point in this arti-
J
February 2005
●
PHP Architect
●
cle, you may be asking out loud? Well, apart from blowing my own horn, I wanted to share my journey of achieving my certification with the readers of php|architect. If you are considering taking the exam yourself, then please read on. Preparation In my opinion, there are two aspects of getting prepared for this exam. The first aspect is one that you gain over time and PHP product use; you can’t buy it—and it’s called experience. You should have at least 2 to 3 years of good solid experience in developing mid to large-scale PHP web applications (or you should be as cleaver as Stephen Hawking!), Now, even this much experience does not mean that you will have covered all aspects of the PHP language, but that much real-world experience should provide you a good foundation to build upon, which leads me
www.phparch.com
to the second aspect of preparation: study. There is no substitute for studying for this exam. There are over 3,500 built-in functions in PHP, and that is only the surface material. There are object-oriented portions to PHP; there are regular expressions; there is e-mail; there is file management; there are basic web client/server concepts; arrays; sessions vs. cookies; date and time management issues; database connectivity; debugging and coding efficiencies; web security concerns; super global arrays; php.ini settings; and web server configurations. All the items I just mentioned are considered as fair territory on which to base the exam questions. Now, I am not listing all these categories to frighten you out of the hunt for your own certification, but it is nice to know just how broad the range is that the exam could possibly cover.
55
PRODUCT REVIEW
The ZEND PHP Certification Experience
Support Materials There are 3 major additional items that I used to help me prepare for this exam and they are all books. The first one is my PHP go-to reference book called Programming PHP (ISBN: 1-56592-610-2) published by O’Reilly Press and co-authored by Rasmus Lerdorf, the original
uled date for the test was December 29th (let me tell you, the period leading up to Christmas is not the best time to be studying for an exam, as stress levels were high to begin with). Because of weather, I was forced to postpone my exam for 2 weeks until January 5th. Having those two extra weeks and
will be forced to operate. The Test The day of the test arrived and I was fairly nervous. The test centre was about a 40-minute drive from my house, and the exam was set for 10:30 AM (Zend has a link on their certification web site to help you
“Being a Zend Certified Engineer is one of the highlights of my 16-year computing career” mind behind PHP. The other two are the two study helps that exist specifically for aiding in this exam: The Zend PHP Certification Study Guide (ISBN: 0-672-32709-0, published by SAMS) and The Zend PHP Certification Practice Test Book (ISBN 0-9735898-8-4, published by php|architect). All three books were extremely valuable to me. Their covers are shown in the accompanying Figures 1 to 3. Study Time Now, the other angle to the studying aspect is… the actual studying. I started studying for the exam about one month before I was scheduled to take it. My first sched-
Figure 2
Figure 1
February 2005
some serious quiet time was a great help. Make sure you have some time to yourself to get your head really into the areas of PHP that you are not familiar with. I had about 25 pages of study sheets that I had written out covering the material that I knew would be well represented in the exam (based on the information given to me in the study guides). If you do get the book of sample questions, be sure to follow the advice of the authors and take the exam in a mock fashion a few times. Time yourself and do about 5 or 6 questions from each section. This gives you a great cross-section of the test and simulates the time frame in which you
●
PHP Architect
●
www.phparch.com
locate a testing centre that is close to you). I left home at 8:30 AM, stopped for a coffee, and arrived in plenty of time to relax and do some last minute reviewing of my materials before I started the test. You are expected to give the exam provider some personal information (name, address, and so on) and then you are taken to the exam room, where you are to surrender all your study materials, jackets, backpacks, cheat sheets, weapons, body armour, and so on. Before you start the actual test, you are given a quick (5 minutes) computerized survey that asks you questions like how long you have been using PHP and what areas of
Figure 3
56
REVIEW
The ZEND PHP Certification Experience
the product you are most comfortable with. Once this is over, you are asked if you are ready to start taking the exam. The test is performed over a time frame of 85 minutes during which you have to answer 70 random PHP-related questions during that time. This was not a tough situation as far as the time frame went, as I finished with my first pass with about 18 minutes to spare. Did I walk away when I was done? Absolutely not. Take the full amount of time that is given to you. When you are going through the exam for the first time, there will be a way in which you can mark questions for later review. This is a great way to re-visit the questions that you are unsure of and spend the first pass on the exam answering the simpler questions you feel comfortable with. At the end of the last question, the exam software will then show you a list of all your questions and ask you if you want to re-visit the ones that you had marked for later review. This is where you have the time to really dig into the more difficult or tricky questions (like the ones on complex arrays). Once you are done or the exam time has expired, you are asked to click the final submission button to see how you faired. My hand was actually shaking over the mouse button, but I closed my eyes and stroked the rodent. To my pleasant surprise, I was told that I had passed the exam! But that is all that I was
told. There is no summary of what questions you got right or wrong and there is no summary of what percentage mark you achieved either. You either pass or fail (did I mention that I passed?). The PC then sends a little summary of your result to the printer, showing you your Zend Certification Registration number, and that is it. It was a little anti-climactic for me just to have a one-page summary to go home with. I was half expecting a little more fanfare, or flashing screen images of running elephants. I am told that I will eventually get a signed certificate from Zend itself for which I will have to give a suitable space on my wall. What Are the Advantages, Really? Of course, there is a whole discussion on whether or not it is worthwhile to even be certified. If you can write great PHP code and you have been doing it for years, then there is no point—or so one side of the argument goes. As well, the Zend Certification web site has this to say about the benefits: In today’s competitive market, it’s more difficult than ever to stand out from the competition. The Zend Certified Engineer credential offers a variety of benefits: Career Rewards: • Differentiate yourself from competitors when looking for a new job or at your annual salary review • Get your resume noticed • Gain recognition from your employer • Have your profile displayed in Zend’s Yellow Pages for PHP Professionals
Figure 4
Figure 5
Exclusive Rewards at Zend: • Receive discounts on Zend products and PHP conferences worldwide February 2005
●
PHP Architect
●
www.phparch.com
• Preview emerging Zend products Now, to be sure that list isn’t terribly long, but it has been my experience that the certification is something that not only holds value to potential employers who are looking for at least a certain level of proven proficiency in the product, but that also gives one a great boost in self confidence in being able to say that they are a Zend Certified Engineer. Besides gaining the certification designation itself, you get a few other perks. You get to use the Zend Certification “mark” for promoting yourself and your services (Figure 4). As well, you are automatically listed in the Zend “Yellow Pages” at http://www.zend.com/store/education/ certification/yellow-pages.php
(Figure 5). Here, your name is listed by Country and Province or State, with the ability for those who may want to contact you to do so via e-mail. Summary I think that, overall, the benefits outweigh the costs in this process. Being a Zend Certified Engineer is one of the highlights of my 16-year computing career. I encourage you to start the process of becoming certified if you are on the fence about it. The cost is reasonable, and the values are real. Remember to give yourself ample study time and have fun with the exam—in my experience, that is the best way to deal with any nervousness that you may have.
About the Author
?>
Peter MacIntyre lives and works in Prince Edward Island, Canada. He has been and editor with php|architect since September 2003. Peter’s web site is at http://paladin-bs.com
57
Secure Communications with PHP and SSH
F E A T U R E
by Sara Golemon
Communication across public networks like the Internet poses serious problems to widespread applications. This article demonstrates the use of the SSH2 protocol within PHP for authentication, remote command execution, file transfer, and socket tunnelling.
T
he Internet is a dangerous place. Isn’t that what those commercials about spyware and trojans are always saying? Well, they’re right. When the Internet was in its infancy and going on-line often meant getting in line to use your college’s computing lab, cryptography wasn’t the first thing on people’s minds. No eCommerce meant that no-one was shuffling around credit card numbers, and since the personal computer was still a few years away from getting connected, a conscientious administrator could catch and block hacking attempts quickly and without being inundated. Given these limiting factors, it’s no wonder that the designers of so-called “core” Internet protocols didn’t give much concern to the notion of prying eyes watching their packets and stealing their data. Besides, the Internet was born of academia, where, in a perfect world, all ideas are free and information is about an open exchange. Welcome to 2005. Chances are, if you walk into a systems administration conference and mention off hand that you just upgraded telnetd on your production servers, you’ll receive more jeers than cheers. Same thing with your FTP daemon and that Web Application that asks your users to login by providing a password, but doesn’t use https://. When it comes to sensitive data, be it a customer’s financial information or your own administrative login credentials, there’s really no
February 2005
●
PHP Architect
●
www.phparch.com
good excuse for not using encryption. In the case of most popular protocols, a plethora of clients and servers already exist for applying a layer of encryption—for example, ftps, pop3s, imaps, https, and so on. PHP even provides access to the generic SSL transport to ease writing new wrappers for other protocols using this simple and flexible security layer. One major problem with each of these SSL-enabled protocols, though, is that they require a server daemon running on the box you want to connect to, a box on which you may, or may not, be the administrator. Unfortunately, while most shared hosting providers supply server daemons for common protocols and the SSL-enabled versions of these protocols, FTPS (FTP over
REQUIREMENTS PHP
4.3.0+
OS
Any
Other Software
SSH2 (PECL), libssh2, OpenSSL
Code Directory
secure
RESOURCES
i
URL http://www.libssh2.org/ URL http://www.openssl.org/
58
Secure Communications with PHP and SSH
SSL—not the same as SFTP, which we’ll look at later) is usually left out. It’s just not… popular enough. And why would they bother to include FTPS? Chances are they’re already running an SSH server that includes support for SFTP (FTP via SSH transport). What is SSH? SSH is not just “telnet with encryption.” Put that idea out of your head. SSH, defined by the Internet Engineering Task Force (IETF), is actually a collection of protocols. At its heart is a Layer 4 binary packet transport protocol that provides extensible encryption and transmission integrity verification. The transport layer uses a combination of very large primes for exchanging shared secrets, symmetric cryptography for encrypting payloads, one-way hashing for integrity checks, and optional compression. Residing above this layer are an additional two protocols (referred to as services). The first is authentication, using methods such as shared secret (password), public key, keyboard-interactive, with room for custom methods to be defined by individual implementations or extension of the protocol by community consensus. Nearly every layer of the SSH protocol is independently extensible without generating incompatibilities with other implementations. We’ll look at an example of this later on when we select specific ciphers. The second protocol residing above the transport layer is the connection service. This allows a single SSH connection to establish multiple “sub-streams” (known as channels), each carrying on an independent conversation. For example, a typical use of an SSH client might be to open a shell on a remote server with X11 forwarding and an additional window for file transfer. In this example, we’d have at least three separate channels: one for our interactive shell, one for File Transfers (SFTP), and one or more for X11 forwarding. Naturally, each of these channel types carries on its own conversation using a protocol sitting on top of the connection service. Figure 1 shows where each of these protocols
FEATURE fit. We’ll visit each of these layers in the next few sections to get a good feel for the theory behind SSH, then delve into some real world problems—and solutions. Finally we’ll finish up by looking at some less common tasks the more esoteric side of SSH. For additional details on the SSH protocol suite, refer to the IETF draft index at: http://www.ietf.org/ids.by.wg/secsh.html . The Transport Layer When a client first connects to a server, they exchange banners, a short string of text declaring protocol version numbers, and the name of the protocol implementation. A typical server banner might look like: SSH-1.99OpenSSH_3.9p1. This tells us that the remote server speaks SSH, with version 1 and version 2 compatibility, and is running OpenSSH version 3.9 portable. Our client will respond to this with: SSH-2.0-libssh2_1.0. Here, a version number of 2.0 indicates that our client only speaks version 2 of the SSH protocol, with no backward compatibility with version 1. Once this simple greeting is done, the client and server compare specific capabilities, stating what methods they support for transport layer activities. This method list includes KEX (key exchange), HOSTKEY (server identity verification), CRYPT (cryptographic ciphers), COMP (compression), MAC (Message Authentication Codes), and LANG (language preferences). The meaning of each of these methods and the means to customize them is detailed later in “Tweaking the Transport.” For now, let’s see what it takes to get the transport layer moving:
That’s it! In this one line of code, we’ve established a connection to the SSH server on shell.example.com, agreed on an encryption method, exchanged encryp-
Figure 1
February 2005
●
PHP Architect
●
www.phparch.com
59
FEATURE
Secure Communications with PHP and SSH
tion keys, and enabled secure communication for life of this connection. During this exchange, the server offers its public key and proves that it has the matching private key by sending a unique, non-reusable signature. PHP handles verifying the signature against the offered public key and calculates the fingerprint hash of the public key offered. PHP’s responsibility stops there however. While the remote server may have been able to prove that it has the private key matching the public key it offered, we haven’t yet confirmed that the public key offered actually belongs to the server we’re connecting to. To do that, we need to compare the fingerprint of the Listing 1 1
public key the server sent with a known fingerprint value. Listing 1 shows this in action. By default, ssh2_fingerprint() will return an MD5 hash of the fingerprint using ASCII hexits. ssh2_fingerprint() can also be instructed to return an SHA1 hash and/or raw binary values (See http://www.php.net/ssh2_fingerprint for more information). By verifying the fingerprint prior to sending any passwords, we ensure that the server we’re sending our password to is, in fact, who we think it is. Otherwise, we could wind up sending our password to a stranger without realizing it and allowing them the opportunity to use that information to break into the server we’d intended to connect to and do all manner of nefarious deviltry. The Authentication Layer Before any self-respecting server allows us to do anything to it, it’ll want us to verify our identity. The simplest way to do this will be to send our username and password combo. Since the transport layer has already been initialized, we can rest assured that this information is being encrypted en route and won’t be privy to spying eyes. If we’ve done our job of verifying the server’s identity by checking the fingerprint, then we also know that it’s not being sent to a well-placed third party. Referring back to Listing 1, we see the connection, verification, and login steps in action. A more exciting authentication method than passwords is public key authentication. You may recall from
Listing 2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
February 2005
●
PHP Architect
●
www.phparch.com
60
Secure Communications with PHP and SSH
our discussion of the Transport layer that, during the connection phase, the server is required to prove its identity using a public key that is known to the client ahead of time, and a signature generated with a matching private key. A third-party server residing between the client and the real server wouldn’t be able to simply reuse a signature previously fetched from the real server because the parts that go into making up the signature are unique to every connection. Similarly, when using public key to authentication to a server, that server can’t use your public key signature to turn around and login to another because the unique parts of the signature would be different. What this means is that, for our initial introduction, even if we don’t know the server’s fingerprint, we can send our public key signature and know that any intercepting third party won’t be able to do a thing with it. That’s not to say that you shouldn’t worry about verifying the server’s fingerprint if you’re using public key authentication—far from it. Just know that it’s one more layer of security between you and the ne’er do wells. The first part of Listing 2 shows us how to authenticate to a remote server using a public/private keypair. Even if you trust the security of your server very well, it’ll be a good idea to encrypt your private key. If you do so, you’ll need to include the private key’s passphrase in the fifth parameter of ssh2_auth_pubkey_file(). The format for public and private keys used by PHP’s SSH2 extension is identical to the formats used by OpenSSH. If you use OpenSSH as your server, you can follow these simple steps to generate a key pair: $ ssh-keygen –t rsa –b 2048 Generating public/private rsa key pair Enter file in which to save the key (/home/joe/.ssh/id_rsa): Enter passphrase (empty for no passphrase): sEcReT Enter same passphrase again: sEcReT Your identification has been saved in /home/joe/.ssh/id_rsa.
Figure 2
February 2005
●
PHP Architect
●
www.phparch.com
FEATURE Your public key has been saved in /home/joe/.ssh/id_rsa.pub. The key fingerprint is: b5:bc:72:25:86:d9:a2:37:5f:01:4c:a8:88:2b:dd:02 joe@hostname
If you’d prefer to use a DSA key, or a key of a different size, simply change the values used with the –t and –b options. You’ll obviously want to select a more unique passphrase than sEcReT, but the rest of the options should be safe to accept the defaults for. The fingerprint provided at the end is an MD5 fingerprint provided in hex (the same format used by ssh2_fingerprint() for the hostkey fingerprint). Once you have a key pair, you can pass the public key portion to all the servers you want to use it with, by appending it to /home/joe/.ssh/authorized_keys (in the case of OpenSSH), or whatever file your SSH server uses. Channels The common name for any substreams managed by the connection service is “Channel.” As shown in Figure 1, there are four root channel types defined by the SSH2 protocol. The first is the “Session” channel, which provides support for a number of interactive protocols including terminal shells, remote command execution, and file transfer protocols such as SCP and SFTP. The next channel type, “x11” is used by traditional SSH client applications for forwarding X11 protocol requests and can be ignored within the context of a PHP application. Last, but not least, are “forwarded-tcpip” and “direct-tcpip”, which correspond to server sockets (sstream_socket_server() and stream_socket_accept() ) and client sockets (ffsockopen() or stream_socket_client()). Listing 2 includes a short conversation with an interactive shell that stops and starts Apache on the remote server by way of the sudo utility. This is precisely as if we’d opened an ordinary SSH client, typed in those commands, paused for a couple seconds to read the resulting messages, and continued until finally exiting the shell and closing the window. Of course, interactive shells were meant for human beings, not autonomous scripts, so instead of starting up a shell, we can issue individual commands through the use of ssh2_exec() as also shown in Listing 2. Like with a shell, we receive a bidirectional stream to send additional data and receive responses through. Because this command requires no additional input and only returns a short bit of output, we can simplify its handling by using fpassthru() to dump whatever it returned to output. The last type of channel we’ll look at is the TCP/IP tunnel, which, as hinted above, comes in two flavors: ssh2_tunnel() establishes a connection channel using the direct-tcpip type to a remote host traversing through an encrypted SSH transport from client to server, then traveling unencrypted from the SSH server
61
FEATURE
Secure Communications with PHP and SSH
to its ultimate destination. The other flavor, forwarded-tcpip using ssh2_forward_listen() and ssh2_forward_accept(), causes the remote server to listen for and accept connections on a given port and forward them back to the client. To reconnect with the parallels we drew earlier, ssh2_tunnel() is like using fsockopen() or stream_socket_client() at the remote server, and using ssh2_forward_listen() / ssh2_forward_accept() is like using stream_socket_server() / stream_socket_accept() . Confused? Consider Listing 3. Here, we’ve established two separate SSH transports, one to host-a (stored in $sshA), and one to host-B (stored in $sshB). From host-a, we listen for connections on port 12345, then we establish a connection by way of host-b to host-a on port 12345, and accept the connection on behalf of host-a. Subsequently, when we fwrite() over the $client_socket, our packet gets encrypted and sent securely to host-b, decrypted and sent in clear text from host-a to host-b, and reencrypted for its route back from host-a along that transport. Figure 2 shows the route taken by these packets. Secure FTP and RCP The final piece of our SSH puzzle is probably also the most useful piece to an automated script: File Transfer. In the world of SSH, file transfer comes in three flavors: SFTP, SCP, and crazy. Raise you hand if you know what SFTP is… if you said “An encrypted version of FTP”, then shame on you, go back and read the introduction! SFTP is not FTP with encryption. That’s FTPS, and while FTPS is a nice solution to the problem of encrypted file transfer files, it still suffers from several of the drawbacks of the FTP protocol. For example, FTP provides very little in the way of stat() data—in fact, only the filesize element of stat can be reliably retrieved. Additionally, FTP/FTPS does not allow for advanced filesystem manipulation, such as the creation and resolution of symlinks, or seeking to arbitrary locations within an open file. To address these, and other limitations, the SFTP protocol was redesigned from the ground up with its own binary packet protocol designed to sit on top of the SSH transport layer protocol, and could, in theory, be used independently or with another transport. A full list of the SFTP functions available within PHP can be found in the online manual ( http://www.php.net/ref.ssh2 and http://www.php.net/wrappers.ssh2), but for starters we’ll just focus on the meat of it: Transferring Files. The first thing we need to do in order to use SFTP is request the SFTP subsystem from the remote server. Listing 4 shows this using the preferred method (via context options), and a fairly simple fopen() and fgets() loop to output the file to the browser. The PHP manual provides some shortcut methods, including the more familiar
February 2005
●
PHP Architect
●
www.phparch.com
fopen(‘ssh2.sftp://username:
[email protected] ple.com/path/to/file’, ‘r’) you’ve undoubtedly used with http:// URLs in the past. As mentioned above, SFTP offers a wide range of services related to reading and writing files as well as filesystem operations like renaming and deleting files, and adding symlinks; however, for simple file copying, SFTP is actually more power than you need. Using a simpler, more lightweight protocol known as SCP, you can easily transfer whole files from your local file system to a remote server and back. Once again, PHP comes to the rescue in the form of an easy to use pair of funcListing 3 1
Listing 4 1
Listing 5 1
62
FEATURE
Secure Communications with PHP and SSH
tions: ssh2_scp_send() and ssh2_scp_recv(). Bear in mind that, because SCP requires knowing the source file’s size before copying it to the remote server, it cannot be used with stream sources (such as HTTP and FTP) and generally works best with files on the local filesystem. If you must use SCP (perhaps SFTP is unavailable on your target server), and you must copy an http:// resource to it, first copy the http file to a temporary location on the local filesystem, then use scp to copy it up to the target server. Use something like the following technique:
Unfortunately, it’s entirely possible that the server you’re connecting to doesn’t support SFTP or SCP. Either the system administrator deliberately disabled those subsystems, or the SSH server they’ve installed simply doesn’t support it. If that’s the case, we’ll need to fallback on the third option mentioned earlier: “crazy.” At this point, I’d like to advise all seasoned programmers to turn their heads and cover their children’s eyes.
By making certain assumptions about our target operating system, we can sculpt cat and echo to play the parts of ssh2_scp_send() and ssh2_scp_recv(). For example, the last two lines of the previous code snippet could be changed to the following: $data = file_get_contents(‘http://www.example.com/x.html’); $arg = escapeshellarg($data); ssh2_exec($ssh, “echo –n $arg > /var/www/x.html”);
Similarly, we could retrieve the contents of a file through the use of cat and the ssh2.exec:// wrapper: $fp = ssh2_exec($ssh, ‘cat /var/www/x.html’); fpassthru($fp);
Before you run off using this method in every script, do yourself a favour and search for an alternate approach (like SFTP or SCP). They will yield much more consistent results and be compatible from server to server. One last important note about SFTP file transfer is the fopen() append mode. Unfortunately, the current release of OpenSSH doesn’t support append mode in its SFTP server. To simulate file appending, we’ll need to use another technique from the bag of dirty tricks: nonatomic seek and write. This technique involves opening a file for simultaneous reading and writing, then seek-
Listing 6 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42