This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
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
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.
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.
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
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:
Upon submitting the form, an SQL statement is assembled and then sent to the database. See a modified excerpt from the code: $sql = sprintf(“INSERT INTO entries (id, title, content) VALUES (GEN_ID(entries_gen, 1), ‘%s’, ‘%s’);”, $_POST[‘title’], $_POST[‘content’] ); ibase_query($db, $sql);
The expression GEN_ID(entries_gen, 1) retrieves the current value of the provided generator and increases it to the next value. The complete listing is available in the code archive for this article under the name insertbad.php. It’s probably obvious why the file carries that name: assembling an SQL query like that opens up your application to SQL injection attacks and must be avoided at any cost. You should, instead, use parameterized queries. Instead of “pasting” values into the SQL statement, you use placeholders and later provide values for those placeholders. The InterBase/Firebird extension then glues the values to the placeholders and takes care of any potentially dangerous special characters. In order to implement this, you first have to prepare an SQL statement using ibase_prepare(), providing the database handle and the parameterized SQL statement (each parameter being marked by the question mark character). The return value of ibase_prepare() can then be used with ibase_execute(). This function expects the statement object (returned by ibase_prepare()) and then a list of the values of all
placeholders. Here is a shortened code snippet that demonstrates the usage of these two functions: $sql = “INSERT INTO entries (id, title, content) VALUES ( GEN_ID(entries_gen, 1), ?, ?);”; $query = ibase_prepare($db, $sql); ibase_execute($query, $_POST[‘title’], $_POST[‘content’]);
This is both safer and also more efficient than concatenating strings into SQL. Listing 1 (iinsert.php) contains the full code for the HTML form that allows entering new weblog entries. Of course, you should build additional security into this page, such as a password-protection mechanism. Reading Out Information Up to now, we have only sent information towards the database but did not care too much about the return values. This will change in this section. The return values of ibase_query() and ibase_execute() identify the result set of these queries. Once you have these, the “standard” naming convention that most PHP extensions use applies to Firebird as well: _fetch_assoc() fetches the current row of the resultset into an associative array, while _fetch_object() creates an object where field names are turned into object properties. A simple while loop, in connection with one of those functions, iterates over the whole result set so that you can send the information gathered to the browser. Do note that, by default, field values are saved in uppercase even if they were typed in lowercase in the CREATE TABLE SQL statement. The following (again, simplified) code snippet reads
Firebird & PHP Saved entry.’; } else { echo ‘Something went wrong while trying to save the entry ...’; } } else { echo ‘Something went wrong ...’; } } ?>
February 2005
●
PHP Architect
●
www.phparch.com
14
FEATURE
PHP Under Fire
all the available information from the database, in reverse chronological order, and prints it out to the browser: if (false !== ($result1 = ibase_query($db,
Figure 6
‘SELECT * FROM entries ORDER BY creation DESC’))) { $selectcomments = ibase_prepare($db, ‘SELECT * FROM comments WHERE entries_id = ? ORDER BY creation DESC’); while ($row1 = ibase_fetch_object($result1)) { printf(‘
In the same script, all comments associated to a blog entry are read out. This is a point where prepared statements really come in handy and help with the performance: $selectcomments = ibase_prepare($db, ‘SELECT * FROM comments WHERE entries_id = ? ORDER BY creation DESC’ );
In every iteration through the result set, the placeholder receives the current value of the id field; then, ibase_execute() is called. The statement is therefore only compiled
’; print ‘’; print ‘’; } } else { print ‘Something went wrong while trying to read the weblog ...’; } } else { print ‘Something went wrong ...’; } ?>
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 ‘’;
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;
Firebird & PHP Saved entry.’; } else { echo ‘Something went wrong while trying to save the comment ...’; } } else { echo ‘Something went wrong while trying to save the comment ...’; } } else { echo ‘Something went wrong ...’; } } ?>
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);
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
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
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
addElement(‘text’, ‘email’, ‘Email’); $form->addElement(‘text’, ‘name’, ‘Name’); $form->addRule(‘email’, ‘That must be a valid email’, ‘email’); $form->addRule(‘name’, ‘Please enter your name’, ‘required’); if ($form->validate()) { $form->freeze(); echo ‘validated!’; } $form->display();
●
PHP Architect
What is HTML_QuickForm? HTML_QuickForm is a PEAR module for developing HTML forms. The module has been around for a number of years. HTML_QuickForm offers tools for form creation as well as flexible methods for validating form data. Not unlike Smarty, there are alternatives to HTML_QuickForm. The popularity of HTML_QuickForm, as well as its flexibility, history and the ability to integrate it with a number of different template engines were reasons I decided to pursue HTML_QuickForm. Sometimes (OK—often) users need some assistance when filling out a web form. Validation can be handled on the client side through JavaScript. That validation should probably be reinforced with server-side validation to ensure malicious posts or posts from browsers without JavaScript are properly verified. HTML_QuickForm can take care of both server-side and client-side validation, but, in this article, we’ll concentrate on the former only. If you’ve written your own validation functions, perhaps they accept an input value, a failed prompt, a minimum length and a maximum length among other parameters. When you want to validate a value, you might pass your function those arguments and expect a value in return. This is similar to the way HTML_QuickForm allows you to do validation. In fact, you can take a look at Listing 4 for a quick intro to HTML_QuickForm. After including HTML/QuickForm.php, an instance of the HTML_QuickForm object is created and the form name (and ID), method and action are defined with $form
=
new
HTML_QuickForm(‘frmTest’,
‘POST’,
Listing 3 { if $row.count == 0 }
Sorry, your login failed.
{ else }
Your login was a success!
{ /if }
?>
February 2005
Why Smarty? Documentation, popularity, performance, debugging console and ease of use have all been reasons why I’ve been drawn to Smarty. Most all of the benefit I see from using Smarty could be accomplished by using Flexy or another template engine. My first exposure to Smarty came after reading an article in php|architect in the summer of 2004. Reading Jason Sweat’s articles on Phrame and Model View Controller (php|architect, May 2003), I was in search of a cleaner way to build applications. While initially interested in the architectural benefits that Phrame and MVC could bring to web applications, it became apparent that by using Smarty on its own, it would be possible to realize the benefits I was looking for.
●
www.phparch.com
22
HTML_QuickForm and Smarty
[$_SERVER[‘REQUEST_URI’]) .
Next, two text fields name and email are defined through the $form->addElement() method. The final element of a submit button element is then added. Two rules of validation are created using the $form>addRule() method. The email field is assigned a rule that requires a valid email. The rule for the name field, on the other hand, only requires that a non empty value be submitted. A test is then performed to see if the form is valid through $form->validate(). If the form is validated successfully, the date is “frozen,” which allows no further editing, and a “validated!” is echoed back to the browser. This mini demo ends with the display of the form through $form->display(). When executed, the code from Listing 4 is rendered Figure 1
Figure 2
FEATURE as shown in Figure 1. If that form is submitted and the validation succeeds, the user would see a screen like Figure 2. Should validation fail, the user would end up with a screen like Figure 3. I found it odd at first to adjust to building forms programmatically like this, but I really enjoy the benefit of the built-in validation now. Still, the code in this example may look too inflexible for your needs. In the next example, we’ll go over how to combine the tools of Smarty and HTML_QuickForm for a mini registration application. A very good introduction to the features and ways to use HTML_QuickForm is on the PEAR website at http://pear.php.net/manual/en/package.html.html-quick form.tutorial.php .
Smarty and HTML_QuickForm HTML_QuickForm allows rendering of the form to be handled by the default renderer as done in the example in Listing 4 or through a template renderer. The basic HTML_QuickForm generates a form that is completely functional; however, if you are building a project with some greater design requirements, you might find that default appearance does not meet the needs of your project. By defining an alternate template renderer with HTML_QuickForm, it is possible to achieve greater presentation customization and control over the form’s labels and elements as well as validation messages. The example project we’re about to examine combines Smarty and HTML_QuickForm in a simple registration application. This application can be set up with these steps on your own development machine: • Install the latest version of the PEAR::DB and PEAR::HTML_QuickForm modules. Instructions for installation are available at the PEAR website http://pear.php.net . • Install the Smarty template engine. Smarty and installation instructions can be found at http://smarty.php.net . • Create a new MySQL 4+ database (this example uses the name db_test for it) and run the SQL code shown in Listing 5. The commands in that file create tables for registration (ttbl_regis-
February 2005
●
PHP Architect
●
www.phparch.com
23
HTML_QuickForm and Smarty
• •
•
•
•
tration) and conferences (ttbl_conference). Additionally, a few records are inserted into tbl_conference so that we may have some basic data to work with. Create a directory named regApp within a web-viewable location. Store index.php, regform.tpl and thankyou.tpl inside the new regApp directory. In index.php, the $tpl->compile_dir variable may need to be modified from /tmp to the full or relative path of a directory which is writable by the webserver. /tmp should be fine for many non-Windows users. Smarty.class.php may exist on your machine in a location different than is specified in the index.php file. Depending on how the Smarty template engine was installed, this require may need to be modified to match your system. Modify the $dsn variable in the index.php file to a MySQL user that has SELECT and INSERT privileges on the new database. For demonstration purposes, it should be fine to use the username and password used for creating the database.
After completing the steps above, if all went well you’ll be able to point a browser at the newly created directory and see the web page shown in Figure 4.
Listing 5 CREATE TABLE tbl_registration ( id int(11) NOT NULL auto_increment, ts timestamp(14) NOT NULL, registrationdate datetime NOT NULL, conferenceid int(11) NOT NULL, prefix varchar(4), firstname varchar(100) NOT NULL, lastname varchar(20) NOT NULL, address1 varchar(50) NOT NULL, address2 varchar(50), city varchar(50) NOT NULL, state varchar(30) NOT NULL, zip varchar(10) NOT NULL, phone varchar(15), email varchar(50) NOT NULL, PRIMARY KEY (id), KEY idx_conference_email (conferenceid,email,id) ); CREATE TABLE tbl_conference ( id int(11) NOT NULL auto_increment, cost decimal(10,0) NOT NULL, date date NOT NULL, location varchar(80) NOT NULL, title varchar(255) NOT NULL, PRIMARY KEY (id) ); INSERT INTO tbl_conference (cost, date, location, title) VALUES (200, ‘2005-07-11’, ‘Las Vegas’, ‘Vegas baby, Vegas!’); INSERT INTO tbl_conference (cost, date, location, title) VALUES (99, ‘2005-07-18’, ‘Boston’, ‘Solving problems through bigger government’); INSERT INTO tbl_conference (cost, date, location, title) VALUES (500, ‘2005-07-25’, ‘Toronto’, ‘Management Consulting’);
February 2005
●
PHP Architect
●
www.phparch.com
FEATURE How Does It Work? The first few lines include the PEAR modules and Smarty class file, which enables the use of their respective classes and methods. After requiring the modules, it is possible to set up a DSN-style connection string and connect to the database. If the attempt to connect to the database results in an error, the code ends by echoing out that message. Next, a new Smarty template object is created and the paths for the Smarty template and template compilation directories are defined. By default, Smarty looks to place templates in a sub-directory called templates, while the compilation of templates is done within a sub-directory named templates_c. The Smarty compilation process interprets template files into PHP files and places those files within the compilation directory. In this example, the template files are located in the current directory. This was done for simplicity, as there are only two template files. The template compilation directory was defined as /tmp to keep things easy to set up. It seems to be considered good form to place the template compilation directory in a location which is not viewable through the web—but do remember that it must be writable by the web server. To reduce the steps for setup in this demonstration, the template compilation directory of /tmp was selected. One very cool feature of Smarty is a debugging console, which you can enable by setting the debugging value to true. When debugging is turned on, the Smarty debugging console pops up in a new browser window. This can be hugely helpful in seeing a big-picture view of variables that are available for use in the Smarty template file. If you’re like me and get stuck sometimes because you can’t see something right under your nose, this tool can help. Next, a couple arrays are set up. The data from these arrays are used in HTML select elements. The first array is built by selecting data from the database. A request is made of the database for upcoming conference records (ID values and conference information). That data is assigned to the $aConference variable through the $db->GetAssoc() method. $aPrefix is a two-dimensional array of prefixes. There are a number of ways to populate select elements, both within HTML_QuickForm and through Smarty. Building a select element with results from a database was an example that seemed important to include. With some preliminary variable definitions out of the way, the HTML_QuickForm object is created. frmReg is defined as the name and ID of the form, POST is the method and the action is set through the $_SERVER variable $_SERVER[‘REQUEST_URI’]. The next group of lines are all very similar—form elements are rapidly defined through the $form->addElement() method. This block of code adds a header, the two select elements that
24
FEATURE
HTML_QuickForm and Smarty
were defined earlier and some text fields. It is possible to group form elements together by creating an array of elements as is done for $cityStateZip. Here city, state and zip elements are added through the HTML_QuickForm::createElement() method. That array is added to the form object through the $form->addGroup() method. A couple of custom validation functions are registered with $form->registerRule(). registerRule() behaves slightly differently, depending on the type of rule that is being called. For example, the first rule is assigned the name checkCityStateZip . It executes a callback function of vCityStateZip. The second rule is assigned the name checkPhone and works using the specified regular expression /d\{3}\D*\d{3}\D*\d{3}\D*\d{4}\D*\d*$/ . If you’re unfamiliar with regular expressions or have a tough time with them as I do, the string can be broken down like this: Figure 3 /\d{3} \D*\d{3}
Gaining On It! All the form elements have been defined and the groundwork for validation has been completed. The new renderer object is created by passing the Smarty template object. Presentation behaviours are defined for the renderer with setRequiredTemplate() and setErrorTemplate(). These methods receive their parameters as Smarty template code, which is then used in rendering the form when required fields or errors are encountered. The code attempts to validate the form through the $form->validate() method. If the form data passes validation rules, the form is processed with the addRegistration() function. That function inserts a reg-
# 3 digits # 0 or more non digits followed by 3 digits # 0 or more non digits followed by 4 digits # 0 or more non digits followed # ending with an optional number (eg extension)
\D*\d{4}
\D*\d*$/
I almost always need to seek reference when creating regular expressions, but they can allow you to do such great things with very limited code that it’s almost always worth the effort. This validation test only passes after receiving a phone number that matches the format defined in the regular expression. 212 555 1212, for example, matches, as would (212) 555-1212 ext. 12. An alternative regular expression would be more welcoming for attendees coming from outside the United States and Canada. Take a look at George Schlossnagle’s regular expressions articles from the March 2004 and April 2004 editions of php|architect for deeper information in putting regular expressions to work in PHP. Once these specialty validation rules defined, they are added with the method $form->addRule(). addRule() accepts the name of the field or group that the rule applies to as well as a prompt to display if a validation rule doesn’t pass. addRule’s final parameter
February 2005
is the type of rule which is being applied (e.g. required, email or a custom rule).
●
PHP Architect
●
www.phparch.com
Figure 4
25
FEATURE
HTML_QuickForm and Smarty
istration record into the database. A thank you message is defined and the thankyou.tpl template is displayed. If the form doesn’t pass validation (because of invalid or no input), the $form object accepts the $renderer renderer to build the HTML. The renderer is defined as a Smarty vari-
able called form_data, which allows the Smarty template access to the variables within the $renderer as $form_data. index.php concludes with two functions. First, the vCityStateZip() validation function returns true or false depending on the data that it received. Then,
finally, the addRegistration() function inserts a registration record. If any of this is unclear or seems kind of ethereal as we’ve not seen any concrete HTML tags yet, no worries. The tangible HTML is right around the corner. The Smarty variables that have been assigned will soon be put to work. The Templates The index.php file hands things off to the template file by passing its name as a parameter to the Smarty display() method. The regform.tpl file is mostly HTML, with a bit of Smarty code thrown in for good measure. The file begins as a typical HTML web page with cascading style sheet (CSS) definitions. Curly braces are special to Smarty, just as the 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