Sunteți pe pagina 1din 67

FEBRUARY 2003 VOLUME II - ISSUE 2

php|architect The Magazine For PHP Professionals

The Object Oriented


Evolution of PHP5
A Look At The New OOP Model Creating A Customized
Using The Zend Engine 2.0
By Zeev Suraski Template-Engine
Hacking the Smarty
Template Engine

Enterprise Applications:
PHP in a Multitier Environment
Data Fingerprinting with Hash Functions in PHP
www.phparch.com

Time Travel: Breadcrumbs and Session History with PHP

Licensed to: Plus:


Tips&Tricks, Book
Joseph Crawford
info@josephcrawford.com Reviews, Product Reviews
and much more..
User #63883
The designers of PHP offer you the full spectrum of PHP solutions

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


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

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

Technologies Ltd.
TABLE OF CONTENTS

php|architect
Departments Features

10 Time Travel - Breadcrumbs and


History with PHP
4 EDITORIAL RANTS by Peter James
INDEX

18 The Object Oriented Evolution

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


5 NEW STUFF of PHP
by Zeev Suraski

6 REVIEWS 27 PHP in an N-Tiered World


Zend Encoder V.3.1.0 by Dave Palmer

32 Creating a Customized
22 REVIEWS Template-Engine
LogiCreate by Andreas Demmer

61 TIPS & TRICKS 38 Building a Turing Test with PHP


by John Holmes
and the GD Library
by Marco Tabini

65 BOOK REVIEWS 47 Using, Reusing and Extending


Smarty
by Tomica Jovanovic

67 exit(0);
Hello, Mr. Gates? 55 Data Fingerprinting with
Hash Functions in PHP
by Leon Vismer

February 2003 PHP Architect www.phparch.com 3


EDITORIAL
EDITORIAL RANTS
L ately, Ive been measuring the
progress of php|a in terms of
was another flash of inspiration,
if I may say so myself), but I will
php|architect
brilliant ideas. After a careful move on to some of the new ini- Volume II - Issue 2
analysis of our history up to this tiatives that we have in store for February, 2003
point, I have noticed that you, and Brian will take over the
instead of following a smooth entire editorial process of php|a.
Publisher
linear progression based on con- As my parting words from my
stant innovation, our ascension editorial post, let me tell you Marco Tabini
from the ashes of the publishing about the Editor-in-Chief.
world seems to have resulted The post of Editor-in-Chief is
from short-lived but intense instrumental in defining the Editors
flashes of genius, which are then course and stature of a maga-
followed by long periods of cere- zine, and I can only be happy Arbi Arzoumani
bral inactivity. (A related study that Brian has decided to accept Brian K. Jones
also showed that I have way too our request to take this position Marco Tabini
much time on my hands, but I with us (not to mention how
Graphics & Layout
was far too busy to notice or happy I am that someone else
will be doing it! But alas, poor

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


even care about that). Arbi Arzoumani
For example, Arbi came up foolish soul that he is, Brian will
with the idea of calling our mag- only read this once the maga-
azine php|architect after I had zine has already hit the virtual Administration
been brooding over names like stands, and by then his fate will
The PHP Gazette and The be sealed). Emanuela Corso
PHP Informer for days. I can Naturally, the person who can
only be thankful for the com- sport the title of Editor-in-Chief
pletely random sequence of enjoys a great deal of powera Authors
synaptic signals that made that power that Brian promptly
Andreas Demmer, Peter James,
brilliant idea come to life. abused by asking me to rewrite Tomica Jovanovic, Dave Palmer,
Given the spontaneity of our this editorial, claiming that my Zeev Suraski, Marco Tabini,
collective genius, it would most thoughts were too fragmented. Leon Vismer
definitely prove futile to even try As if. That could ever. Happen.
to explain how we arrived at our Let me now tell you about the
most brilliant idea: to ask Brian Publisher. The post of Publisher
Jones, who joined our editorial affords great powers as well.
php|architect (ISSN 1705-1142) is published
staff last month, to become our That includes the power to edit twelve times a year by Marco Tabini & Associates,
new Editor-in-Chief. I can only and reject (did I say reject? I Inc., P.O. Box. 3342, Markham, ON L3R 6G6,
Canada.
say that it was a moment of meant evaluate) the Editor-in-
unparalleled brilliance. Im sure Chiefs monthly editorial. Although all possible care has been placed in
assuring the accuracy of the contents of this mag-
that we burned a good number Regardless of whether his azine, including all associated source code, listings
and figures, the publisher assumes no responsibil-
of those remaining brain cells thoughts are fragmented or not. ities with regards of use of the information con-
which had not yet been fried by Ah, the bittersweet taste of tained herein or in all associated material.

years of exposure to the radia- revenge...


tion that our monitors happily Contact Information:
produce to keep us warm, but it General mailbox: info@phparch.com
was worth it. Editorial: editors@phparch.com
Subscriptions: subs@phparch.com
This is, therefore, my last edi- Sales & advertising: sales@phparch.com
torial as the Editor of php|a. I will Technical support: support@phparch.com

still be the Publisher of this mag-


azine and I will do my best to Copyright 2002-2003 Marco Tabini &
contribute my thoughts to our Associates, Inc. All Rights Reserved

exit(0) column (whose name

February 2003 PHP Architect www.phparch.com 4


NEW STUFF

ionCube Launches New Encoder users of the OpenWeb product free of charge. A
new full subscription can be purchased for $60 US
British PHP software company ionCube Ltd. from the OpenOSX website at :
released version 2.0 of their standalone Encoder http://openosx.com/openweb/
product last month. The new encoder providers
additional features compared to its predecessors, 2003 PHP Conference in Montral
such as the possibility of creating text headers that
are appended to the encoded files. This could be This just hot off the pressThe Qubec PHP
NEW STUFF

useful, for example, for creating copyright and Association announced that they will be holding a
ownership notices, as well as instructions and PHP conference in Montral, Canada, on March
how-to guides at the beginning of each file. The 20th and 21st. The conference will attract a whos
Encoder sells for $349.00 US. who of the PHP community, from PHP creator
For more information, you can visit the ionCube Rasmus Lerdorf to Zend-engine co-creator (and
website at : php|a author) Zeev Suraski. The conference will
http://www.ioncube.com/encoder include sessions in both French and English, and it
is sure to attract visitors from the Americas as well
TechMeridian Releases XAVIOUR as from Europe.
CMS The php|a team will be with a booth and to

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


cover the event.
TechMeridian, a new PHP development compa- http://phpconf.phpquebec.com/
ny based in the United States, has released a prod-
uct called XAVIOUR Web Application Platform. php|a
According to TechMeridian, XAVIOUR represents a
combination of the features normally found in
content management and templating systems. It
promotes reusability of the code and provides a
flexible framework that can easily be extended by
We Dare You
writing code directly into the templates, if neces-
sary.
XAVIOUR is entirely based on PHP and
To Be
PostgreSQL, thus providing a platform that can
easily be taken to an enterprise-level of stability
and performance. The software product costs
A Professional.
Subscribe to php|a Today and
$199.00 US for a single-domain license. More Win a book from Wrox Press
information can be found on the TechMeridian
website at :
http://www.techmeridian.com/xaviour

OpenOSX Updates OpenWeb With


PHP 4.3.0

MacIntosh open-source software provider


OpenOSX have updated their OpenWeb CD prod-
uct to include the latest version of PHP.
OpenWeb is a CD compilation of open-source
software for the MacIntosh market designed to

php|architect
provide publishing and development tools to Web
developers and webmasters. Besides technologies
like Apache, PHP and MySQL, it also includes a
The Magazine For PHP Professionals
shopping card, a content management system
and a graphical management interface.
The new 2.5.1 update is available to registered

February 2003 PHP Architect www.phparch.com 5


REVIEWS

Reviewed For You

Zend Encoder
REVIEWS

V.3.1.0

By Marco Tabini

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


T he simplicity and immediateness of a scripting lan-
guage like PHP is a double-edged sword. On the
one hand, everyone has direct access to the source
The Cost:
$960.00 - $2,880.00 (US)
(or less through special small business pricing)
code; no need to compile for separate platforms and
distribute separate versions of your application. On the Requirements:
-Supported PHP versions: 4.0.5 up through 4.3.0.
other... everyone has direct access to the source code, -Supported operating systems:
which makes the protection of intellectual property -Linux glibc 2.1 / 2.2 (e.g. RedHat
6.x/7.x/8.x, Debian 4.2, SuSE 6.4, Mandrake
really difficult. 8.1, and others)
Lets face facts, it would be difficult for a software -Windows 98 / NT 4.0 / 2000 / XP.
market to exist without some form of code protection. -Solaris Sparc 2.6 /2.7 /2.8 (Non-GUI ver-
sion).
After all, PHP consultants and software developers are -Supported Web Servers for deployment:
selling their knowledge, which, for better or for worse, -Apache 1.3.x, IIS 4 / 5
-Zeus (using FastCGI) or any Web server that
is somewhat embedded into the source code they pro- supports CGI
duce. The demand for code protection has resulted in -The Graphical User Interface is supported under
Windows and Linux only.
several products which have been developed to make it
possible to limit the functionality of a PHP script (or a Product Page:
series of scripts) so as to facilitate a commercial licens- Zend Encoder

ing scheme. Home Page:


The Zend Encoder, produced by Zend Technologies Zend Technologies (http://www.zend.com)
(yep, the same folks who wrote the Zend Engine on Company Background:
which PHP is based) is a rather complete system that Zend Technologies provides web developers and
enterprises using PHP, integrated software solutions
makes it possible to transform a PHP source file into an for developing, protecting and scaling their PHP
intermediate representation of itselfthat is, a pre- applications providing a foundation that allows
interpreted set of bytecode instructions that the PHP companies to efficiently and effectively develop PHP
based web applications. Zend's founders are the
interpreter would be able to execute but that are quite designers of PHP 3, PHP 4 and Zend Engine 1.0; on
meaningless to a human being. The resulting file is fur- which all PHP sites and applications are run. They
are also currently leading the design and develop-
ther mangled to make reverse-engineering almost ment of PHP 5 and the Zend Engine 2.0.
impossible. In addition, it is possible to require that a

February 2003 PHP Architect www.phparch.com 6


REVIEWS Zend Encoder V.3.1.0

script only run if a special license file is present and The User Interface
provided by the end-user, thus making it possible to
limit the execution of a script to a specific timeframe Once installed, the Encoder launched flawlesslyno
(limited trial), or to specific IPs, and so on. ifs, ands or buts about it. It features a neat graphical
user interface (shown in Figure 1) that makes using the
Installation software for the encoding of large numbers of files easy
and convenient.
Zend installation systems are, in my experience, As you can see, the interface is based on the concept
among the best ones available to the PHP community. of a projectthis way, you can encode entire direc-
As in most cases, the Encoder is a Java-based applica- tories of PHP scripts at the click of a mouse. In fact, I
tion that will run on pretty much any platformthe tried to encode the entire php|a website, and the entire
Zend website allows you to download a version for operation took only a few seconds. The original files
Windows, Linux or Solaris. were not overwritten (if they were, Id lose our source
On Linux, the platform I tested it on, the application code!), but copied over to a destination folder of our
is set up through a very straightforward process that choosing. The program even skipped over our CVS
even connects to the Zend website and downloads the folders automatically!
appropriate license code automatically. A free 14-day The Encoder supports several options designed to
trial of the Encoder is available, its only limitation being enrich the value of encoded files. For example, its pos-
that your encoded files will only work for three days, sible to prepend a clear-text header to the file that

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


regardless of what settings you choose when convert- allows you to insert installation or usage comments, or
ing them. even plain PHP code that is executed if the server is not
set up properly to accept encoded files. In addition,

Figure 1

February 2003 PHP Architect www.phparch.com 7


REVIEWS Zend Encoder V.3.1.0

the Encoder also applies several opti- Figure 2


mizations to the code while encod-
ing it (although I did not find much
in the way of improvement to our
scripts performance... I guess we
just write good code!), and it is pos-
sible to require that each encoded
script only work with other encoded
scripts. This last feature can be very
important if, for example, your
include files contain your custom
license authentication mechanism
and you dont want them to be
replaced with dummy versions that
circumvent your scheme.

Licensing Capabilities

Perhaps one of the most interest-

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


ing features of the Encoder is its abil-
ity to encode a project and control
the functionality that an end-user
licenses in an automated fashion directly from a web-
has access to by issuing a special license file. This can be
site.
very useful if you plan to offer specialized trial ver-
sions of your application that the end-user can play There is no online help for the Encoder, but the instal-
with, build upon as needed and then upgrade to a lation includes a well-thought-out manual in PDF for-
full version by just installing a single file. Licensing is mat that provides plenty of information on using the
only available if support for it is activated when a script Encoder, both programmatically and from the GUI.
is encoded. Unfortunately, neither the installation program nor the
The licensing screen (Figure 2) offers a wide variety of
GUI application itself offers any hints that the docu-
options, including the ability to limit the execution of
mentation itself exists, and this may discourage the
the scripts using constraints such as time or IP or even
a hardware ID that is generated by a small Zend appli- less-than-zealous user.
cation distributed together with the Encoder.
Bottom Line
Distributing the Code

Once encrypted, a project can be redistributed by The Zend Encoder is not a cheap product, although
simply transferring the files to another system. you can actually get it very inexpensively through the
Depending on the settings, you may also need to dis- Zend Small Business Program that we illustrated in last
tribute a license file. months issue of php|a ($295.00 US will grant you a
In order to execute your script on a target system,
license to the Encoder, the Zend Studio IDE and the
your customers will need to install a copy of the Zend
Optimizer together with their installation of PHP. This is Zend Performance Suite). However, it is also a very
not normally a big problem, as the Optimizer is a freely well-thought-out product that offers ease-of-use and
available product whose installation only require a consistencysomething you dont always find in the
small change in the php.ini file. However, if the appli- world of PHP.
cation is being hosted by a third party, it might be dif- If youre looking for a way to protect your products
ficult to convince them to install this software. and want the maximum flexibility possible, then I rec-
ommend you check this product out. If your business
Advanced Capabilities And
qualifies for the Small Business Program (visit
Documentation
http://www.zend.com/store/products/zend-
In addition to the GUI, the Encoder also features the smallbiz.php for more information). I think you will
necessary tools for encoding scripts and generating find that the entire package offers tremendous value at
license files on-the-fly through a command-line exe- a very reasonable price.
cutable. This can be helpful if your goal is to hand out php|a

February 2003 PHP Architect www.phparch.com 8


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

Time Travel -
Breadcrumbs and
History with PHP
FEATURES

By Peter James

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


With all of the things to consider when designing and laying out your site, you can quickly become lost and over-
whelmed. Usability, accessibility and localization usually require attention before a single line of code is written.
While I cant possibly hope to cover all of these topics meaningfully in a single article, what I can do is give you some
code to implement one very important (and often overlooked) tool to increase your sites usability: Breadcrumbs.

Introduction tion), other types of information can be gathered pas-


sively and silently.
The great philosopher and novelist George Santayana A prime example of the type of data which can be
once told us: Those who cannot remember the past collected behind the scenes is patterns of use informa-
are condemned to repeat it. tion. This can provide invaluable insight into how your
The past has always been a vital part of the present. site is being used, allowing you to make decisions
Like a map, it tells us where weve been and gives us about the layout of your site. This could influence link
important information with which to make decisions
about the future. If youre asking yourself what all of
this has to do with PHP, let me show you.
Information is critical. If you
Information is critical. If you are reading this maga- are reading this magazine,
zine, your business probably revolves around informa-
tion, and you probably recognize the value of that your business probably
information to the people who want it. The fact is,
regardless of what your business is, every time someone revolves around information,
visits your site you have the opportunity to capture use-
ful information about them. That information can be and you probably recognize
used to significantly enhance their experience on your
site, and there are many levels of detail to be had.
the value of that information
For instance, you could request that users register to the people who want it.
and log in to your site, which usually has required the
provision of at least some personal information on
behalf of the end user. You could use this to contact REQUIREMENTS
your customers with promotions or newsletters, or tar-
PHP Version: 4.0 and Above
get their demographic (age, gender, location) with
O/S: Any
advertising. While the gleaning of this personal data
Additional Software: N/A
requires action from the user (ie, voluntary registra-

February 2003 PHP Architect www.phparch.com 10


FEATURES Time Travel - Breadcrumbs and History with PHP

placement, advertisement placement and numerous


other factors. As an aside, one popular tool that can aid Figure A
in the discovery and inspection of this data is Home > Life > Plants > Flowers > Daisy
phpOpenTracker.
Another silently collected class of data, which will be
our primary focus in this article, is the history of the cur- Usability expert Keith Instone tells us that there are
rent session. This is the most easily handled type of generally two different types of breadcrumbs: location
information. We can get it as they navigate, use it and path.
while we have them on the site, and then discard it Location breadcrumbs are useful on hierarchical sites,
when their session ends. Unlike many other types of and represent a straight line from home (or the root) to
desirable user data, it doesnt require a database or the your current location. This is similar to a file path in the
associated design; it is very transient. Interestingly Windows Explorer location bar, or the output of the
enough, if stored across sessions, the history provides pwd command in Unix, and is what we saw in
patterns of use data, which we mention briefly above. Figure A.
Among many other things, this information can be Path breadcrumbs are much less structured, and
used to provide enhanced navigation options, called show the actual path you took to your current location,
breadcrumbs, which is the subject of this article. as in Figure B. Hence, if you jump in from a search
First, Ill first explain what breadcrumbs are, and then engine, youll see a breadcrumb with only one crumb.
Ill examine in detail the ways in which breadcrumbs Clicking a link on that page will take you to another

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


can be used, giving you plenty of usable code along the page on that site, where you will then see two crumbs,
way. and so forth. This can be used on non-hierarchical sites
to supplement with a visual whats under the browsers
A note about the coding back button.
style used in my examples
Figure B
In all of my examples involving sessions, I will use the Store > Tables > Wines > Tables >
$_SESSION[] array, rather than the associated glob- Liquers > Edit
als. Aside from being what I consider to be a cleaner
approach, this will reduce the chance for the introduc-
Is that clear? Location breadcrumbs are like a You are
tion of errors by, for example, missing a
here marker on a map, and path breadcrumbs are like
session_register() call. I also do not assume in
a highlighted route on a map. Location breadcrumbs
these examples that you will have register_glob-
are more focused on where you are in the scheme of
als enabled in your php.ini file. This will ensure max-
things, whereas a path breadcrumb is more concerned
imum compatibility with different systems and PHP
about the way you came.
implementations.
As an aside, it may be that if you used a search engine
and looked for daisy, you might come across the
Of Bread and Crumbs page in Figure A directly. Strangely, you may still see
the full location breadcrumb, even though you didnt
...and Hansel comforted his little sister and said: Just navigate through those points. This is because pages
wait, Gretel, until the moon rises, and then we shall see on sites that implement location breadcrumbs typically
the crumbs of bread which I have strewn about, they know their way home. This can work in a couple of dif-
will show us our way home again. ferent ways, which Ill discuss a little later.
- Hansel and Gretel - Brothers Grimm, et al Because they are the most common form, this article
will focus on the location breadcrumb concept,
Earning their name from the above-quoted famous although I will relate back to path breadcrumbs when
tale, breadcrumbs are a navigation tool that helps the appropriate. In fact, the second example that we will
user find their context, and quickly navigate back to cover will actually be a hybrid that looks like a location
anywhere in that context. Breadcrumbs can be used to breadcrumb, but the processing underneath is closely
expose the structure of the site to the user, allowing related to that of the path breadcrumb method. From
them to learn the location of items. this point on, unless otherwise noted, when I talk about
Usually found near the top of a web page under the breadcrumbs Ill be referring to location breadcrumbs.
title banner, breadcrumbs can be created using any
server-side or client-side language. An example bread- More About Breadcrumbs
crumb is shown in Figure A, and a nice, live example of
breadcrumbs can be found at Googles web directory Breadcrumbs usually will appear like a directory struc-
at http://directory.google.com/

February 2003 PHP Architect www.phparch.com 11


FEATURES Time Travel - Breadcrumbs and History with PHP

ture, which is appropriate since they work best when individual library pages, but the category layout is
you have a highly hierarchical site layout. A user can mostly static. Lets write some code.
quickly navigate to any previous level by clicking one of In Figure F, youll see a simple class that I created to
the links in the breadcrumb. For instance, if a user show how easy it is to deploy this form of breadcrumb.
clicked on the Plants link in Figure A, she would likely Lets examine this code. I neednt have used a class
see a page listing the different types of plants, such as here, but it provides nice encapsulation. The classs job
flowers, trees, and grasses. The breadcrumb found on is to build breadcrumbs from the scripts URL, and
that page might look like Figure C. return the finished product. There are two member
variables: $_separator, which contains the charac-
Figure C ter used to delimit the links in the breadcrumb, and
$_label_map, which is a list of labels to use for direc-
Home > Life > Plants
tory names. This code works off of the directories in
the scripts URL, and the label map allows us to give
Breadcrumb separators can be anything, including these directory levels descriptive labels, rather than out-
images, but are commonly one of a number of semi- put the raw directory names.
intuitive characters, as shown in Figure D. The class also contains a basic constructor, as well as
a couple of gratuitous setter functions. The main func-
Figure D tionality, though, comes from get_output(), which
Home > Movies > Animated actually produces the breadcrumb. Lets look at how.

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


Home | Plays | Comedies It first gets the scripts path from the $PHP_SELF
Home : Cooking : Fish server variable. If this class was instantiated from the
Home / Monsters / Sully script running at
http://www.example.com/foo/bar/index.php
The links, such as Home and Monsters above, are links an example of what wed get from this variable is
that contain just enough informtion to get you back to /foo/bar/index.php. The method then gets the
that place in your session history. Depending on your highest directory in the path, also the current directo-
intention, and the layout of the site, this information ry, which in this case would be bar. Then it explodes
can be very different, ranging from a plain script name the path and puts all of the pieces into the $dirs vari-
through to a full blown URL. (or even a Javascript func- able.
tion call). It now loops over all of the entries in $dirs. $base
keeps a running concatenation of the directories
HarveysHTMLHaven.com already processed and is the basis for all links in the
breadcrumb. As each directory is processed, the loop
Lets set the stage. Harvey is an aspiring business searches for an entry in the $label_map and, if one is
leader, and his final project while studying for his MBA found, outputs a breadcrumb using the current $base
required a business idea that related to IT. Harvey value, and the label from the map.
thought about it (no pun intended), and decided that Once all of the directories are processed, the
as a part of his business he could resell web code and $crumbs are joined together using $separator, and
graphics. All he had to do was browse the web for an
hour or two, and hed have enough code and images Figure G
to start up his business, HarveysHTMLHaven.com.
<?php
Harvey ended up using a highly hierarchical physical
structure for his site. Directories named images and // simple example of using SimpleBreadcrumb class
code resided in his sites home directory. Inside of the $crumb_label_map = $array(images => Images,
buttons => Buttons,
images directory were four more directories named but-
clipart => Clip Art,
tons, clipart, icons and photos. Inside each of these icons => Icons,
were, potentially, gif, jpg and png. The code directory photos => Photographs,
had a similarly nested structure. For brevity, well focus jpg => JPEG,
only on the images tree. gif => GIF,
png => PNG,
A user could follow links down any of these paths, code => Code,
and Harvey was concerned that the user would lose // etc...
their bearings and become frustrated. He wanted to );
maintain some sort of map back to home at all times. $sbc = &new SimpleBreadcrumb($crumb_label_map);
Breadcrumbs to the rescue! This is the easiest form of
breadcrumb to implement, because it is a more or less print $sbc->get_output();
static structure. Sure, Harvey may add images to his ?>

February 2003 PHP Architect www.phparch.com 12


FEATURES Time Travel - Breadcrumbs and History with PHP

returned to the caller. Figure G contains the code to Harvey is happy with the default separator, so he does-
use this class for Harveys site. Harvey simply includes nt pass any in. Finally, he gets the output, and prints
this file at the point in his page where he wants the it. This is a pretty simple class, but it provides very
breadcrumb to appear, and hes in business. powerful functionality for a simple directory-oriented
The code is very simple. A label map is defined, and site such as Harveys.
the SimpleBreadcrumb class is instantiated using it. Although we know that this is a very simple example,

Figure F
1 <?php
2
3 // a simple breadcrumb class that works off of the directory
4 // names in the current script
5 class SimpleBreadcrumb
6 {
7 var $_separator = > ;
8 var $_label_map = array();
9
10 function SimpleBreadcrumb($label_map=null, $separator=null)
11 {
12 if (isset($separator))
13 {

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


14 $this->set_separator($separator);
15 }
16 if (isset($label_map))
17 {
18 $this->set_label_map($label_map);
19 }
20 }
21
22 function set_separator($separator)
23 {
24 $this->_separator = $separator;
25 }
26
27 function set_label_map($label_map)
28 {
29 $this->_label_map = $label_map;
30 }
31
32 function get_output()
33 {
34 // get script path, strip any trailing slashes, and
35 // split into constituents
36 // ex. /images/icons/gif
37 $dir = $_SERVER[PHP_SELF];
38 $current = basename(dirname($dir));
39 $dirs = split(/, $dir);
40
41 // base is built as we go through the dirs
42 $base = /;
43 foreach($dirs as $dir)
44 {
45 $base .= {$dir}/;
46 if (isset($this->_crumb_label[$dir]))
47 {
48 if ($dir == $current)
49 {
50 $crumbs[] = $this->_crumb_label[$dir];
51 }
52 else
53 {
54 $crumbs[] = <a href={$base}>{$this->_crumb_label[$dir]}</a>;
55 }
56 }
57 }
58
59 return join($this->_separator, $crumbs);
60 }
61 }
62
63 ?>

February 2003 PHP Architect www.phparch.com 13


FEATURES Time Travel - Breadcrumbs and History with PHP

lets mention a couple of its limitations.


First, it is dependent on a directory structure, which State and History
makes it very fragile. This class simply doesnt work if
your site is not laid out by directory, since thats where State can be defined as a unique snapshot that is
the breadcrumbs come from. quantified by values for a set of variables, characterizing
Second, it doesnt actually maintain any history. the web site for a period of time, and is different from
Although the URL in a site structured by directory can other states. This is rather like your checkbook. Right
generally be interpreted as the history (if your site is now, if it is balanced, you have a certain set of posses-
navigated linearly) the breadcrumbs are still built sions, and a certain amount of money left. Your check-
directly from it without any regard for querystring vari- book, and life, is in a particular state. Writing a check
ables, or the path actually taken. This severely limits will transition you to another state (hopefully not a
how flexible this class is.
broke one).
Lets leave Harvey to his questionable business ideas,
and move on to a more complex situation involving In order to get the information we need for the state,
(almost) everyones favorite relative. we must first ask the question: What identifies it? The
answer to that question depends on the way youve put
your site together, and may end up being a very com-
GrannysGiftGaggle.com plicated question to answer. Harvey used his directory
names to identify state, which was very simple. Granny
Granny Smith had a dream of being an online king- doesnt really have that luxury, so she will need to per-

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


pin. After some thought, she decided that the best way form a little more magic.
to begin her dynasty was to open an e-store, and wait While Granny doesnt have a physically hierarchical
for the riches to flood in. She even started buying system, we can see from the above transaction that she
pants with extra-big pockets, so shed have room for all does have a home directory, which appears to contain
the money she was going to make. a products directory and an estore directory. This allows
Her store, GrannysGiftGaggle.com is an emporium of us to use Harveys system for part of the breadcrumb
goodies, from health food to hinges. If you can buy it, generation. The rest of the breadcrumb will need to
you can buy it at Grannys. She has hundreds of cate- be created from the incoming querystrings.
gories and thousands of sub-categories. Lets examine Grannys breadcrumbs will be a little different, not
the structure a little further. only in method, but also in construction. Grannys
All activity on GrannysGiftGaggle.com comes breadcrumbs will aggregate themselves based on the
through specialized points of access, or controller users history, rather than building fresh on each page,
scripts, and as I mentioned earlier, there is no really like Harveys. The reason for this is that Harvey had all
structured physical underlay for Grannys site. She of the states built into each request (because of the
mostly relies on the data passed in on the querystring URL), whereas Granny doesnt. The subcategory page
to get the current state, which determines what to request contains no direct information about what
show. page it came from.
Since the querystring will determine the current The users history is just a stored collection of past
page, it might be educational for us to follow a simple states. As an example, the users session history, after
set of transactions on Grannys site, and see how peo- selecting the football category on Grannys site, would
ple navigate around in Figure H. contain the following states.

Disclaimer: This is obviously not intended to be a 1. Home no variables, no directories


tutorial on how to write a shopping cart applica- 2. Product Categories no variables,
tion, and is only a very rough example, for demon- products directory
stration purposes, of how an application like this 3. Football Sub-Categories category=football
might work. variable , products directory

This is starting to look suspiciously like a breadcrumb!


Now that weve got the view from 50,000 feet on Lets see the code to make it so, Number One. Its
how Grannys site works, we can move on to what we listed in Figure I.
need to make breadcrumbs work. Its ironic that this is the ComplexBreadcrumb class,
You may have seen some information in the links when the code is almost simpler than the
above that might be useful for generating bread- SimpleBreadcrumb class that we discussed earlier.
crumbs. This information will provide what bread- Regardless, Ill walk through this code. Again, I really
crumbs require: state. didnt need to use a class here, but there it is.
The purpose of the class is to store state history in the

February 2003 PHP Architect www.phparch.com 14


FEATURES Time Travel - Breadcrumbs and History with PHP

users session and allow retrieval of state in breadcrumb Once all of the parents have been processed, the
form. It has two member variables, $_separator, array of $crumbs is reversed because we built it right-
and $_current_state. $_separator again con- to-left, but want to display it left-to-right. Finally, the
tains the character used to delimit the breadcrumbs completed breadcrumb string is returned.
links, while $_current_state contains the label of Figure J shows a usage example of the
the current state. This label is used as a crude lookup ComplexBreadcrumb class. This file could be either
later. included or embedded in the code that handles the
The class also contains a very basic constructor, case where category is set, as in http://grannys-
which can optionally initialize the breadcrumb separa- giftgaggle.com/products/?category=foot-
tor at instantiation. A setter function is also provided ball.
for this purpose. The meat of the class is contained in This usage code instantiates our new class, and just to
the get_output() method. This method first checks to be different, changes the output separator to a forward
see if a state has been registered for the current page. slash. Since this is specific to the category state, it gets
If not, it returns an empty string. the category from the $_GET array, and sets the state
If get_output() did detect a current state, it makes a accordingly. Finally, it gets the output and displays it.
non-linked breadcrumb entry for it, and then proceeds I have no doubt that using a little ingenuity specific to
to process the parents of that state. For each parent, the situation, it would be possible to generalize this
the state variables are URL-encoded and links are pro- usage code and use it as a common include file, similar
duced. to Harveys.

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


Figure H
First the user navigates to the main page, where
1 http://grannysgiftgaggle.com/ they are presented with, among other things, a link to
the products section.
When the user navigates to the products section,
http://grannysgiftgaggle.com/prod-
2 they are presented with a number of product cate-
ucts/
gories

http://grannysgiftgaggle.com/prod- Once a category is selected, a list of subcategories


3
ucts/?category=football will appear.

http://grannysgiftgaggle.com/prod- After they select a category, a list of products


4
ucts/?subcategory=footballs appears.

Once a product is chosen, the user may browse


http://grannysgiftgaggle.com/prod- through any amount of relevant information about it,
5
ucts/?product_id=A1234 including the description, testimonials, specifications
and supporting material.

https://grannysgiftgaggle.com/estor Once the user is happy with the product, they may
6
e/?cart=add&prod_id=A1234 choose to add it to the cart.

https://grannysgiftgaggle.com/estor If no other products are wanted, the next logical


7
e/?cart=checkout step is to check out.

https://grannysgiftgaggle.com/estor The checkout is multi-stage. First they must enter


8
e/?cart=checkout&subpage=shipping their shipping address, and pick the delivery method.

https://grannysgiftgaggle.com/estor
9 Second, they enter their billing information.
e/?cart=checkout&subpage=billing
https://grannysgiftgaggle.com/estor
10 Then they confirm the order.
e/?cart=checkout&subpage=confirm
https://grannysgiftgaggle.com/estor
11 And lastly, they get the receipt
e/?cart=checkout&subpage=receipt

February 2003 PHP Architect www.phparch.com 15


FEATURES Time Travel - Breadcrumbs and History with PHP

Figure I

1 <?php
2
3 class ComplexBreadcrumb
4 {
5 var $_separator = ' > ';
6 var $_current_state;
7
8 // constructor
9 function ComplexBreadcrumb($separator=null)
10 {
11 if (isset($separator))
12 $this->set_separator($separator);
13 }
14
15 function set_separator($separator)
16 {
17 $this->separator = $separator;
18 }
19
20 // set a state/crumb
21 // - 'parent' is the parent label (allows chaining)
22 // - 'label' is the state/crumb label you are adding

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


23 // - 'vars' is an array of name-value pairs (usually from $_GET) that
24 // identify the state/crumb
25 function set_state($label, $parent='home', $vars=array())
26 {
27 $_SESSION['_crumbs'][$label] = array( 'label' => $label,
28 'path' => $_SERVER['PHP_SELF'],
29 'vars' => $vars,
30 'parent' => $parent,
31 );
32 $this->current_state = $label;
33 }
34
35 function get_output()
36 {
37 // no current crumb means no crumb display on the page
38 if (! isset($this->current_state))
39 return '';
40
41 $crumbs = array();
42
43 // don't make a link from the current state
44 $crumbs[] = $this->current_state;
45
46 // get the current state's parent and set the new current
47 $parent = $_SESSION['_crumbs'][$this->current_state]['parent'];
48 $current = $_SESSION['_crumbs'][$parent];
49
50 // do all parents
51 do
52 {
53 foreach ($current['vars'] as $name=>$value)
54 $values[] = urlencode($name) . '=' . urlencode($value);
55
56 $crumbs[] = "<a href='{$current['path']}?" . join('&', $values) . "'>"
57 . "{$current['label']}</a>";
58 // get the current state's parent and set the new current
59 $parent = $_SESSION['_crumbs'][$this->current_state]['parent'];
60 $current = $_SESSION['_crumbs'][$parent];
61 }
62 while ($current['label'] != 'home'))
63
64 // reverse the array (we built it in reverse)
65 krsort($crumbs);
66
67 return 'back to ' . join(" {$this->separator} ", $crumbs);
68 }
69 }
70
71 ?>

February 2003 PHP Architect www.phparch.com 16


FEATURES Time Travel - Breadcrumbs and History with PHP

You might notice that, with some effort, this more Figure J
complex version of breadcrumbing would work for <?php
Harvey as well. For his situation, the first method is ~
more than adequate, and will keep things simple, but // simple example of using ComplexBreadcrumb class
this would certainly do the trick, too. $cbc = &new ComplexBreadcrumb( / );

// get category name


Limitations $category = $_GET[category];

$cbc->set_state({$category} categories,
Although these examples will cover help with the
product categories,
majority of applications, they still dont address a few array(category=>$_GET[category])
things: );
Using images as separators is not supported. This
print $cbc->get_output();
would be a very simple extension, and would really
only involve the addition of a parameter to the contruc- ?>
tor, an extra setter method, and the extra logic needed
to add an <img> tag, rather than a symbol. your state minimally, then you are a big step closer to
Saving $_POST variable state is also not supported. having the information you need.
This is a very simple modification, requiring only that We introduced the concept of state history here,
you change $_GET to $_REQUEST. Note that storing which has a number of other uses as well. Some of

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


and later resubmitting $_POST values is generally con- these uses include preventing form resubmission, path
sidered a bad idea, though, since they usually come breadcrumbs (remember those?), and in-stream
from submitted forms, and can have nasty side-effects authentication. In-stream authentication refers to a sit-
(like attempting to re-insert entries to your database uation in which you click a link, are asked to authenti-
tables) if you arent careful. cate, and then proceed to that links location directly,
If you are browsing Grannys site, and you navigate to which is a very nice usability feature.
two different products in two different categories, you
may find problems with old history. If you use your Wrapping Up
browsers back button to back out of the second prod-
uct all of the way back to the first product and refresh, Boy, weve come a long way! I hope these examples
the categories link in the breadcrumb may be incorrect. have helped to clear the mud. As with just about any-
This is because each state overwrites any state with the thing in this world, there are a million ways to imple-
same name. This could be handled by further identify- ment breadcrumbs. I cant possibly attempt to cover
ing state using sequences or other methods, but you every base, but I hope that Ive been able to provide
will need to determine whether this is really an impor- some insight into this topic, and provide some useful
tant feature, or just fluff. code to work with.
In the complex example, jumping in from a search
engine, or otherwise browsing in a non-linear fashion is
not supported. Because the breadcrumbs are built
References
incrementally, you would have some difficulty building
a dynamic system like this that responded well to that
Those who cannot remember the past are
situation. Two solutions come to mind:
condemned to repeat it.
Life of Reason, Reason in Common Sense,
1. default crumbs
Scribners, 1905, page 284
2. redirection
Definition of state
Default crumbs means that if history cannot be http://gd.tuwien.ac.at/systeng/bah
found, default crumbs will be inserted in their place.
ill/Definitions.html
Redirection means that if a page is navigated to that
shouldnt be, the user would be redirected back into
Breadcrumb concepts
the linear path. Both of these are reasonable solutions http://keith.instone.org/breadcrumbs/
in some situations. What you do in your situation is up
to you.
Applications that use very complex messaging and php|a
event systems may have difficulty fitting into the mold Peter James is a developer and team lead working in Edmonton, Alberta,
that breadcrumbs often require. Remember, if you face Canada. In his spare time he tries to magically juggle his family, free-
this task, the fundamental question is what defines lance work, tool development and learning. You can reach Peter at
your state at any given moment?. If you can define petej@shaman.ca.

February 2003 PHP Architect www.phparch.com 17


FEATURES

The Object Oriented


Evolution of PHP
FEATURES

By Zeev Suraski

One of the key ingredients in the upcoming version 5 of PHP will be the Zend Engine 2.0, with support for a brand

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


new object-oriented programming model. This article describes the evolution of the object-oriented programming sup-
port in PHP, covering the new features and changes that are scheduled for PHP 5.

Where did it all start? as it existed at the time - in fact we know they liked it.
But as the authors of the engine we knew what was
Few people know this, but when PHP as we know it going on under the hood and we couldnt live peace-
today was being molded, back in the summer of 1997, fully with that. The rewrite, which was later dubbed
there were no plans for it to have any object-oriented the Zend Engine (Zend being a combination of Zeev
capabilities. Andi Gutmans and I were working to cre- and Andi), initiated and became one of the core com-
ate a powerful, robust and efficient Web language ponents of the 2nd revolution that PHP experienced in
loosely based on the PHP/FI 2.0 and C syntax. As a just over a year.
matter of fact, we got pretty far without having any This revolution, however, left PHPs object model
notion of classes or objects it was to be a purely struc- mostly unchanged from version 3 it was still very sim-
tured language. One of these summer nights however, ple. Objects were still very much syntactic sugar for
on August 27th that year, this changed. associative arrays, and didnt offer users too many addi-
At the time classes were introduced to the code base tional features.
of what was to become PHP 3.0, they were added as
syntactic sugar for accessing collections. PHP already Objects in the old days
had the notion of associative arrays collections, and the
new critters were nothing but a neat new way of So, what could one do with objects back in the days
accessing them. However, as time has proven, this new of PHP 3.0 or even with the current version of PHP 4.0?
syntax proved to have a much more far-reaching effect Not that much, really. Objects were essentially contain-
on PHP than originally intended. ers of properties, like associative arrays. The biggest
Another thing that most people dont know is that by difference was that objects had to belong to a class.
the time PHP 3.0 came out officially in mid-1998 and Classes, as in other languages, contained a collection of
was gaining momentum at a staggering rate, Andi properties and methods (functions), and objects could
Gutmans and I were already determined to rewrite the be instantiated from them using the new operator.
language implementation. Users may have liked PHP Single inheritance was supported, allowing users to

February 2003 PHP Architect www.phparch.com 18


FEATURES The Object Oriented Evolution of PHP

extend (or specialize) the scope of an existing class (The implementation of Woman::setHusband(),
without having to write it from scratch or copy it. Man::setWife() and areMarried() is left as an
Finally, PHP 4.0 also added the ability to call methods exercise for the reader).
of a specific class, both from within and outside object What will areMarried() return? We would hope
contexts. that the two newlyweds would manage to stay married
One of the biggest twists in PHPs history was the fact at least until the following line of code, but as you may
that despite the very limited functionality, and despite have guessed they wouldnt. areMarried() will
a host of problems and limitations, object oriented pro- confirm that they got divorced just as soon as they got
gramming in PHP thrived and became the most popu- married. Why?
lar paradigm for the growing numbers of off-the-shelf The reason is simple. Because objects in PHP 3.0 and
PHP applications. This trend, which was mostly unex- 4.0 are not special, and behave like any other kind of
pected, caught PHP in a sub-optimal situation. The fact variable, when you pass $joanne and $joe to wed(),
that objects were not behaving like objects in other OO you dont really pass them. Instead, you pass clones or
languages, and were instead behaving like associating replicas of them. So, while their clones end up being
arrays was beginning to show. married inside wed(), the real $joe and $joanne
remain within a safe distance from the sacrament of
The limitations of the holy matrimony, in their protected outer-scope.
old Object Model Of course, PHP 3 and 4 did give you an option to
force your variables to be passed by reference, conse-

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


The most problematic aspects of the PHP 3 / PHP 4 quently allowing functions to change the arguments
object model was the fact that objects were passed that were passed to them in the outer scope. If we
around by value, and not by reference. What does that defined wed()s prototype like this:
mean?
Lets say you have a simple, somewhat useless func- function wed(&$bride, &$groom)
tion, called myFunction():

then Joanne and Joe would have had better luck (or
function myFunction($arg) not, depending on your point of view).
{
$arg = 5;
However, it gets more complicated than that. For
} instance, what if you want to return an object from a
function, by reference? What if you want to make
And you call this function:
$myArgument = 7;
modifications to $this inside the constructor, without
myFunction($myArgument); worrying about what may happen when it gets copied
print $myArgument; back from news result into the container variable?
Don't know what I'm talking about? Say hallelujah.
While PHP 3 and 4 did address these problems to a
As you probably know, the call to myFunction() certain extent by providing syntactic hacks to pass
will leave $myArgument unchanged; Sent to around objects by reference, they never addressed the
myFunction() is a copy of $myarguments value, and core of the problem:
not $myargument itself. This type of argument pass-
ing is called passing arguments by value. Passing argu-
ments by reference is done by most structured lan- Objects and other types of values are not
guages and is extremely useful, as it allows you to write created equal, therefore, Objects should be
your functions or call other peoples functions without passed around by reference unless stated
worrying about side effects they may have on variables otherwise.
outside their scope.
However, consider the following example:
The Answer Zend Engine 2
function wed($bride, $groom)
{ When we were finally convinced that objects are
if ($bride->setHusband($groom)
&& $groom->setWife($bride)) {
indeed special creatures and deserve their own distinct
return true; behavior, it was only the first step. We had to come up
} else { with a way of doing this without interfering with the
return false;
rest of the semantics of PHP, and preferably, without
}
} having to rewrite the whole of PHP itself. Luckily, the
wed($joanne, $joe); solution came in the form of a big light bulb that
print areMarried($joanne, $joe); emerged above Andi Gutmans head just over a year

February 2003 PHP Architect www.phparch.com 19


FEATURES The Object Oriented Evolution of PHP

ago. His idea was to replace objects with object han- object model. Some of the features further enhance
dles. The object handles would essentially be numbers, object-oriented capabilities, such as private member
indices in a global object table. Much like any other variables and methods, static variables and language-
kind of variables, they will be passed and returned by level aggregation. Most notable is the revolutionized
value. Thanks to this new level of indirection we will interaction with external component models, such as
now be moving around handles to the objects and not Java, COM/DCOM and .NET through overloading.
the objects themselves. In effect, this feature means In comparison to the Zend Engine 1 in PHP 4.0,
that PHP will behave as if the objects themselves are which first introduced this sort of integration, the new
passed by reference. implementation is much quicker, more complete, more
Lets go back to Joe and Joanne. How would wed() reliable and even easier to maintain and extend. This
behave differently now? First, $joanne and $joe will means that PHP 5.0 will play very nicely in your exist-
no longer be objects, but rather, object handles, lets ing Java or .NET based setup, as you will be able to use
say 4 and 7 respectively. These integer handles point to your existing components inside PHP transparently, as
slots in some global objects table where the actual if they were regular PHP objects. Unlike PHP 4.0, that
objects sit. When we send them to wed(), the local had a special implementation for such overloaded
variables $bride and $groom will receive the values 4 objects, PHP 5.0 uses the same interface for all objects,
and 7; setHusband() will change the object refer- including native PHP objects. This feature ensures that
enced by 4; setWife() will change the object refer- PHP objects and overloaded objects behave in exactly
enced by 7; and when wed() returns, $joanne and the same way.

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


$joe will already be living the first day of the rest of Finally, the Zend Engine 2 also brings exception han-
their lives together. dling to PHP. To date, the sad reality is that most devel-
opers write code that does not handle error situations
What does that mean gracefully. Its not uncommon to see sites that spit out
to end-users? cryptic database errors to your browser, instead of dis-
playing a well-phrased An error has occurred kind of
Alright, so the ending to the story is now more idyl- message. With PHP, the key reason for this is that han-
lic, but what does it mean to PHP developers? It means dling error situations is a daunting task you actually
quite a number of things. First, it means that your have to check for the return value of each and every
applications will run faster, as there will be much less function. Since set_error_handler() was added,
data-copying going around. For instance, when you this issue became slightly easier to manage, as it was
send $joe to a function, instead of having to create a possible to centralize error handling but it still left a
replica, and copy over his name, birth date, parents lot to be desired. Adding exception handling to PHP
name, list of former addresses, social security number will allow developers both fine-grained error recovery,
and whatnot PHP will only have to pass on one object but more important it will facilitate graceful applica-
handle, one integer. Of course, a direct result of this is tion-wide error recovery.
also a significant amount of memory savings storing
an integer requires much less space than storing a full- Conclusion
fledged replica of the object.
But perhaps more important, the new object model The release of PHP 5.0, powered by the Zend Engine
makes object oriented programming in PHP much 2.0, will mark a significant step forward in PHPs evolu-
more powerful and intuitive. No longer will you have tion as one of the key Web platforms in the world
to mess up with cryptic & signs in order to get the job today. While keeping its firm commitment to users
done. No longer will you have to worry about whether who prefer using the functional structured syntax of
changes you make to the object inside the constructor PHP, the new version will provide a giant leap ahead for
will survive the dreaded new-operator behavior. No those who are interested in its object oriented capabil-
longer will you ever have to stay up until 2:00AM track- ities especially for companies developing large scale
ing elusive bugs! Ok, maybe Im lying with that last applications.
one, but seriously, the new object model reduces the
object-related stay-up-until-2:00AM type of bugs very
significantly. In turn, it means that the feasibility of
using PHP for large-scale projects becomes much easi- php|a
er to explain. Zeev has been working for over five years on the PHP project. Along with
Andi Gutmans, he started the PHP 3 and 4 projects and wrote most of
What else is new? their infrastructure and core components, thereby helping to forge PHP
as we know it today and attracting many more developers to join the
movement. Zeev is a co-founder and CTO of Zend Technologies Ltd, the
As one could expect, the Zend Engine 2 packs quite leading provider of development and performance management tools for
a few other features to go along with its brand new PHP-enabled enterprises. Zends website is www.zend.com.

February 2003 PHP Architect www.phparch.com 20


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

LogiCreate
Tap Internet
REVIEWS

LogiCreate is a web application framework, providing a common development base for web systems. By standardizing

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


development practices, developers can quickly create new web-based systems such as intranets, extranets, and other
database-driven applications.

A s a developer, I have always been used to building


most of my own websites from scratch, rather
than using something that other people have devel-
The Cost:
$1,495.00 - $4,950.00 (US)

oped. When we had to build the php|a site, we did- Requirements:


Any L.A.M.P. based architecture
nt even look at any other optionswe simply
designed the website the way we wanted it and put it Download Page:
LogiCreate
together. The reason is simple: after so many years of
being in this business, I have a pretty good under- LogiCreate Home Page:
http://www.logicreate.com
standing of how things work and can take care of
most things more quickly than I can learn how to use
a new product that would do the same in my place
(plus, lets face it, one always likes to think that his
own stuff is better than something an automated
script can generate).
Unfortunately, this approach does not always work. Internet for review this month. Now, its important to
Building things from scratcheven when it comes to understand that the version Tap sent us is a develop-
reusing some of your own code from previous proj- ment snapshot; as such, some of the kinks still need to
ectstakes time, and time is money. When budget- be worked out, but this really seems to affect only the
ary considerations must take precedence over person- installation part of the application, and for the rest I
al pride and the general laziness that all programmers cant really say that the product exhibits any major
sport, its necessary to look beyond ones personal war flaws. I wish our development snapshots were quite
chest and start from solid building blocks that take as good as this...
care of most basic (and some advanced) aspects of a
good website. What Is LogiCreate?
Its therefore with quite a bit of interest that I look at
the LogiCreate product that we received from Tap At the most basic level, LogiCreate is a web applica-

February 2003 PHP Architect www.phparch.com 22


REVIEWS

tion framework. It provides a set Figure 1


of modules that can be integrat-
ed into a single website, provid-
ing functionality like content
management, an ad placement
system, an FAQ, a search func-
tion, and more. If this sounds
like something that youve
already heard, I urge you to read
onI had exactly the same
impression at the beginning, but
changed my mind as I explored
things in a bit more detail.
As with other similar systems,
an entire LogiCreate application
can be managed through an
easy-to-use web-based interface.
Unlike other similar systems,
LogiCreate takes a security-cen-

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


tric approach to the entire appli-
cation. Most functions can only
be managed by users who have
the proper permissions, and its
possible to push almost all data
that is published through the
website through an approval
process to provide complete
control over it.
Whats more, unlike most con-
tent management systems and
web application frameworks,
LogiCreate seems to have been
built with the understanding that
access the database. Although our version only came
a developer might want to work with something more
with a driver for MySQL, the entire data access layer is
than the templates offered by the framework. As
abstracted, and its actually possible to extend it to
such, the creation of custom templates and custom
support other database systems.
scripts is not only supported, but also encouraged as a
The installation process also includes a post-setup
way to provide a more flexible environment for the
phase in which the user is required to adjust the per-
programmer.
missions of the files that have been created. Although
this requires only four simple shell commands, it could
The Installation Process
certainly have been automated.
The simplicity of the installation process, even with
As I mentioned above, this is the only area where the
its manual portion, is somewhat staggering. As you
version of LogiCreate that we received still needs a sig-
can see from Figure 2, LogiCreate is a complex piece
nificant amount of work. The creation of a new
of software that includes a large number of modules.
LogiCreate website takes place through a simple two-
For anyone who has ever tried to create a new online
step process in which the user is asked to enter only a
store using Microsoft Commerce Server, the two-step
few parameters, like the sites name and database
approach adopted by this application framework is
information (Figure 1). Here, my use of Marcos Site
just plain great.
as the name of the application caused a bit of a hic-
cupthe single quote was not escaped and ended up
LogiCreate Modules
in a constant definition, thus confusing the PHP inter-
preter, which threw an error when I subsequently tried
A total of twelve different modules were packaged
to open my website. Note that the installation script
with the version that we received, with functionality
also asks for the database driver that should be used to
ranging from a welcome page to a complete con-

February 2003 PHP Architect www.phparch.com 23


REVIEWS

tent management system. Figure 2


The modules can be adminis-
tered through the Hercules
Control Center (affectionately
referred to as Herc), a backend
web-based application that
allows you to control the individ-
ual settings of each aspect of your
website. As I mentioned earlier,
Herc provides a very well-
thought-out user management
system, complete with grouping
and permissions at both the user
and group level. Thus, for exam-
ple, if I want to publish a new
news item and type it into the
system (Figure 3), it will be
parked into a special repository
until it can be verified and

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


approved by someone who has
the correct permissions to do so.
Herc also provides a complete
statistical system that can be used
to create reports on the kind of
traffic that your site receives
(Figure 4). Although this tool
does not provide the level of
sophistication that a more spe-
cialized web analysis package
offers, its still very valuable,
because it produces reports in
realtime, rather than on a specific
schedule.

Expandability A non-technical person who is


How good is an application framework if it cant be just looking for a quick solu-
expanded to meet your actual requirements?
Naturally, it depends: a non-technical person who is
tion to his problems probably
just looking for a quick solution to his problems prob- wont care that he can't mess
ably wont care that he cant mess around with the
source code. On the other hand, a developer who is around with the source code.
looking for a framework on which to build his website
will. This, however, does not prevent you from imple-
LogiCreate is clearly slanted towards the latter menting your own PHP functionality inside a particu-
approach. The systems templates are really just PHP lar template. In fact, thats the whole point of
scripts that make use of the various classes and meth- LogiCreates philosophy: the modules, classes and
ods made available by each of the applications mod- procedures that it provides are just there to help you
ules. This, in my opinion, is the way it should be by providing a simple framework on which you can
after all, PHP provides an excellent development envi- build your own specialized functionality.
ronment, and there simply is no way that any other
artificial templating system can match its functional- Documentation
ity. The core of LogiCreate, therefore, relies heavily on
code reusability and, as such, it is primarily based on This is probably another area in which LogiCreate
OOP. needs a bit of help. The user documentation, com-

February 2003 PHP Architect www.phparch.com 24


REVIEWS

posed primarily of Hercs user manual, is of excellent The Big Picture


quality and very clear. For some reason, however, it is
distributed separately from the Herc interface itself. From a purely technical perspective, I am pleasantly
This makes it difficult for a user to get help from the surprised by how much LogiCreate manages to
system, and risks negating the advantage of having an accomplish without being over-complicated or requir-
interface like Herc in the first place. If I were to devel- ing a week of reading in order to get the proverbial
op a production system based on LogiCreate, inte- Hello, World! message to appear on the screen.
grating the help in the Herc web pages would certain- From a business point of view, LogiCreate is not
ly be on my to-do list. cheap, but given its characteristics can provide
The developers manual is a bit sparse, but otherwise tremendous value if you consider how long develop-
functional enough to be quite complete. It includes ing the functionality that it provides would take. The
information on how to create new templates and how product is priced at a base of $1,495.00 (US) per serv-
to modify (or integrate with) the application frame- er, plus $500.00 (US) for each additional module that
work itself. Mercifully, this is not a difficult task to you decide to use.
accomplish by an intermediate-level developer. My Surely, other inexpensive systems provide similar
only real gripe about the docs is that they contain very frameworks, but its really difficult to find something
few examples; my experience is that two lines of code that is quite as simple to understand and master as
can easily explain what ten lines of text cant, so I real- LogiCreate. Overall, if youre building a new applica-
ly hope that the Tap team will add more examples and tion and are looking for a framework to take care of

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


sample code to their documentation. the gritty work for you, Id recommend you take a
look at this system.
php|a

Figure 3

February 2003 PHP Architect www.phparch.com 25


REVIEWS

Figure 4

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

February 2003 PHP Architect www.phparch.com 26


FEATURES

PHP in an N-T
Tiered
World
FEATURES

By Dave Palmer

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


In today's enterprise, many applications are coded by roomfuls of Java programmers, and deployed to a middle tier
Java container (read: application server). This article introduces you to some advanced techniques in allowing PHP to
interact with EJBs and other layers of your n-tiered model, in order to provide a front end interface to the application.

like scalability and flexibility start to take a back seat to


I f I had to take a guess, I would say someone on our
development team asks "so Dave, why can't our
[application server that rhymes with "confusion"] tem-
the infectious mantra of "just get it working", which
invades many a development project.
plates just directly query the database?" at least 5 or 10 If the above scenario strikes a little too close to home
times a day (maybe that's just a slight exaggeration). I for you, have no fear! The good news is that because
then feel compelled to explain the concept once again, PHP is so accommodating in its openness and its abili-
spouting trade-rag-babble about how great n-tiered ty to work happily with other technologies, PHP is quite
architectures are, and the benefits in doing things the comfortable fitting in with an n-tiered architecture. I
n-tiered way. hope this article will equip you with at least enough
The developer usually just nods his head humoring background knowledge to get you started with this
me, then walks away reminiscing of the good old days invaluable development/design technique.
when they could just throw everything (including the Before I go on any further, perhaps some explanation
kitchen sink) into their code and be done with it. But of what I mean by 'n-tiered' would be helpful. "Tiers"
alas, the logical conclusion is reached all the same that are like layers. Pardon the mixture of metaphors, and I
n-tiered architectures lend themselves perfectly to know just how tired metaphors are when trying to
applications that must scale and be flexible. describe anything in the application development
If you look at your PHP applications you'll notice a world, but to think of a program as being comprised of
common pattern. This pattern includes things like con- "layers" is a useful device in clarifying what I'm talking
necting to and querying a database, performing logic about. Each "layer" or tier represents a common set of
on data, and presenting that data back to the user so functionality. These layers/tiers in your typical PHP
that the user can act (or not act) on what was present- application are "coupled", meaning that your presenta-
ed. For those of you who have built complex PHP
applications I'm sure that you've noticed that your code REQUIREMENTS
becomes, well... complex. Maintaining it becomes PHP Version: 4.1.0 or Above
rather difficult, if not impossible, especially after taking O/S: Any
a break from it for a few days or even hours. You'll also Additional Software: Java SDK 1.4.x AND Java J2EE
notice that performance starts to degrade and ideals 1.4.x, JBoss 3.0.4, MySQL

February 2003 PHP Architect www.phparch.com 27


FEATURES PHP in an N-Tiered World

tion layer is tied to your business logic layer which is in Finally, there's the Database Layer. This tier is the
turn tied to your data layer. It's not unlike the days end of the line, or beginning, depending on how you
when the client/server model was king. Your client is a look at it. This tier represents our data storage tier.
"fat client", containing all of the code needed to not Each tier has a very specific purpose and depending
only display a nice UI, but also to access and manipu- on the complexity of your application each of these
late the data! Everything (including that kitchen sink) is general tiers may be broken up into more specific tiers.
stuffed into the client, and the server is nothing more For example, the Business Logic tier may be comprised
than a data store. This client/server model is still alive of business rules that utilize messaging for systems inte-
and well today in many Web applications. Even though gration, or Data Access tiers may be broken up into
the web clients are a bit thinner (because the browser separate sub-tiers to handle legacy data sources and
takes care of some UI details), you still notice that LDAP or other directory services which also supply data
everything is still crammed into that client tier. in some form.
Now, along comes the n-tiered architecture, which In this example I am using PHP as the presentation
spreads things out and creates more room for every- layer. The presentation layer is responsible for facilitat-
thing by encapsulating these tiers into separate and ing the actions or events that occur in an application.
identifiable entities. The "n" in n-tiered is simply the You'll notice in the example that the PHP code does not
mathematical notation for "unknown", meaning that contain any database calls, nor does it contain any busi-
using this type of architecture assumes nothing about ness logic. The reason is that PHP, in this example, is
what is required in order to implement the application responsible for presenting data and enabling interac-

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


you must develop. Essentially, you are no longer con- tion between the user and the data. Nothing more,
fined to a stringent implementation model and are free nothing less.
to scale and grow as the application and requirements The "middle tier," the tier that contains the business
demand. With the n-tiered framework, the application logic and data access tiers, will be implemented using
can scale without the pain of having to re-engineer an EJB (Enterprise Java Bean) in JBoss using the "session
everything each time growth is necessary. facade" design pattern. Using this particular design
N-tiered applications are typically broken up into dis- pattern in the J2EE space means we can further sepa-
tinct units of work (tiers) and generally fall into these rate our middle tier code into clear distinctions
categories: between controller (the EJB) and data access (Data
Access Object). J2EE (Java 2 Enterprise Edition) has
Presentation become (or is becoming) the defacto standard technol-
Business Logic ogy for the middle tier because of Java's wide accept-
Data access ance on most platforms and because Java's object ori-
Database ented design makes modeling business processes much
simpler. However, I must digress, as this is not an arti-
The Presentation Layer is often referred to as the cle about the fundamentals of EJB, object oriented
"web tier." This tier contains all of the code to display design principles or JBoss. The code I have included
the user interface. In our example, PHP running on should provide you with a good launching off point for
Apache represents our presentation or web tier. getting your feet wet in the middle tier.
The Business Logic Layer is often thought of as the I have chosen to use MySQL to represent the data
"middle tier". This tier is where the vast majority of tier. MySQL is a powerful and robust relational data-
business logic is implemented. I often think of this tier base which is free to use. This ease of accessibility,
as being pretty fuzzy in that it may be comprised of along with its compact, simple design makes MySQL
many smaller tiers, as is the case (to a lesser extent) in perfect for our informal example code. There are lots
this example. The primary business logic component, of other reasons to use MySQL in your applications but
in our example, is an EJB. But there is a clear distinction I don't want to get political on you, so on with the
between the EJB and the Data Access Object - the class show!
which is responsible for doing the "heavy lifting" - Lets get on with the example, which I think clearly
which leads us into the next tier. describes this n-tiered architecture and also clearly illus-
Data access - This tier is often part of the middle tier, trates the advantages of this type of architecture. The
but is clearly separate from the business logic tier in small application I am using to illustrate this concept is
that its primary responsibility is in establishing connec- a very simple user management program that enables
tions to data stores and querying those data stores. In you to add and edit a user account. Keep in mind that
our example, this is done with the Data Access Object, this is merely an example and is used only to illustrate
which is a Java class running in the J2EE space which how PHP lends itself well to n-tiered applications. I
sends queries to the MySQL database and returns data eagerly invite you to take this code and run with it, and
back to the EJB (the main component of our business turn it into something useful.
logic layer, as noted above). First things first. You will need PHP (obviously) and

February 2003 PHP Architect www.phparch.com 28


FEATURES PHP in an N-Tiered World

you will need to configure PHP with Java support for we use the WDDX API to serialize and unserialize those
this to work. Next you'll need a copy of JBoss objects into arrays in PHP. Because we are dealing with
(http://www.jboss.org). JBoss is an open source disparate application platforms (PHP and Java) we need
J2EE application server. Configuring JBoss can be a bit to pass complex objects from one platform to the next
confusing for those new to the whole Java application and WDDX satisfies the requirements in this area rather
server environment, but there are some excellent nicely.
resources such as the JBoss discussion forums and the Let's start looking at our PHP code in the presentation
Getting Started PDF that can get you up and running tier.
rather quickly. Next, you'll need MySQL The interesting stuff starts on line 24 (index.php is
(http://www.mysql.org). MySQL is rather simple included in this months package). Because we are
to setup and configure - just make sure everything is using an EJB to serve as our business logic - or "middle"
running on your local host for this first example. Once tier, we need to establish three variables so that our EJB
you get more comfortable and have the machines to client class can locate our EJB remote reference. We
do so, I would recommend spreading things out a bit communicate with our EJB through a remote method
so you can really see how n-tiered architectures enable invocation protocol, and through this protocol our
logical scaling. business methods are said to be "exposed" through
Once you have JBoss and MySQL set up, configured what is referred to as a "remote interface."
and running, you'll need to run the SQL scripts provid- $factory represents the naming factory interface
ed in the source code for this article (shown in listing JBoss uses to instantiate the naming service. $url

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


1). Basically you just need a database with a single points to where JBoss is running, and also indicates
table. You can use phpMyAdmin which remote method invocation protocol we'll be
(http://www.phpmyadmin.net) to run the SQL using. $jndiName is the name of the reference in
scripts. I used phpMyAdmin to generate them, so you JBoss's naming service which points to the actual bean's
should have no trouble getting them to work. "home" interface. It is through the "home" interface
Next, you'll need to create a data source for JBoss that that we create an instance of our remote object. I
maps to your MySQL database server and database. I would recommend checking out
provided my XML configuration files, which I use in my http://java.sun.com/products/ejb/ to learn
own JBoss distribution to help give you a leg-up in get- more about EJB.
ting this running. Just open up the phpa_mysql- Once we have our EJB "environment" established, we
service.xml file and make the modifications to the want to create a Java object in our PHP script. The
user name and password settings. You'll also need to object we are creating is an EJB client object. This client
change the login-config.xml file. Please read the is a simple Java class that establishes the connection
README file I included. It should make getting config- between the PHP application and the EJB.
ured and running much simpler. We first create the object:
Yes, I am the first to admit that doing this type of
development does have a bit of a steep learning curve, $java_obj = new Java
but once you have gotten comfortable with the whole ("org.ew.phpa.User.clients.UserClient");
notion of application servers and the configurations
surrounding them, all of this will make perfect sense
and become second nature. Now that we have an object reference for our EJB
As part of the source code package you'll notice a client, we can issue method calls against our
Java JAR file. This JAR file contains the EJB classes you $java_obj. The first method call we need to issue is
need to deploy (place into the 'deploy' directory in the an init() call. The UserClient class contains an
JBoss server). You'll also want this JAR file in PHP's class init() method that performs the initial context
path. This JAR file contains all of the required class files lookup of the EJB home reference and creates the
of the EJB and the client application your PHP program remote object reference.
will use in order to "talk" to the EJB (running in JBoss).
Before we get too much into the source code, take a $java_obj->init($factory,$url,$jndiName);
look at figure 1. This is a simplification of the design of
the User Management application we'll be looking at. Okay, so we've gotten all of the boring environmen-
There are some important concepts illustrated in this tal and initialization stuff over with and out of the way.
figure, one being that the presentation layer does not Now we can have some fun!
directly call the middle tier (EJB) but uses a client To keep things in a single PHP script, I stuck all of the
object to perform the work required to instantiate the functions of this user management application into a
EJB and to pass user input into the middle tier. single index.php file. The main functionality is repre-
In this example, I use the Java Collections API (Vectors sented by the following PHP functions:
and Hash tables) to pass data back and forth because

February 2003 PHP Architect www.phparch.com 29


FEATURES PHP in an N-Tiered World

Listing 1
saveChanges
printNewUserForm # Connection: Obione
printEditUserForm # Host: obione
printEntry # Saved: 2003-01-10 20:15:54
#
# Host: obione
Let's start at the very beginning. In the # Database: PHP_Articles
printEntry() function (which is displayed if you've # Table: users
#
just accessed index.php for the first time). CREATE TABLE `users` (
We first start with this: `user_id` int(11) NOT NULL auto_increment,
`username` varchar(20) NOT NULL default ,
$wddx = $java_obj->getUsers(); `password` varchar(100) NOT NULL default ,
$user_array = wddx_deserialize($wddx); `email` varchar(50) NOT NULL default ,
`firstname` varchar(50) NOT NULL default ,
`lastname` varchar(50) NOT NULL default ,
PRIMARY KEY (`user_id`)
You'll notice we call a method in our $java_obj
) TYPE=MyISAM;
(which if you remember is a reference to our EJB client

Figure 1

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

February 2003 PHP Architect www.phparch.com 30


FEATURES PHP in an N-Tiered World

class). In our EJB client, we have a method called returned by our EJB, through our EJB client which, for
getUsers() which takes no parameters and returns a this example, just says something like "hey, user infor-
WDDX string. This getUsers() method in our EJB mation was saved.". In a real application it's usually a
client maps directly to our remote EJB method called good idea to pass back a real status object with error
getUsers(). codes, meaningful messages and things of that nature.
The WDDX string is then deserialized (using The last thing we should take a look at is the
wddx_deserialize($wddx)) into a PHP array. Now printEditUserForm() function. You'll notice this
that we have our data back from our database (via our line of code:
middle tier) we are now ready to do something with it!
$userHash = wddx_deserialize(
for ($i = 0; $i < count($user_array); $i++) $java_obj->getUser($uid)
{ );
$uid = $user_array[$i]["user_id"];
$username = $user_array[$i]["username"];
echo '<option value="'.$uid.'">' With this one line we call the other business method
.$username.'</option>';
} in our EJB (via our EJB client class) called getUser().
This method takes a single parameter - our user ID.
getUser() returns a WDDX string which deserializes
Here we are looping through the array, printing indi- into a PHP associative array. This array contains all of

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


vidual <OPTION> tags for a <SELECT> widget. Our our columns in our database with the values based on
array contains two keys; user_id and username, the user ID we passed into our method.
which represent the primary data elements needed in There, was that so bad? We went through all of the
order to paint this entry screen. important bits of code to illustrate how we separate our
Now that we have our entry screen displaying a list of application into logical tiers. I hope you noticed that in
users, we also have an add and an edit button. If we our PHP code we did not rely on database calls or per-
click the add button we just get a nice little HTML form form any business logic. Our PHP code just carried out
where we can fill in a user name, password, email the task of preparing, sending and displaying data.
address, first name and last name. We have a method Something I also hope you noticed with this small
called saveChanges() we'll use as our action() example is how our application code can directly map
function, which will be responsible for inserting or to a requirements document. In any major application
updating our database. We'll use the hidden form vari- development project, requirements and design docu-
able user_id as a flag to help us determine what ments play a very large role in the actual project. In
action we need to take (if user_id is 0, then we are fact I'd go so far as to say that they may be one of the
inserting. Otherwise, we are updating). most important components of a project. Using an n-
Taking a look at saveChanges() you'll notice we tiered architecture, whoever is wearing the project
need to first prepare our form data by serializing it in a manager's hat can make dividing up the tasks amongst
WDDX packet: the specialists more clear, and can produce real esti-
mates on the effort required to build each component.
$packet = wddx_serialize_vars("username", Of course this small example hardly scrapes the sur-
"password", "firstname", "lastname", face of what is possible and it only really describes one
"email", "user_id"); method of implementation for an n-tiered architecture.
There are other technologies and protocols available
like SOAP and XML-RPC that can be used to divide your
Once our data is serialized into a WDDX packet we application into the multiple tiers.
can call our EJB client: So, leave the kitchen sink right where it is - you don't
need it in your code. Keep your presentation code free
$status = $java_obj->saveUser($packet); of business logic which, in turn, should be kept free of
your data access code. You'll be happier, accomplish
more and be admired by peers!
You'll notice our friend $java_obj. In our EJB client
class we have a method called saveUser() which
accepts a single string parameter. This parameter is our
WDDX string. The EJB client converts (deserializes) the php|a
WDDX packet into a Java hash table. Our EJB client Dave is a professional geek specializing in java/j2ee, php (naturally), perl
then calls our EJB remote method saveUser() and development which is just a cover for his real passion for spending large
sums of money on home recording and musical equipment and general-
passes the deserialized Hash table to the EJB. ly making a nuisance of himself. it should also be noted that his /. karma
The $status variable is a simple string which is is currently "positive" which will surely fall.

February 2003 PHP Architect www.phparch.com 31


FEATURES

Creating a Customized
Template-E
Engine
FEATURES

By Andreas Demmer

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


Separating logic code from the presentation layer is a significant challenge in any large-scale PHP application develop-
ment project. This article aims to show how a template engine can help to overcome this challenge, and how to get
started in creating an engine of your own to meet the unique needs of any project.

So What Is A Template Engine impossible to debug and maintain, and as the applica-
Anyway? tion grows larger it begins to look more and more like
spaghetti-code. In addition, if you work on a team
In general, you could say that the purpose of a tem- where logic and design teams work separately, coordi-
plate engine is to bring certain dynamic content into a nation efforts become quite troublesome.
flexible, predefined design framework. The template If youve ever created large-scale applications with
engine we will implement later on in this article will your PHP code interspersed amongst your HTML,
replace substitution symbols in preformatted HTML youre probably well aware of the tremendous effort
documents with content delivered by PHP scripts. This involved in trying to decipher what you or (even
may sound a little bit unspectacular, but the engine is worse) someone else has written; if loops are frag-
quite smart and has a few other useful features. It mented by hundreds of lines of HTML code thats been
doesnt matter if your content consists of a single word wedged between the if and else statements; just as
or a whole list of datasets with several rows each. Even disconcerting, every line of HTML seems to have some
arbitrary interlocked datasets can be used to feed a small PHP snippet hidden within. These are the night-
template. In addition, SSIs (server side includes) will be mares of every PHP programmer tasked with the main-
processed automatically, even if the web server the tenance of such spaghetti-code, even if it was you
script is running on is not capable of processing SSIs. who implemented it in the first place! Thus, in order to
save your soul from this living hell, the pragmatic PHP
Clean Up Your Code! programmer really should be armed with some way of
creating cleaner, more maintainable, non-nightmare
There are several good reasons for using a template inducing code.
engine. Probably the most apparent is that it results in
clean and tidy code. While one of the greatest advan- REQUIREMENTS
tages of PHP is the ability to embed the PHP source
PHP Version: 4.0 and Above
right into your HTML documents, this mixture of pro-
O/S: Any
gramming and formatting languages is also one of the
Additional Software: N/A
greatest disadvantages. Eventually, the code becomes

February 2003 PHP Architect www.phparch.com 32


FEATURES Creating a Customized Template-Engine

Using templates can help put an end to this madness iar to programmers (its also shorter). Whatever you
by strictly separating PHP (logic) from HTML (design). decide to call them, just remember that they will be
Your code, now free of the extra HTML baggage, will replaced with dynamic content by the template
look more tidy and will be much easier to understand. engine.
But wait! Theres more! The separation of logic and Once youve decided to take the plunge into using a
design enables you to easily share work within a team. template-driven development model, several agree-
PHP programmers can focus on their code without ments between the template designers and the PHP
worrying about how to make the output look nice, coders have to be made. The most important areas to
while the HTML programmers can design complex lay- focus on are:
outs without having to understand how the content is
created. And there was much rejoicing (yay). - which template tags are needed to integrate the
necessary content?
Seems Logical, But How
Does It Work? - which HTML elements (e.g. dropdown fields or
radio buttons), if any, are dependent on PHP
Substitution symbols are positioned within the HTML code?
document to be used as placeholders for the dynamic
content, to be delivered later by PHP. In this article, I - how will form elements later referenced by PHP
will use the term tags rather than substitution sym- form processing scripts be named?

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


bols, simply because the term tag sounds more famil-

Figure 1

February 2003 PHP Architect www.phparch.com 33


FEATURES Creating a Customized Template-Engine

Table 1

read_file($filename) defines an existing file as template.

add_content($content, $name) assigns $content to the content tag $name.

parse() processes the template.

output() sends the template to the browser.

get() returns the template without sending it to the browser.

dump($filename) writes the template to disk as $filename.

get_content() returns assigned content for debugging.

debugging($switch) turns debugging features on or off.

The PHP script instantiates an object from the tem- hadnt thought about, or find solutions to existing
plate class and tells the object which template should problems, which can keep you from reinventing the

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


be used to display the content. It then builds an array wheel. To summarize: Collect the best, improve it if
filled with content and hands it over to the object. The possible and add the features that are missing or
template engine finds, evaluates and replaces the tem- unique to your environment/project.
plate tags with the corresponding content and stores
the result. Template tags without any corresponding Designing The Templates
data returned in the content array can be left empty or
visually marked for debugging purposes. Most designers will code the templates by hand but
Now the script tells the object how to deal with the use a WYSIWG editor to design and prototype them.
stored result: It can be Therefore, it would be practical if this editor could rec-
ognize the template tags and display them with a spe-
- sent to the browser which made the original cial symbol. This lets you rearrange them with the
request mouse and gives you a good preview showing where
- written to a file the dynamic content will appear in the final product.
- returned to the script for further use At the same time, the template tags themselves should
not be intrusive to the designer during the prototyping
At this point, a complete page has been built with phase of their work. We can make everyone happy by
very few lines of code and a minimum of effort. using simple HTML comments as tags. In order to
make it easier to separate the template tags from other
comments in the source code, we can frame the tag-
Take Advantage Of Existing descriptors with curly brackets.
Solutions In the code snippet below, a begin loop tag is placed
in front of those passages in the template which need
Before we start designing the templates, we should to be repeated for each row of a dataset in the content
take a few things into consideration. I have seen many array. In this example, the pattern of creating table
projects go down in flames simply because no one rows of two cells apiece is repeated for each name
even thought to come up with a goal for the applica- and surname pair in the content array. The end of the
tion, not to mention how to achieve the goal in some passage also has to be marked, which we accomplish
reasonably efficient way. They started off by going by using an end loop tag. Bear in mind that you can
after the end result, but with no plan at hand, most of put anything you want between the begin and end
their time is used up by implementing yet another cool tags! We were just being nice to you with this exam-
(or even worse, necessary) feature which requires a ple. Feel free to put more tags, fewer tags, other loops
complete overhaul of the existing code, or at least which contain other loops and more tags... You get the
major changes in some functions. To avoid this fate, a picture. Just remember to indent your code so you
good approach is to have a look at the competitors: dont lose track of which tags belong to which loops!
What are their key features? What problems are solved
in an elegant way? Where are the weak points? Often <html>
during this research phase you will notice things you <body>

February 2003 PHP Architect www.phparch.com 34


FEATURES Creating a Customized Template-Engine

<table> between the filename and the normal editor-friendly


<! begin loop {contacts} > html extension. As a result, we get filename.tpl.html
<tr> and were ready to design functional templates.
<td><! {name} ></td>
<td><! {surname} ></td>
</tr>
<! end loop >
The purpose of a template
</table>
</body>
engine is to bring certain
</html> dynamic content into a flexi-
If you use the template structure and tags as exem- ble, predefined design frame-
plified by the sample code here, you have a powerful
yet easy to use language to prescribe content for a
work.
given template. In addition, it does not matter if the
content is used as textual data in the browser window
for human consumption, or even attributes of HTML Creating The Template Engine
elements!
It would also be very nice if we could identify an We will implement the whole engine as a single class,

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


HTML document that acts as a template without hav- taking advantage of PHPs OOP (object orientated pro-
ing to open it. For that reason, we can introduce a gramming) capabilities. Each possible modification to
new suffix, .tpl. Some WYSIWIG editors and even the template object from outside the class is performed
some simple text editors are unable to deal with by using a corresponding public method and some
unknown suffixes, so we can tuck our new extension additional private methods as internal class helpers.

Figure 2

February 2003 PHP Architect www.phparch.com 35


FEATURES Creating a Customized Template-Engine

Listing 1 $this->file for Server Side Include tags and


$db = mysql_connect('localhost', 'root', '');
processes them by using the private method
process_ssis(). Next, it processes possible con-
$query = "SELECT authors.name as name, tained loops by calling process_loops(). This pri-
books.name as book
FROM authors,
vate method scans for loops with get_loop(), catch-
books es their names with get_loopname() and calls
WHERE books.author_id = authors.id replace_template_tags() to parse the proper
ORDER BY authors.name,
books.name";
content from $this->content. It also scans for
subloops and calls itself recursively, if subloops are
mysql_select_db('my_db', $db); found.
$results = mysql_query($query, $db);
The last step is scanning for remaining standalone
$last_author = ''; content tags with replace_template_tags(). The
$item = array(); parsed template is now contained in $this->tem-
$num_authors = 0;
$num_books = 0;
plate and ready for anything!
The template object can now be told either to out-
while($author = mysql_fetch_assoc($results)){ put() the template, return it as a string with the
if($author['name'] != $last_author)
{
method get() or to dump() it to disk. When dump-
if(sizeof($item) != 0) ing to disk, a file pointer is opened, the string is writ-
{ ten as a binary safe file and the pointer is closed again.

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


$items[] = $item;
$item = array();
} Finally: Filling The Templates With
$last_author = $author['name']; Customized Content
$item['name'] = $author['name'];
$num_authors++;
} Now that we have implemented the template
engine and designed the necessary templates, we are
$item['books'][]['title']=$author['book'];
$num_books++;
ready to start off by filling the templates with content.
} Thanks to our engine, we can now do this with very
few lines of code. The example I will use here is a typ-
$items[] = $item;
ical database query, the result of which should be dis-
$template->add_content($items, 'authors'); played in a useful and attractive manner. Imagine a lit-
$template->add_content($num_authors, erary database where we want to list all of the authors
'num_authors');
$template->add_content($num_books, from one table with all of their books, which are stored
'num_books); in another table. First, we create a new instance of our
template class and tell the object which file to use as a
We need the the public methods listed in Table 1 for template:
modifying the template object
With these methods, we have all we need to declare, require_once(class_pet.eng.inc.php);
$template = new pet;
modify and output the template after having created a $template->read_file(catalogue.tpl.html);
template object as an instance of the template class.
Now we have to implement the tag recognition and
processing functionality. In addition to the public Then, we query the database. The result should look
methods, we add several private methods to lend a bit like this:
more structure to our code. By doing so, we keep these
ideas separate in the code as well, which allows us to
modify a certain detail or behaviour of our engine
without having to change too much of our source
code.

And This Is How It Works

A call to read_template() opens a file pointer to


the template file, and reads it into the class variable
$this->file. Adding content with the add_con-
tent() method inserts a new key into the content Now the result is added to the content in Listing 1.
array $this->content and assigns the content. The Finally, we place the content into the template and
parse() method is the heart of the class. It scans

February 2003 PHP Architect www.phparch.com 36


FEATURES Creating a Customized Template-Engine

send it to the browser: est releases, additional documentation and more


examples are available at http://php-
$template->parse(); pet.sourceforge.net.
$template->output();

The results can be seen in (where are the drums, I


Figure 4
cannot here any drums?!?) Figure 4.

Endless Possibilities For


Extensions

Now that our template engine is working, there are


endless possibilities to extend its capabilities and to
customize it to your own needs. This can be done by
implementing new methods or creating and extending
classes. Another approach would be to use the
return() method, which enables you to modify the
page before you send it to the browser.
One of my extensions was implementing a security

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


feature; logging all links in the parsed template made
it possible for me to let the another script compare its
request URI to the allowed links in the URL cache. If a
user has modified one of the clickable links (e.g. by
changing parameters passed in the URL) and, there-
with, has tried to modify the behaviour of the script, php|a
error handling engages. Andreas Demmer owns a company which offers IT consulting, software-
engineering and web design (http://www.demmer-online.de) He has
been programming PHP for 3 years and is currently involved in creating
Information About The Author
a free web-based personal information manager
(http://www.netpda.de) which is also available as commercial group-
The template engine shown here is published under ware. This project makes extensive use of a more sophisticated, modi-
the name P.E.T. (PHP Processor Engine For Templates) fied version of this template engine.
as open source software under the GNU GPL. The lat-

www.cyberbite.com

CYBERBITE
W
W E
E B
B H
H O
O S
S T
T II N
N G
G

Revolving around YOUR business

Designed for PHP Programmers Virtual Private Servers & Dedicated Servers
Reliable Internet hosting solutions Guaranteed 99.95% uptime

February 2003 PHP Architect www.phparch.com 37


FEATURES

Building a Turing Test


with PHP and the GD
Library
FEATURES

By Marco Tabini

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


Annoyed by those bots that keep scouring through your website to collect your users personal information? A simple
Turing Test might be the solution to your problems.

particular IP over a certain period of time. Similarly,


O ne of the most basic problems affecting Internet
transactions is one of identity and trust. Its
impossible to establish beyond any shadow of doubt
you could omit any contact e-mail addresses from your
website, or use versions of them that contain spurious
that the user who is accessing a website is who he or text easily identifiable by a human but difficult to spot
she claims to beor even that there is a he or a by an automated program (like
she at the other end of the connection. marcot@removeme.phparch.com). The world, howev-
Automated web browsing programs, also known as er, is full of unscrupulous people who are quick to adapt
robots or bots, have been around for a rather long to this type of solution by spoofing their IP address or
time, and most often they exist for a legitimate pur- programming their bots to recognize typical spurious
pose. For example, most search engines use bots for text like nospam or removeme. While you could
the purpose of collecting information about the pages add further complexity to your schemes to combat
of a website and indexing them so that they can pro-
vide meaningful responses to their users.
Lately, however, bots are part of a worrying trend One of the most basic prob-
that sees them used for less-than-legal purposes, such
as mass-creating fake accounts on a website so that lems affecting Internet trans-
they can be used for perpetrating scams. Imagine, for
example, creating and checking one thousand different
actions is one of identity and
e-mail accounts on a free e-mail website and then using
those accounts to send unsolicited messages, or even
trust.
just scouring the web looking for valid e-mail addresses
to spam. These tasks can be easily scripted and per-
formed by a bot. REQUIREMENTS
The easiest solution to this problem is to place some
sort of restrictions on a sites usage. For example, a free PHP Version: 4.0 and Above
webmail provider could limit the number of new O/S: Any
accounts that can be created by a user coming from a Additional Software: GD Library

February 2003 PHP Architect www.phparch.com 38


FEATURES Building a Turing Test with PHP and the GD Library

against these digital desperados, at some point this historical question: Can machines think? and came up
complexity will affect the usability and convenience of with a testlater dubbed the Turing Testfor deter-
your site. In other words, it will impact your users! mining whether an interlocutor at the other end of a
Unfortunately, finding a middle ground that allows easy conversation is human or not. Naturally, the perfect
access to legitimate users while stopping evil-doers at thinking machine would be indistinguishable from a
the gates is what is often referred to in IT as a non-triv- human.
ial task.
Turing Tests in Practice
Enter Dr. Turing
In 1990, New York-based philantrophist Hugh
Dr. Alan Turing (1912-1954) was a very important, Loebner and Cambridge University (Dr. Turings alma
and very peculiar, figure in the history of computers. In mater) instituted the $100,000 (US) Loebner Prize, the
fact, if youre in the computer industry, you may indi- first formal implementation of a Turing Test. Since thir-
rectly owe your fortunes to some of his work. teen years later the prize is still unawarded (despite
Dr. Turing is responsible for much of the work behind many having tried to win it), its safe to say that a
the invention of the first workable computer. In fact, Turing Test can be a valuable tool for determining
todays PCs can be described finite versions of his whether the user of a website is man or machine.
Turing Machine. His name is also behind most of the A typical Turing Test is administered by letting a
decryption work performed at Bletchley Park, the human initiate and sustain a discussion with the test

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


British cryptanalysis laboratory that analyzedand ulti- subject through a communication medium that is not
mately brokeGerman code cyphers during World War immediately going to disclose the identity of the latter.
II. The web perfectly fits this description but if your site
In an article published in 1950, Turing pondered the has more than three visitors a day, it would be imprac-

Listing 1
1 <?php
2
3 function GenerateImage($token)
4 {
5 $iFont = 5; // Font ID
6 $iSpacing = 2; // Spacing between characters
7
8 // Establish font metric and image size
9
10 $iCharWidth = ImageFontWidth ($iFont);
11 $iCharHeight = ImageFontHeight ($iFont);
12 $iWidth = strlen($token) * ($iCharWidth + $iSpacing);
13 $iHeight = $iCharHeight;
14
15 // Create the image
16
17 $pic = ImageCreate ($iWidth, $iHeight);
18
19 // Allocate a background and foreground colour
20
21 ImageColorAllocate ($pic, 200, 200, 200);
22 $col2 = ImageColorAllocate ($pic, 0, 0, 100);
23
24 $iX=1;
25
26 for ($i=0; $i < strlen ($token); $i++)
27 {
28 ImageChar ($pic, $iFont, $iX, 0, $token[$i], $col2);
29 $iX += $iCharWidth + $iSpacing;
30 }
31
32 ob_start();
33 ImageJPEG($pic);
34 $data = base64_encode (ob_get_contents());
35 ob_clean();
36 ImageDestroy($pic);
37
38 return $data;
39 }
40
41 ?>

February 2003 PHP Architect www.phparch.com 39


FEATURES Building a Turing Test with PHP and the GD Library

tical for you to subject each of them to a Q&A before Next, the function creates a new image by calling
you let them into it. ImageCreate() and selects a background and fore-
Luckily, the next best thing is easy to implement. All ground colour. Youll notice that I dont bother filling
you need is a method for requesting information from the background explicitly with the appropriate
the user in a form that would be extremely difficult for colourin fact, I dont even save the colour resource
a machine to understand. Consider this example: you returned by the first call to ImageColorAllocate()
show the user a dynamically-generated image that con- into a PHP variable. This is because, by default, the first
tains a combination of numbers, and ask the user to colour created as part of an image is also used as its
input the same combination into a text box. Now, for background colour.
a human this is a trivial matterread the text from one Finally, the text is outputted one character at a time
box, write it in the other. For a bot, however, its a to account for the desired spacing (and to provide a
completely different story. In order for it to properly framework for some additional hardening of the Turing
identify the symbols in the image, it would have to Test that Ill explore later on). The resulting image is
implement an OCR system, and even then it would rendered into PHPs text buffer (whose output has been
have to know what to look for. interrupted by a call to ob_start()) and extracted
from it. The final step consists of clearing the buffer,
Getting Started destroying the image resource and returning the JPEG
data to the caller.
The simplest way to create and manipulate images in Despite its simplicity, GenerateImage() paints the

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


a meaningful way from PHP is to use the image exten- pretty picture (pardon the pun) shown in Figure 1.
sion, which relies on the GD Library. If youre using
PHP 4.3, then a version of the GD Library comes pack- Figure 1
aged together with the PHP interpreter, and all you
have to do is either enable it at compile time by using
the with-gd switch when invoking configure (if
youre compiling PHP from source), or add it to the list
Creating the First Turing Test
of enabled extensions (if youre using a pre-compiled
version of PHP, as most Windows users will).
Now that we have the tools for creating an image
If youre using a previous version of PHP, the proce-
that contains a combination, its time to create a web
dure is a bit more complicated, since youll have to
page that is able to administer the Turing Test to an
download the GD library and all of its ancillary libraries
Internet user. This will work by showing the picture to
in order to properly compile it into the PHP interpreter.
the user and allowing the user to submit a web form
However, this is not that difficult, and the PHP manual
with his or her intepretation of the images contents.
has plenty of information to help you out.
Upon submission of the form, our script will compare
The function that we will use to create an image that
the users input to the original combination and deter-
contains our numeric combination is shown in
mine whether they match.
Listing 1.
As you can see, GenerateImage()
relies on the use of GDs built-in font Figure 2
handling functions. These provide
the necessary facilities for loading
and outputting bitmap fonts; they
also include five built-in fonts that
can be used directly and without
loading any information from an
external resource.
Because the fonts are mono-
spaced, its relatively easy to deter-
mine the correct size of the image
that the function will need to prop-
erly display the combination. This is
accomplished by calling the
ImageFontWidth() and
ImageFontHeight() functions
and multiplying their results by the
number of characters in $token as
appropriate.

February 2003 PHP Architect www.phparch.com 40


FEATURES Building a Turing Test with PHP and the GD Library

Listing 2
1 <html>
2 <body>
3 <center><h1>Marco's Turing Test</h1>
4 <h2>Are you human?</h2></center>
5
6 <?php
7
8 // Generate a Turing Test image and output it to the browser
9
10 function GenerateImage($token)
11 {
12 $iFont = 5; // Font ID
13 $iSpacing = 2; // Spacing between characters
14
15 // Establish font metric and image size
16
17 $iCharWidth = ImageFontWidth ($iFont);
18 $iCharHeight = ImageFontHeight ($iFont);
19 $iWidth = strlen($token) * ($iCharWidth + $iSpacing);
20 $iHeight = $iCharHeight;
21
22 // Create the image
23

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


24 $pic = ImageCreate ($iWidth, $iHeight);
25
26 // Allocate a background and foreground colour
27
28 $col = ImageColorAllocate ($pic, 200, 200, 200);
29 $col2 = ImageColorAllocate ($pic, 0, 0, 100);
30
31 $iX=1;
32
33 for ($i=0; $i < strlen ($token); $i++)
34 {
35 ImageChar ($pic, $iFont, $iX, 0, $token[$i], $col2);
36 $iX += $iCharWidth + $iSpacing;
37 }
38
39 ob_start();
40 ImageJPEG($pic);
41 $data = ob_get_contents();
42 ob_clean();
43 ImageDestroy($pic);
44
45 return $data;
46 }
47
48 function AdministerTest ($error = false)
49 {
50 // Generate a six-digit random string
51
52 $token = (string) rand (100000, 999999);
53
54 // Output form to the user
55
56 if ($error)
57 echo '<b>ERROR! You\'re not human!</b><p />';
58 ?>
59 <img src="data:image/jpeg;base64,<?= base64_encode (GenerateImage ($token)) ?>">
60
61 <form action="<?= $_SERVER['PHP_SELF'] ?>" method=post>
62 <input type="hidden" name="tok" value="<?= md5 ($token) ?>">
63 Please enter the combination you see in the string above:
64 <input type="text" name="inp">
65 <input type="submit">
66 </form>
67 <?
68 }
69
70 function CheckResults()
71 {
$ $ [ i ]
Continued On Page 42...

February 2003 PHP Architect www.phparch.com 41


FEATURES Building a Turing Test with PHP and the GD Library

Before moving on to the code, however, we have to on the fly, wed have to actually point to a PHP script.
make a simple (and yet important) consideration: the This approach, however, presents the same problem of
test will be administered by a machineitself unable to requiring that the combination be passed to the script
interpret the contents of the image. As such, were in some way. In addition, in this case an MD5 hash
going to have to come up with a way to pass the infor- would not work, since the image-generating script
mation about the combination between the first execu- would be unable to retrieve the initial string, thus leav-
tion of the script, in which the image is generated and ing us with far more complex solutions.
shown to the user, and the second, in which the users Instead of the typical approach, then, I will adopt an
input is analyzed and compared to the original. atypical one. There is a little known extension of HTML
One possible way to pass the information is to use that makes it possible to pass the image data directly as
sessions. I will not cover this mechanism, however, for part of the <img> tag. To do so, its necessary to use
two reasons. First, its a trivial exercise that does not the following format:
require much explanation. Second, and most impor-
tant, you may not want to create a session for a user <img src=data:<content-type>;<for-
until you have established that he or she (or it) should mat>,<encoded-data>>
access the site by passing (or failing!) the Turing Test.
There is another simple (although imperfect, as well Where <content-type> is the MIME content type
see) way to pass the information along: it can be of the data (in our case, image/jpeg), <format> is the
encrypted using the md5 function, which will make it format in which the data is being passed, and <encod-

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


meaningless to the user but easily manipulated by the ed-data> is the image data itself. For our purposes,
script. The script will pass the hashed value of the com- we need a format that will not interfere with the struc-
bination to itself as part of the web form that the user ture of the HTML code itself. For our purposes, the
submits and then compare it to the hashed value of the base64 encoding format fits the bill perfectly. It for-
users input. Because the MD5 hash is a one-way func- mats eight-bit values using only six bits at a time, thus
tion, there is no way for the user to determine the com- producing an output that does not include any special
bination, short of adopting a brute force approach characters, such as quotation marks, that could confuse
(more about this later). the HTML parser in the browser.
This inline data definition mechanism is supported by
Generating the Image recent versions of most browsers, including Intenret
Explorer and Netscape Navigator/Mozilla. By using it,
Our script will also have to worry about how to dis- its possible to completely eliminate the problem of
play the image. Typically, this is done by using an passing the combination in a two-way encrypted form
<img> tag that causes the browser to load a separate to an external script responsible for generating the
file. Since our script is required to generate the image image.

Listing 2: Continued From Page 41...


72 $token = $_POST['inp'];
73 $hash = $_POST['tok'];
74
75 if (md5 ($token) === $hash)
76 {
77 ?>
78
79 <b>Congratulations! You're either human or a really smart machine!
80
81 <?
82 }
83 else
84 AdministerTest (true);
85 }
86
87 // If the user has not POSTed the page, then
88 // administer the test. Otherwise, check the results.
89
90 if (strcmp ($_SERVER['REQUEST_METHOD'], 'POST'))
91 AdministerTest();
92 else
93 CheckResults();
94
95 ?>
96
97 </body>
98 </html>

February 2003 PHP Architect www.phparch.com 42


FEATURES Building a Turing Test with PHP and the GD Library

On To The Code Improving The Test

Listing 2 shows an implementation of our Turing Test. As it is, the test is not really foolproof. The use of just
As you can see, aside from the GenerateImage() func- six digits and a well-known hashing algorithm, in fact,
tion, there is little new here. The AdministerTest() func- makes it relatively easy to write a bot that can brute-
tion is used to generate the form that requests the user force hack the combination. For example, take a look
to validate the combination. The script calls this func- at the script in Listing 3, which loads up the HTML pro-
tion directly whenever it is not invoked through a POST duced by Listing 2 and simply cycles through all the
operation, which would indicate that the user is actual- possible 6-digit combinations between 100,000 and
ly submitting information. Otherwise, the 999,999 until it has found the one whose MD5 hash
CheckResults() function is called, and the users input is matches the value passed along by the script. As you
matched against the MD5 hash of the original combi- can see for Figure 2, even on a relatively low-end
nation. machine like mine (I use a Celeron-based computer),

Listing 3
1 <?php
2
3 // Read the test source HTML
4

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


5 $f = fopen ('http://localhost/turing/code/turing_safe.php', 'r');
6 $data = fread ($f, 500000);
7 fclose ($f);
8
9 // Extract the md5 hash of the token
10
11 if (!ereg ('<input type="hidden" name="tok" value="([^"]*)">', $data, $token))
12 die ("Unable to find token");
13
14 $token = $token[1];
15
16 // Extract the image for visual verification
17
18 ereg ('(<img src="data:image/jpeg;base64,[^"]*">)', $data, $img);
19
20 // Record start time
21
22 $start = explode (' ', microtime());
23
24 // Brute-force through all the possible combinations
25
26 $result = 0;
27
28 for ($i = 100000; $i <= 999999; $i++)
29 {
30 if (md5 ((string) $i) === $token)
31 {
32 $result = $i;
33 break;
34 }
35 }
36
37 // Record end time
38
39 $end = explode (' ', microtime());
40
41 // Brag!
42
43 ?>
44
45 <html>
46 <body>
47 <?=$img[1]?><p />
48 <?php if ($result > 0) { ?>
49 Broken with <?= $result ?> in <?= ($end[0] + $end[1]) - ($start[0] + $start[1]) ?> seconds.
50 <?php } else { ?>
51 Unable to break. After all, I'm only... not human!
52 <?php } ?>
53 </body>
54 </html>

February 2003 PHP Architect www.phparch.com 43


FEATURES Building a Turing Test with PHP and the GD Library

breaking the combination 894,097, which is almost at tion would make it more difficult to crack the code with
the far end of the range, only takes about nineteen sec- a brute-force approach by a factor of ten. However,
onds. Assuming a linear distribution of the combina- this would still mean several hundred breaches a day,
tions generated by the script, therefore, my machine and more work for the human visitors to the website,
should be able to crack any combination in an average who get easily annoyed and would rather not have to
of between 9 and 10 seconds. Even though this still type that many numbers. Another possibility would be
slows down the process, it does not prevent it from including letters together with numbers; even keeping
going ahead, and my little bot, properly modified, the total number of characters in the combination at
would be able to penetrate a website protected by the six; simply adding the twenty-six letters of the alphabet
Turing Test administered by the script in Listing 2 to it would increase the total number of possible com-
almost 8,700 hundred times in a single day. binations to 366, or 2,176,782,336thats almost
The simplest way to make the test more accurate is to 2,177 more than just using digits.
increase the number of digits. A seven-digit combina- However, I still think that adding letters to the com-

Listing 4
1 <html>
2 <body>
3 <center><h1>Marco's Turing Test</h1>
4 <h2>Are you human?</h2></center>

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


5
6 <?php
7
8 // Generate a Turing Test image and output it to the browser
9
10 function GenerateImage($token)
11 {
12 $iFont = 5; // Font ID
13 $iSpacing = 2; // Spacing between characters
14 $iDisplacement = 4; // Vertical chracter displacement
15
16 // Establish font metric and image size
17
18 $iCharWidth = ImageFontWidth ($iFont);
19 $iCharHeight = ImageFontHeight ($iFont);
20 $iWidth = strlen($token) * ($iCharWidth + $iSpacing);
21 $iHeight = $iCharHeight + 2 * $iDisplacement;
22
23 // Create the image
24
25 $pic = ImageCreate ($iWidth, $iHeight);
26
27 // Allocate a background and foreground colour
28
29 $col = ImageColorAllocate ($pic, 200, 200, 200);
30 $col2 = ImageColorAllocate ($pic, 0, 0, 100);
31 $col3 = ImageColorAllocate ($pic, 123, 245, 250);
32
33 $iX=1;
34
35 for ($i=0; $i < strlen ($token); $i++)
36 {
37 ImageChar ($pic, $iFont, $iX, $iDisplacement - (rand (-
$iDisplacement, $iDisplacement)), $token[$i], $col2);
38 $iX += $iCharWidth + $iSpacing;
39 }
40
41 // Draw some lines
42
43 for ($i = 0; $i < 5; $i++)
44 ImageLine ($pic,
45 rand (0, $iWidth / 2),
46 rand (0, $iHeight / 2),
47 rand ($iWidth / 2, $iWidth),
48 rand ($iHeight / 2, $iHeight),
49 $col3);
50 ob_start();
51 ImageJPEG($pic);

Continued On Page 45...

February 2003 PHP Architect www.phparch.com 44


FEATURES Building a Turing Test with PHP and the GD Library

bination (something that can be done very easily by Listing 4 contains a revised version of Listing 2 that
just changing a couple of lines of code in Listing 2) cannot be broken by the brute-force script of Listing 3.
would make it too complex for users to follow. As such, As you can see, the only changes made are on line 63
I have adopted a third alternative, which consists of and line 73, where I have added the string Youll never
adding a secret phrase to the MD5 hash. This is an break this to the combination when calculating the
extremely easy change to implement and, at the same MD5 hashes. Because this string is secret and is never
time, it maintains the simplicity of the original interface sent over the wire, a bot would never be able to re-cre-
while increasing the security of the test to a level that ate the MD5 (save a really lucky accident), and would
makes it impractical for a bot to break it through a sim- therefore have to try and submit each combination
ple brute-force approach. separately. Even assuming a response time of one sec-

Listing 4: Continued From Page 44...


52 $data = ob_get_contents();
53 ob_clean();
54 ImageDestroy($pic);
55
56 return $data;
57 }
58
59 function AdministerTest ($error = false)

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


60 {
61 // Generate a six-digit random string
62
63 $token = (string) rand (100000, 999999);
64
65 // Output form to the user
66
67 if ($error)
68 echo '<b>ERROR! You\'re not human!</b><p />';
69 ?>
70 <img src="data:image/jpeg;base64,<?= base64_encode (GenerateImage ($token)) ?>">
71
72 <form action="<?= $_SERVER['PHP_SELF'] ?>" method=post>
73 <input type="hidden" name="tok" value="<?= md5 ($token . 'You\'ll never break this!') ?>">
74 Please enter the combination you see in the string above:
75 <input type="text" name="inp">
76 <input type="submit">
77 </form>
78 <?
79 }
80
81 function CheckResults()
82 {
83 $token = $_POST['inp'];
84 $hash = $_POST['tok'];
85
86 if (md5 ($token . 'You\'ll never break this!') === $hash)
87 {
88 ?>
89
90 <b>Congratulations! You're either human or a really smart machine!
91
92 <?
93 }
94 else
95 AdministerTest (true);
96 }
97
98 // If the user has not POSTed the page, then
99 // administer the test. Otherwise, check the results.
100
101 if (strcmp ($_SERVER['REQUEST_METHOD'], 'POST'))
102 AdministerTest();
103 else
104 CheckResults();
105
106 ?>
107
108 </body>
109 </html>

February 2003 PHP Architect www.phparch.com 45


FEATURES Building a Turing Test with PHP and the GD Library

ond (which is probably very generous, considering that ed vertically, the OCR system will find it more difficult
the bot will try to access the website from across the to recognize their correct order. In addition, you can
Internet), it will take an average of 500,000 seconds also add some noise to the image, for example by
(something like eight or nine days) before the test can adding random lines to it in different colours. This will
be cracked. break the pattern of the characters that compose the
combination, making it difficult to understand for an
What If the Bot Can Read? OCR application but only slightly less clear to a human
viewer.
Another possibility that must be taken into account is Listing 5 shows a modified version of Listing 4 that
that a bot might contain an OCR mechanism capable implements this changes, whose effect you can see in
of correctly interpreting the image generated by our Figure 3.
script. A simple solution to this problem would be to
use odd fonts, or fonts that are strongly anti-aliased.
PHPs Image extension supports the use of both
TrueType and PostScript Type1 fonts, which can pro-
vide high-quality output and are strongly anti-aliased.
These fonts are actually more difficult to detect for an
OCR program than the simple, crisp bitmap fonts
provided by the GD library. However, its also more dif-

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


ficult to compile PHP with built-in support for these
fonts, and their use is beyond the scope of this article.
php|a
Luckily, there are at least another couple of measures
that you can take to counter the use of OCR programs. Marco Tabini is co-editor of php|architect. He spends most of his time
lurking around on the PHP mailing lists and on the php|a website doing
The first consists of changing the baseline of the
his best to confuse fellow programmers who are in trouble. You can reach
string outputted by the script. If the fonts appear shift- him at marcot@phparch.com.

February 2003 PHP Architect www.phparch.com 46


FEATURES

Using, Reusing and


Extending Smarty
FEATURES

By Tomica Jovanovic

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


Smarty represents an excellent solution for creating websites that are easy to manage and maintain, all the while pro-
viding very good performance and caching features. This article illustrates how you can get Smarty to give you more
by extending its functionality.

The Question others were created, since FastTemplates wasnt every-


ones cup of tea (even I created my own system for
When PHP was born, it was a very simple language, this very reason). Most of the systems featured vari-
whose main purpose was to generate HTML code for able substitution, dynamic (repeating) blocks, inclu-
the presentation of data. That data came from C func- sion of other templates, and sometimes even optional
tions that formed the application logic. One of the if blocks. This was fine, except that almost all of
goals of PHP from the outset was to separate front-end them did this using regular expressions, which compli-
presentation from the back-end logic of web cated code, and added additional overhead to the
applications. whole application.
However, by version 3 PHP had already become a They were hard to extend (and to adjust to taste),
pretty complex and complete language. With numer- and didnt provide the complete separation of presen-
ous string manipulating functions, arrays, and access tation and logic (for example, to make rows of a table
to flat-file and SQL databases, PHP soon grew to be alternate colors, PHP code had to be adjusted).
powerful enough to create complex, database driven
sites. All you needed to create a small to medium The Answer
sized dynamic web site was PHP, and a database serv-
er (usually MySQL). The SmartTemplate system was the predecessor of
Once people became daring enough to start the todays Smarty, though it is not a direct ancestor.
development of larger sites with PHP, however, it Development of SmartTemplate started in 1999, and
became evident that the old concept of mixing PHP it was very similar to all other template engines avail-
and HTML code was not A Good ThingTM. A quick able at that time. Even though it supported some
solution was provided in the form of a PHP port of the
popular FastTemplates engine, which was originally REQUIREMENTS
done in Perl. PHP Version: 4.0.6 and above (4.1 recommended)
Shortly thereafter, even though FastTemplates was Additional Software: Smarty Template Engine
probably the most popular template engine, a lot of (http://smarty.php.net)

February 2003 PHP Architect www.phparch.com 47


FEATURES Using, Reusing and Extending Smarty

advanced features like configuration files and nested tion. This is not a trivial task to do thoroughly and
blocks, it suffered from the same issues as other tem- accurately, which is why a somewhat complex system
plate engines of that time. However, even though it like Smarty is needed.
had problems, it helped the creators gain an intimate
knowledge of all aspects of the creation and use of Extending Smarty
template systems. The idea to employ a completely
new concept was born from this experience, and this There are lots of Smarty tutorials on the net, because
concept was welded together with the other ideas of Smarty itself is gaining popularity. The most prevalent
the development team to form what later became shortcoming in the majority of the tutorials is that they
Smarty. just barely scratch the surface in terms of showing off
This new revolutionary concept is referred to as all of the functionality and the ways in which Smarty
compiling templates. It combines the speed of exe- can be leveraged to get a job done. Even those which
cution of pure PHP code with the ease and simplicity explain the idea of template compiling often say noth-
of template syntax. Using this process, template files ing about the second biggest advantage of Smarty,
are first converted to regular PHP code before they are which is its extensibility.
executed. This sounds costly in terms of performance It is this unique extensibility that really makes Smarty
until you consider that this need only be done when an invaluable development tool and solution frame-
the template file is changed! Once a template file is work. With Smartys simple-yet-powerful plugin sys-
compiled, they are stored for later use (and re-use). tem, developers can truly have the flexibility they

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


The end result being a reduced overall server load. need to satisfy any requirement that may arise during
a given projects life cycle.
The primary goal of this article is to explain some of
The old concept of mixing Smartys more advanced functionality, and to show
you some real life examples of how you can elegantly
PHP and HTML code is not A use Smarty to ease your life (ok, your work then). If
you dont have any experience with Smarty (or at least
Good Thingtm. with a template system), I suggest that you read some
introductory material on Smarty before you continue
with this article.
Smarty basically processes templates. It uses tem-
Why Not Smarty? plate tags to distinguish dynamic content (data pro-
vided from your PHP application) from the rest of the
Some opponents of Smarty will say that it is too template HTML. These tags are replaced with the
complex, that it has complicated syntax, and an over- result of some Smarty functions. Except for a couple
abundance of functions and features. Some have said of basic, or core Smarty functions, every other func-
that Smarty is a framework of its own, or even a lan- tion is really included in the form of a plugin. This
guage of its own, and that it isnt worthwhile to learn allows you to change the behaviour of current plug-
YAL (yet another language). ins, and to even add entirely new ones if you need
While I cannot confirm or deny those allegations, them. This is what I plan to show you next, but first,
what I can do is offer a view from a different perspec- lets set up some basic code.
tive. I already mentioned that Smarty (as PHP itself) As previously noted, for the sake of brevity, this arti-
was created to be used in the presentation layer of cle will assume a basic knowledge of Smarty or some
web applications. For comparisons sake lets look other template system. That said, it quite naturally fol-
briefly at the opposite end of the multi-tier application lows that I wont explain how to use Smarty, or its
architecture, at data storage. It wasnt long before every function. In the examples to follow, I will
creators and users of RDBMS realized that standard explain only the interesting parts of code that relate
SQL wasnt enough to satisfy the needs of complex directly to this article. Also, except in Listing 1, where
applications. Thats why they created views, triggers, a Smarty class is used, I will not use OOP program-
and even completely new languages (PL/SQL, T-SQL) ming techniques in the examples. I feel that it would
for stored procedures and other tools necessary to only confuse someone not familiar with OOP con-
maintain and insure data integrity. The task of those cepts. On the other hand, those of you who under-
new languages and technologies was to separate the stand OOP shouldnt have much difficulty converting
job of data storage from the application logic itself, all of these ideas into classes and objects. So lets dig
just as the task of Smarty is to separate the job of pre- in!
setting data from the main logic of the web applica- In Listing 1, you can see the basic code used in this

February 2003 PHP Architect www.phparch.com 48


FEATURES Using, Reusing and Extending Smarty

Listing 1: Basic Smarty include script - smarty.inc.php


1 <?php
2
3 include_once('proteus/smarty/Smarty.class.php');
4
5 function smarty($file, $vars) {
6 global $smarty_debugging, $error_log, $debug_log;
7 $smarty=new Smarty;
8 $smarty->template_dir='templates';
9 $smarty->compile_dir='templates_c';
10 $smarty->plugins_dir=array('my_plugins', 'plugins');
11 $smarty->debug_tpl='smarty_debug.htm';
12 $smarty->debugging=$smarty_debugging;
13 $smarty->load_filter('output', 'literal_cleanup');
14 $smarty->assign($vars);
15 $smarty->assign('error_log', $error_log);
16 $smarty->assign('debug_log', $debug_log);
17 $smarty->display($file);
18 }
19
20 ?>

article. This code will be modified later to demon- Smarty supports several types of plugins. Some plu-

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


strate Smartys extensibility. However, well view it in gins, such as template functions, modifiers and inserts,
its pristine state here as a starting point. Keep this Smarty can load on its own if you use them in the tem-
listing handy, as we'll be referring back to it later in the plate code. Others, such as Pre, Post and Output fil-
article ters have to be explicitly loaded (with the load_fil-
Basically, this function processes a template passed ter() method), because they arent mentioned in
in using the variable $file, and passes data from an the template code. I will explain modifiers and filters
associative array $vars to the template engine. The here, and give a couple of very useful examples that
first thing you should notice in Listing 1 is on line 10, show how easy it is to extend Smarty.
where an array is assigned to the plugins_dir Smarty Modifiers are small functions that modify a vari-
property. The second item in the array is the name of able that precedes them. To apply some modifier to a
the plugin directory that comes with Smarty by value, you append | (pipe) to a value, and then a name
default. The first item is the name of the plugin direc- of a modifier. For example:
tory where I keep all of the plugins I have made or
modified. It is smart to separate them like this, <b>{$article_title|capitalize}</b>
because when new versions of Smarty are installed, all
I need to do is overwrite the standard files and direc- Modifiers can be combined (chained) together, and
tories, while my files remain safely in their own direc- can accept parameters that are separated with a :
tory. Ordering of plugin directories in the Smarty (colon).
$plugin_dir array is also important. For example, if Writing your own modifier is not a difficult task. I
you want to modify some of the standard Smarty plu- often needed a classic PHP function - substr() in my
gins, you can just copy them to your plugin directory, templates, so I have built my own. Listing 2 shows
and change them at will. Later, your version of the code for a substr() modifier.
plugin will be used, because it is in the first plugins The official name of this modifier is substr. Names
directory (in Smartys plugin path). of modifier functions need to start with smarty_mod-
ifier_, followed by the actual modifier name.
Modifier code must be placed in a file that resides in
Listing 2: Basic substr() Modifier - the plugin directory, and its name should start with
modifier.substr.php modifier, followed (again) by the modifier name,
<?php and ending with .php. All this naming and position-
ing of code helps Smarty determine what file to
function smarty_modifier_substr($string, $start,
$length = dummy)
include, and which function to invoke. So, whenever
{ you use the |substr modifier in your template, Smarty
if ($length===dummy) knows to call this function.
return substr($string, $start);
return substr($string, $start, $length);
The function code is so simple theres hardly any-
} thing to explain. As you can see, the modifier function
simply calls the PHP standard substr() function,
?> and returns the result of that function. By the way,

February 2003 PHP Architect www.phparch.com 49


FEATURES Using, Reusing and Extending Smarty

Listing 3: prefilter.literal_script.php
<?php

function smarty_prefilter_literal_script($source, &$smarty){


$result=&$source;
$result=preg_replace('~<script\b(?![^>]*smarty)~iU', '<!{literal} ><script', $result);
$result=preg_replace('~</script>~iU', '</script><!{literal} {/literal}>', $result);
return $result;
}

?>

this modifier also performs in exactly the same man-


ner as the PHP substr() function. The only differ-
ence is logistical; our modifier only accepts up to two <STYLE type=text/css>
parameters, rather than the possible three accepted <!
.article-title { font-size: x-large; }
by the substr() function. This is due to the first
>
parameter in our modifier being implicitly added as
</STYLE>
the value that precedes the modifier. So, for example,
{$article_title|substr:0:3} prints the first
This is easily solved by wrapping your JavaScript or
three characters of $article_title, whereas this

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


CSS code in the Smarty tags, {literal} and {/lit-
would be written as substr( $article_title, 0,
eral}. These tags tell Smarty that it shouldnt try to
3) using native PHP. Likewise,
interpret the marked code. Since web designers tend
{$article_title|substr:-5} behaves just like
to forget details like that, we need a more automat-
substr($article_title, -5). That is, it prints
ed solution. This is accomplished with two pre-filters.
the last 5 characters of a string.
In Listings 3 and 4, you can see a bit of regex magic
that adds {literal} tags around <SCRIPT> and
Filters
<STYLE> tags.
The explanation of regular expressions used in these
Filters are functions that do some processing on the
functions is out of scope for this article, so if you dont
source of our templates either before or after they are
understand that code, you will have to trust me that
compiled, which is why theyre referred to as pre and
this pre-filter converts code from the above into some-
post filters, respectively. Filters can also be invoked
thing like:
after the execution of a compiled template. These are
called output filters, since they influence what is <! {literal} ><STYLE type=text/css>
essentially the final output of the code. Filters are use- <!
ful in various situations, but lets go over one in partic- .article-title {
ular that you will run into the minute you start using font-size: x-large;
Smarty in a real world project. }
Smarty delimiters are { and }. Although they can >
be changed, they are still the most commonly used. </STYLE><!{literal} {/literal}>
Unfortunately, { and } characters are also used in
every HTML page that uses JavaScript and/or CSS But wait a minute!, you will say. What if I do
styles. When the Smarty parser comes across want to use Smarty variables inside script or style tags,
JavaScript or CSS code, it reports a compile error, or I want to let Smarty process contents inside these
because it doesnt know how to interpret the foreign tags?. In that case, all you have to do is add a smar-
code: ty pseudo-attribute to the opening HTML tag, like

Listing 4: prefilter.literal_style.php
<?php

function smarty_prefilter_literal_style($source, &$smarty){


$result=&$source;
$result=preg_replace('~<style\b(?![^>]*smarty)~iU', '<!{literal} ><style', $result);
$result=preg_replace('~</style>~iU', '</style><!{literal} {/literal}>', $result);
return $result;
}

?>

February 2003 PHP Architect www.phparch.com 50


FEATURES Using, Reusing and Extending Smarty

Listing 5: outputfilter.literal_cleanup.php
<?php

function smarty_outputfilter_literal_cleanup($source, &$smarty){


$result=&$source;
$result=preg_replace(~<!({literal})? >~iU, , $result);
return $result;
}

?>

Listing 6: debug.inc.php PHP application code is executed, and when all of the
<?php templates are processed, before the HTML code is sent
to the browser, it is processed with an output han-
session_start();
if (isset($_GET[debug])) { dler, which modifies it if necessary. The output filter
$_SESSION[debug]=$_GET[debug]; from Listing 5 does just that. It cleans HTML code
} from needless comment tags before sending the page
$smarty_debugging=false;
if (isset($_SESSION[debug])) {
to the browser. The code in the function is just one
$smarty_debugging=$_SESSION[debug]; call to PHPs preg_replace() function, which does
} all of the dirty work for us.

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


All of these filters are explicitly loaded in the smar-
error_reporting(E_ALL);
set_error_handler(error_handler); ty() function in Listing 1 (lines 14, 15 and 16).

function error_handler($type, $text, $file, $line, Debugging With Smarty


$vars)
{
global $error_log; Smarty already has some built in mechanisms for
$error_log[]=$text;
debugging your templates. These methods can be
$error_log[]=[$line] $file;
} easily extended and used for debugging your whole
web application.
function debug_log($key, $data) { Smarty Debug Console, for example, is a popup
global $debug_log;
debug_log[$key][]=$data; window that informs you about all included templates
} and variables that were passed to Smarty. This can be
?>
very useful in detecting bugs in your templates. A pit-
fall in using this tool, however, is that whenever it is
this: turned on it pops up in response to every page
request made to your site. Fortunately, like everything
<SCRIPT language=JavaScript smarty> else in Smarty, this behaviour can be changed too.
<! Since debug console is just another template that
alert(Hello {$user_name}!); comes with Smarty by default, it is easy to edit and
//> alter its behaviour. I adjusted it to my taste, and the
</SCRIPT>
code can be found in the accompanying zip file.
In Listing 6 you can see a little trick that limits visibil-
With this attribute, contents of script tag will be ity of the debug console only to you. In line 4, vari-
compiled into PHP code, and JavaScript will greet site ables passed via the HTTP GET method are checked. If
visitors with an alert box containing their name. the variable named debug is passed (with values 1
These two pre-filters leave some junk behind them, or 0, for on/off), its value is stored in a session variable
in the form of empty and/or unnecessary HTML com- debug. Later, this value is used in the smarty()
ment tags. I know that these HTML comments dont function (Listing 1, line 12) to show/hide the debug
affect the final look of the page, but I have decided to console. Smarty has a similar capability already built
implement an output filter that cleans this for two rea- in, but my method is slightly better since you don't
sons. First, to test-drive how output-filters work, have to add a debug parameter in every URL you want
(since I havent found a real, practical use for them to call.
yet), and second, to show you how easy it is to imple- This modification makes the debugging functionali-
ment them. ty within Smarty a bit less intrusive. When you need
First of all, what exactly are output filters? Well, if to debug your application, just visit a page on the site,
youve ever used output buffering in your PHP scripts, adding ?debug=1 to the URL. After that, debug
you will grasp the idea very quickly. When all of your

February 2003 PHP Architect www.phparch.com 51


FEATURES Using, Reusing and Extending Smarty

mode is turned on just for you, and for every other vis- Listing 6 contains another useful function called
itor to your site, it is turned off. This remains in effect debug_log(). Its purpose is to log debugging mes-
until you close your browser or add a ?debug=0 to sages to the debug console. It stores those messages
the URL. This can be very handy for debugging live, to the $debug_log global array and can be used to
production sites. provide meaningful error messages that PHP doesnt
report as errors (like mysql_error()) or to an indi-
Note: this isnt the safest method for debug- cation that your script did or didnt do something:
ging, so you should probably turn this feature off
manually whenever you dont need it. mysql_query($query);
if ($mysql_errno()) {
debug_log(mysql_query, $query);
debug_log(mysql_error, mysql_error());
Later in Listing 6, you can see how to use the debug }
console for error handling. First, you turn all errors on
with error_reporting(), and set error_han-
dler() as your error handler. This function stores Debug messages are stored in key->value pairs to
error messages in a global array $error_log, which increase readability. Later, the $debug_log variable
is later passed to smarty in the function smarty() is passed to Smarty in the smarty() function (Listing
(Listing 1, line 18), so that you can see them later in 1, line 19).
the console.

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


Bonus: Smarty MX

Note: this is not the full-fledged, recommended For extra lazy web designers (is that redundant?)
error handling and logging procedure. You should who refuse to learn Smarty syntax, there is a solution
still log errors to a file, a database or a syslog, or that lets them use their favorite WYSIWYG editor
use one of the existing log classes for that job (Macromedia Dreamweaver MX) and still leverage the
(look at PEAR::Log). power of PHP and Smarty.
Figure 1 shows all of the features of Dreamweaver
templates. Two Editable, one Repeating and one

Figure 1: Dreamweaver MX Template Features

Figure 2: Dreamweaver template source code - smarty_mx.dwt


<!-- TemplateBeginRepeat name="items" -->

<tr>
<td><!-- TemplateBeginEditable name="items.name" -->Apples<!-- TemplateEndEditable --></td>
<td>$<!-- TemplateBeginEditable name="items.price" -->14.70<!-- TemplateEndEditable --></td>
<td><!-- TemplateBeginIf cond="items.isnew" --><img src="new.gif"><!-- TemplateEndIf --></td>
</tr>

<!-- TemplateEndRepeat -->

February 2003 PHP Architect www.phparch.com 52


FEATURES Using, Reusing and Extending Smarty

Optional region are used. In Dreamweaver language, will produce a new row for every item in the list.
Editable regions are the only part of the page you are Pay attention to the names of the regions in the
allowed to change in a file based on a template. In the light blue boxes above. We use these names to con-
same manner, you can control the visibility of vert this Dreamweaver template into a Smarty tem-
Optional regions, and you can duplicate Repeating plate.
regions as many times as you like. These regions will This Dreamweaver template source code can be
perform slightly different functions when transfered to seen in Figure 2, and the Smarty template code that
a Smarty template that we will produce from this tem- we want to produce from this Dreamweaver template
plate. We will replace Editable regions with regular is shown in Figure 3. Note the region names we used
template variables, Optional regions will form if in Dreamweaver. They are shown in name pseudo-
statements, and Repeating regions will produce attributes in HTML comment tags. These comment
foreach statements. tags are actually a method that Dreamweaver uses to
This table was produced with the Repeating Table mark template regions, similar to the Smarty tags in
command (see the tool tip in the figure) so that the Figure 3. Note in fact, that the foreach tag in Figure
repeating region contains the whole table row, and 3 uses the same name as the corresponding repeating

Listing 7: prefilter.dreamweaver_template.php
<?php

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


function smarty_prefilter_dreamweaver_template(&$source, &$smarty){
$pattern[]=~<! TemplateBeginEditable name=(.*) >.*<! TemplateEndEditable >~Us;
$pattern[]=~<! TemplateBeginIf cond=(.*) >(.*)<! TemplateEndIf >~Us;
$pattern[]=~<! TemplateBeginRepeat name=(.*) >(.*)<! TemplateEndRepeat >~Us;
$replace[]={$$1};
$replace[]={if $$1}$2{/if};
$replace[]={foreach from=$$1 item=$1}$2{/foreach};
return preg_replace($pattern, $replace, $source);
}

?>

Figure 3: Smarty template code for that template

{foreach from=$items item=$items}


<tr>
<td>{$items.name}</td>
<td>${$items.price}</td>
<td>{if $items.isnew}<img src=new.gif>{/if}</td>
</tr>
{/foreach}

Figure 4: the results

February 2003 PHP Architect www.phparch.com 53


FEATURES Using, Reusing and Extending Smarty

Listing 8: example data for Dreamweaver template smarty_mx.php


<?php

include_once(debug.inc.php);
include_once(smarty.inc.php);

$user_name = Andrei Zmievski;

items[] = array (name=>Apple, price=>10.2, isnew=>false);


items[] = array (name=>Pear, price=>1.0, isnew=>true);
items[] = array (name=>Pecl, price=>0.5, isnew=>true);

$vars=compact(user_name, items);
smarty(smarty_mx.dwt, $vars);

?>

region. This also holds true for simple template vari- their inner workings. In the end, the code itself is
ables, and the condition in the if smarty tag (the clean and readable (or at least as readable as regular
same as the one in optional region). expressions can be).
Listing 7 shows a pre-filter function that does the These arent even all of the features of Smarty. I

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


conversion from Dreamweaver template to a Smarty havent even made mention of caching, configuration
template. Since there are three types of regions, the files or resources (the ability to read template code
$pattern array has three regular expressions, just like from a database or any other source). Ill leave these
the $replace array. The filter uses only the simplest topics as an exercise to the user (or at least another
of regular expressions, yet does so much to ease the article).
lives of PHP programmers and web designers. I can only hope that I managed to bring you closer
Finally, Listing 8 contains sample code that gener- and give you an insight to advanced Smarty features
ates data to fill this Dreamweaver-generated, Smarty- and how they can be useful. If you havent used
parsed template. The result can be seen in Figure 4. Smarty yet, I hope that you are now able to recognize
its potential as a useful website or web-based applica-
At the End tion development tool.

It is really amazing how such a small amount of code


can produce such a dramatic result. Actually, most of
what weve done here isnt even possible using other php|a
template engines! And we did all this without need-
Tomica Jovanovic is a freelance programmer from Serbia, working his
ing to inspect any of the Smarty classes to decipher way trough college. You can contact him at tomica@mbox.co.y

Connect with your database


Publish your data fast with PHPLens
PHPLens is the fastest rapid application tool you can find for
publishing your databases and creating sophisticated web
applications. Heres what a satisfied customer, Ajit Dixit of Shreya
Life Sciences Private Ltd has to say:
I have written more than 650 programs and have almost covered 70% of MIS,
Collaboration, Project Management, Workflow based system just in two
months. This was only possible due to PHPLens. You can develop high
quality programs at the speed of thinking with PHPLens
Visit phplens.com for more details. Free download.

February 2003 PHP Architect www.phparch.com 54


FEATURES

Data Fingerprinting with


FEATURES

Hash Functions in PHP


By Leon Vismer

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


After the Jan 2003 article from Theo Spears Writing that a download hasnt been corrupted has used a hash
Secure PHP Code, I am sure we have all done some function. A hash function is a cryptographic function
auditing of our code, fixing up those security eye sores. that takes variable length input and creates fixed-
In this article I would like to reinforce and enhance even length encrypted output, often referred to as a signa-
further the security of your PHP applications by focus- ture or message digest. The digest serves as a unique
ing on insuring the integrity and the authenticity of our fingerprint of the original file or message. If we alter
information. We need to know that the information a the original message and re-calculate the hash, we will
user intends to send is the information we are actually have a completely new signature. Therefore, a hash
receiving! The key concept here is how to make our function can provide us with a means of checking for
information tamper proof. message integrity.
As briefly stated above, hash functions are often used
The Problem to create a fingerprint of a certain file to make sure that
the file has not been altered. For example, on a typi-
I am sure that most of us have seen code that makes cal Linux system you could hash your important files
use of a URL to pass information to the program (ie using the following commands:
http://www.site.com/admin.php?function=e
dit&user_id=1). Many times, these URLs are seen # md5sum /etc/passwd > hash_important.txt
on intranet systems, where we often think that security # md5sum /etc/group >> hash_important.txt
is not a concern. We have a firewall, dont we? After all,
we are using a cookie to check that authentication has The file hash_important.txt is now a two line
occurred, right? The concern for any security-con- file, one line for each of the file signatures. Saving this
scious developer would be what if I changed user_id file to a read-only device, one can periodically check if
in the above URL to 2? Would I be able to edit anoth- they are still the same by running md5sum on the files
er users information? Surely not! We need to insure again. If the signature for a given file differs from the
that the URL is tamper proof. one recorded in our original hash_important.txt file,
someone has tampered with the file. It could have
The Solution and Some Definitions
REQUIREMENTS
Certainly many of you reading this article have heard
of hash functions, and at least some of us use them PHP Version: 4.0.6 and above (4.1 recommended)
often to check the integrity of certain files that we have Additional Software: mhash
downloaded. Anyone who has used md5sum to check

February 2003 PHP Architect www.phparch.com 55


FEATURES Data Fingerprinting with Hash Functions in PHP

been you adding a new user! sage with a secret key is referred to as a MAC. The
function used to generate a MAC must be a one-way
Why Hash Functions Are Appealing function. By combining the use of a MAC and crypto-
graphic hash functions, we get HMAC (Hashed
First, hash functions create one-way hashes, meaning Message Authentication Code). HMAC is the most
that while it is easy to compute the message digest, it common form of MAC. The message is combined with
is designed to be (theoretically) impossible to use the a secret key and runs through the hash function. The
digest to compute the original input. result is then combined with the secret key and runs
Second, the hash algorithm itself does not need to be through the hash function again, as described below:
kept secret and in fact should be in the public domain.
In this way, were not dependent on secrecy for securi- HMAC(Data) = Hash(SecretKey+(Hash(SecretKey+Data)))
ty.
Additionally, with a hash function it should be impos- Generally, MAC is used between two parties sharing
sible to create the same message digest for two differ- a secret key, enabling them to validate information sent
ent pieces of input, and the message digest created between the parties. This is the exact type of mecha-
from a hash function is a fixed length. nism we are looking for to insure integrity and authen-
ticity. However, parties do not need to share a secret
MD5 and SHA-1 key in this example, as we will be the only party. The
HMAC specification and implementations are described

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


The most common hash functions include MD5 and in RFC2104.
SHA-1. MD5 is a hash function developed by Ron
Rivest, one of the inventors of the RSA public-key Implementing and Using
encryption scheme. The MD5 algorithm produces a HMAC in PHP
128-bit (32 character) output. (RFC1321)
SHA-1 (Secure Hash Algorithm-1) is an MD5-like Solution 1 - Using mhash
algorithm that was designed to be used with the Digital The best way to use HMAC in PHP is to include
Signature Standard (DSS). The NIST (National Institute mhash support from
of Standards and Technology) and NSA (National http://mhash.sourceforge.net. Mhash pro-
Security Agency) are responsible for SHA-1. The SHA-1 vides an array of hashing algorithms (including SHA1,
algorithm produces a 160-bit (40 character) output. MD5 etc.) used to compute checksums, message
digests and other signatures. HMAC support in mhash
Alphabet Soup, MD5 and SHA implements message authentication as described in
With URLs in PHP! RFC2104.

If we apply the MD5 hash function to our URL exam- Installing mhash
ple to hash the user_id, we get:
http://.../admin.php?function=edit&use You can use the phpinfo() function to check if
r_id=1&hash=c4ca4238a0b923820dcc509a6f75 mhash support is included in your PHP distribution.
849b. The hash variable now insures the integrity of Under the Additional Modules section, you should see
our information. If the hash variable does not equal mhash. If mhash is not included, you have some work
MD5(user_id), we know that someone has changed our to do.
user_id. You may choose to download the latest version of
The astute among us would notice something wrong mhash from the SourceForge site. Keep in mind that
here. If I can guess the hash function being used, in our youll need to be root on your system to install it. The
case MD5, one can easily create a message digest valid normal 5 step approach for doing this on a typical
for the program, for example: Linux system is:
http://.../admin.php?function=edit&use
r_id=2&hash=c81e728d9d4c2f636f067f89cc14 # tar zxvf mhash-0.8.17.tar.gz
862c. This illustrates rather vividly that it is not # cd mhash-0.8.17
# ./configure
enough to provide information integrity. We need to
# make
provide proof of authenticity as well, which well move # make install
on to next.
To build mhash support into PHP, you will have to
Message Authentication Code
add an argument for mhash to the ./configure
(MAC)
command. For example:
$> cd php-4.2.3 (or the source version you
The result of a hash function that combines a mes-

February 2003 PHP Architect www.phparch.com 56


FEATURES Data Fingerprinting with Hash Functions in PHP

Listing 1 This would create the following as our hash:


function hmacMD5($data, $key) {
// Using our definition: 5d2974773aa244ba1c7a8c516b363e5a6f37dec5
// HMAC(Data) = Hash(SecretKey +
// (Hash(SecretKey + Data)))
$block_size = 64; // byte length for md5 Solution 2 - A Function to Emulate HMAC-MD5
if ( strlen($key) > $block_size ) (RFC2104, RFC2085)
$key = pack(H*, md5($key)); If you cannot install the mhash library, then use the
// Fill the rest of the key with NULL if
// shorter than block_size function in listing 1 to create the HMAC-MD5. The
$key = str_pad($key, $block_size, function only supports MD5 HMAC, and not SHA-1.
chr(0x00)); Using our hmacMD5 function creates the exact same
// Create some padded vars to XOR
// with our key HMAC as the one using mhash:
$ipad = str_pad(, $block_size,
chr(0x36)); $hmac = hmacMD5(user_id=1,
$opad = str_pad(, $block_size, this is our secret key!);
chr(0x5C)); echo $hmac;
$k_ipad = $ipad ^ $key; // returns 7ee29e215dcf47050acfd9b50b184fce
$k_opad = $opad ^ $key;
return md5($k_opad . pack(H*,
md5($k_ipad . $data)));
} Our hmacMD5 function uses the definition of a HMAC
from RFC2104 (Section 2):

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


are using)
$> ./configure with-apxs=/location \ H(K XOR opad, H(K XOR ipad, text))
with-mysql=/mysql_location \
.... other modules you normally
and implements this in PHP. First we make sure the
include ...
with-mhash
block-size of the key is correct and pad the key with the
$> make NULL (0x00) character if needed. We then need two
$> su root padded values. One padded by Ox36 (the literal char-
# make install acter 6), and the other padded by 0x5C (which is a
backslash - \). The two values are XORed with the key,
On my relatively stock and standard RedHat 8.0 sys- and thereafter the XOR key values are used in the hash
tem, I rebuilt PHP version 4.2.3 with mhash support fol- function with our message data.
lowing the instructions I have laid out above.
Everything was up and running in less than 10 minutes. Putting It All Together
On my Debian (Woody with some testing/unstable
stuff) system I used the libmhash-dev and libmhash2 For the rest of the article, we will focus on using the
deb files with the php4 and php4-mhash (4.2.3) deb information gathered so far, and HMAC specifically as
files. described above to allow us to protect the integrity and
Lets take a look at some examples using mhash to authenticity of GET and POST variables, which can be
create a HMAC. Lets assume we would like to create a set in URLs and forms.
HMAC for our user_id=1 example from above. The Finally! Were ready to jump in and get our key-
mhash function takes three parameters, the hash type boards dirty. Well, almost. Listing 2 contains the code
we would like to use, the data/message itself and an to work with the mhash function and return our
optional secret key. If the key is omitted, we will just HMAC-MD5. We will actually use a wrapper function
have a MD5 hash as the output from our example. called mhash_cgi. Other utility functions exist for this
purpose as well. If the ENABLE_MHASH variable is set
$hmac = mhash(MHASH_MD5, user_id=1, to true, we will use the mhash library. Otherwise, we
this is our secret key!); will use our emulation function.
$hmac = bin2hex($hmac); Our mhash_cgi function takes two parameters; the
echo $hmac;
// returns 7ee29e215dcf47050acfd9b50b184fce $data we would like to create a MAC for and the
secret key we would like to use. The function will
return a 32 character hash. The following code will cre-
To create a SHA-1 HMAC, just replace the ate a hash for our initial URL example:
MHASH_MD5 hash type variable with MHASH_SDA1, like
this: include_once(./mhash.php);
$hash = mhash_cgi(user_id=1, SECRET);
$hmac = mhash(MHASH_SHA1, user_id=1,
this is our secret key!);
where SECRET is the secret key we are using as

February 2003 PHP Architect www.phparch.com 57


FEATURES Data Fingerprinting with Hash Functions in PHP

defined in Listing 2. The above will always create our hash will allow us to determine whether or not some-
hash as one has tampered with our URL. Assuming that it is
unlikely someone will guess our secret, we are looking
7ee29e215dcf47050acfd9b50b184fce fairly safe. Just dont make the secret key your animals
name or something similar. Using our mhash_check
using our secret key (as previously seen). If you function from Listing 2, we will be able to verify if the
change ENABLE_MHASH to true and the mhash library original hash matches our current hash.
is installed, you will get the same hash value back. This

Listing 2
1 <?php
2
3 // Create file as mhash.php
4
5 define("ENABLE_MHASH", false);
6 define("SECRET", 'this is our secret key!');
7
8 function hmacMD5($data, $key) {
9 // Using our definition:
10 // HMAC(Data) = Hash(SecretKey, Hash(SecretKey, Data))
11 $block_size = 64; // byte length for md5
12 if ( strlen($key) > $block_size )

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


13 $key = pack("H*", md5($key));
14 // Full the rest of the key with NULL if shorter than block_size
15 $key = str_pad($key, $block_size, chr(0x00));
16 // Create some padded vars to Xor with our key
17 $ipad = str_pad('', $block_size, chr(0x36));
18 $opad = str_pad('', $block_size, chr(0x5C));
19 $k_ipad = $ipad ^ $key;
20 $k_opad = $opad ^ $key;
21 return md5($k_opad . pack("H*", md5($k_ipad . $data)));
22 }
23
24 // Return a HMAC using the md5 hash function
25 function mhash_cgi($data, $key) {
26 // If we enabled mhash usage use it
27 if ( ENABLE_MHASH ) {
28 // Convert the binary hash to something legable
29 return bin2hex(mhash(MHASH_MD5, $data, $key));
30 } else {
31 return hmacMD5($data, $key);
32 }
33 }
34
35 // Check if the hash has changed
36 function mhash_check($data, $hash) {
37 $check_hash = mhash_cgi($data, SECRET);
38 // Check if our original data hash matches our current data hash
39 if ( $hash != $check_hash ) {
40 echo "Mmm, some CGI variables have been tampered with, abort!";
41 exit();
42 }
43 return true;
44 }
45
46 global $check_hash;
47 $check_hash = array(
48 'website' => 'user_id:section',
49 'invoices' => 'user_id:action:amount:date'
50 );
51
52 // A new improved hash function to use
53 function mhash_improved_check($lookup, $method) {
54 global $check_hash;
55 $method == 'GET' ? $_VAR = $_GET : $_VAR = $_POST;
56 $a_cgi = explode(":", $check_hash[$lookup]);
57 foreach ( $a_cgi as $var )
58 $data .= $var.'='.$_VAR[$var];
59 mhash_check($data, $_VAR['hash']);
60 }
61
62 ?>

February 2003 PHP Architect www.phparch.com 58


FEATURES Data Fingerprinting with Hash Functions in PHP

Listing 3
Notice that we do not create separate hashes for the
<!-- To be named login.html -->
<html> separate GET variables. We create a hash of the full
<body bgcolor='#FFFFFF'> dataset we wish to check. Next, we create the URL to
Welcome and please login ...<br> be used for our first menu item and likewise for the sec-
<form action='login.php' method='post'> ond menu option.
Username: <input name='user' size=20><br>
Password: <input name='passwd' size=20><br> Listing 5 is our moment of truth. We check to see if
<input type='submit' value='Login'><br> the variables we received have been tampered with and
</form> if not, the user is allowed to continue to the chosen sec-
</body> tion. Clicking on the two links for sports and news
</html>
should be successful. However, if you change the URL,
Illustrating GET Method Variables the user_id or section variables, you will not be
allowed to continue. I have purposefully broken up the
We would like to use our new knowledge to limit login, menu and verification sections into separate files
access for certain users to only certain areas defined by to show the stages more clearly.
the site administrators. In our example, a user first logs In a manner similar to what weve already done up to
into our system, after which we build a menu they can this point, one can also use the MAC with POST vari-
use to navigate to certain sections on our site. In our ables as shown below.
menu URLs we will use our hash functionality to make

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


<input type=hidden name=user_id
sure they cannot access other sections. value=1>
Listing 3 contains the login form the user employs. <input type=hidden name=function
Any username and password will grant access. Listing value=remove_funds>
4 contains the code to build our dummy menu with the <input type=hidden name=hash
clients details. After login, we set the users id to 1 and value=the_derived_hash>
create the first menu hash. <input name=fund size=20>$200

$m1_hash = hash_cgi(user_id=. This way we will insure that a user cannot remove
$user_id.section=4, SECRET) funds from someone elses account by creating a
dummy form.
Listing 4 I hope that these examples have shown you how to
incorporate the concept of checking integrity and
<html>
<body bgcolor='#FFFFFF'>
authenticity into your code.
<?php
A Simpler Way of Checking
include_once('./mhash.php');
Your Hashes
echo "Welcome to <b>". $_POST['user'] ."</b><br>";
// The user logs in, check access in a database If you take a look at mhash_check, you might notice
// We presume the user has gained access that it can be a very laborious way to check that your
// Lets set the user_id
$user_id = 1; HMAC actually matches the original. We can extend
the mhash_check function by having a lookup array
// Menu option no 1, the user has access to news to map GET or POST variables to values that need
$m1_hash = mhash_cgi("user_id=".$user_id
."section=4", SECRET); checking:
echo "m1 ". $m1_hash ."<br>";
$m1_url = "<a href='section.php?user_id= global $check_hash;
$user_id&section=4&hash $check_hash = array(
=$m1_hash'>Access the website => user_id:section,
news section</a>"; invoices => user_id:action:amount:date
);
// Menu option no 2, the user has access to sport
$m2_hash = mhash_cgi("user_id=".$user_id
."section=3", SECRET);
echo "m2 ". $m2_hash ."<br>"; Using the check_hash array, we can now build an
$m2_url = "<a href='section.php?user_id= array that accepts a unique identifier that weve defined
$user_id&section=3&hash= for a specific function in our project to explain the GET
$m2_hash'>Access the
sport section</a>"; or POST variables that actually make up the hash. As
you can see, our website identifier uses two variables,
echo $m1_url ."<br>". $m2_url; $user_id and $section, to create the hash. The
?>
</body> order of the variables we use to create the original hash
</html> should match the order here.

February 2003 PHP Architect www.phparch.com 59


FEATURES Data Fingerprinting with Hash Functions in PHP

Using the above method, we would only need to Some Other Alternatives
parse the unique identifier and the request method we to Consider
are using to our mhash_improved function.
What about timed hashes?
function mhash_improved_check($lookup, $method)
To create a HMAC that expires, try incorporating the
{ date into the secret key somewhere. Typically a good
global $check_hash; size for the secret key is 48 characters, allowing the
$method == GET ? $_VAR = $_GET date stamp to be 16 characters (remember the 64
:
$_VAR = $_POST; block size). For example, a newly set key
$a_cgi = explode(:, $check_hash[$lookup]);
this is our secret key!.date(YmdH) ;
foreach ( $a_cgi as $var )
$data .= $var.=.$_VAR[$var];

mhash_check($data, $_VAR[hash]); would allow for a valid HMAC that will last at least
} one hour, or in the worst case, about 1 minute.
Using the above techniques, one could even build a
Our improved function first checks which global vari- HMAC that will allow someone to do something before
able array to use for performing lookups. It then the time expires, like getting a discount on some items
retrieves the list of variables to create the hash. within 30 days of placing a previously configured order
(once again date is our friend).

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


Looping through these variables, the function builds
the data needed for our hash function. Using our pre-
vious example, we could substitute $expire = strtotime(+30 days);
$expire_date = date(Ymd, $expire);
$key = SECRET.$expire_date;
mhash_check(user_id=$user_id .
section=$section, $hash);
Our new key would allow someone to respond to a
link in email or a user profile, and it would be valid for
with the improved function call to
30 days.
Conclusion
mhash_improved_check(website, GET);

If you are a PHP Extension and Application Repository


Dont you just love improvements? We dont have to (PEAR) supporter, you should head over to
keep track or recalculate how we actually got to the http://pear.php.net and take a look at the
original data. Our new function computes this for us. Message module that supplies some classes and func-
tions to create HMAC using the mhash library. The fol-
lowing is from the README in the Message module. If
Listing 5 the mhash extension is detected, then it will be used.
1 <!-- To be named section.php --> Otherwise it will fall back to using md5() and sha1()
2 <html> only.
3 <body bgcolor='#FFFFFF'> We have looked at a way to use HMAC in PHP to
4 <?php
5 include_once("./mhash.php"); allow our GET and POST variables to retain their integri-
6 $user_id = $_GET['user_id']; ty and authenticity. I am sure you will be able to take
7 $section = $_GET['section']; the HMAC concepts further to deploy them into other
8 $hash = $_GET['hash'];
9
areas of your PHP development. As always, have fun!
10 mhash_check("user_id=$user_id".
"section=$section", $hash);
11
12 switch ( $section ) {
References
13 case '2':
14 echo "Welcome to finance ..."; RFC2104
15 break; http://www.ietf.org/rfc/rfc2104.txt
16 case '3': MHASH Website:
17 echo "Welcome to sport ..."; http://mhash.sourceforge.net/
18 break;
19 case '4':
20 echo "Welcome to news ...";
21 break;
22 } php|a
23 ?>
24 </body> Leon is a developer based in Cresta, South Africa, who specializes in web
25 </html> application development.

February 2003 PHP Architect www.phparch.com 60


TIPS & TRICKS
TIPS & TRICKS

Tips & Tricks


By John W. Holmes

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


Custom Error Handler error was preceeded by an @ (normally used to sup-
press errors), your function will still be called, but
Are you going live with your ultra-important site? error_reporting(), when you read it, will have a
Want to knowanytime any of your users get an error? value of zero.
An easy way to do this is to use set_error_han- You could even have different levels of error handling
dler() in your code and define your own function to for different parts of your scripts. Say you have a cer-
handle errors. tain part of your script that, if it causes an error, you
Instead of just displaying a message to the user, want to log it to a certain file. You can use
which may or may not ever make it back to you, you
can use your own function to customize the message
and perhaps log it in a database or file, or even send Parse or compile errors, along
yourself an email message.
The set_error_handler() function accepts one with other major errors, cant
parameter, the name of the function you want called
when PHP encounters an error. That function can be caught by your custom
accept up to five parameters: the error code, a string
describing the error, the filename the error occurred in,
function.
the line number the error occurred on, and a possible
last parameter - the context in which the error set_error_handler() to define a new function to
occurred. The manual describes the last parameter as handle errors for that section of the script and then,
an array that points to the active symbol table at the afterwards, use restore_error_handler() to set
point the error occurred. the error handler back to what it was before you
Parse or compile errors, along with other major changed it.
errors, cant be caught by your custom function, as
they will occur before your script even begins to run. When Was The Last Error?
But for basic errors and warnings that your users may
come across, this is a perfect solution. Okay, so now youve got your custom error logging
Some other caveats that the manual notes: your func- script up and going. Youre more than likely storing the
tion is called regardless of the value of time of the error, the file the error occurred in and the
error_reporting(). You can, however, check the line number, etc. One tip given by Chris Shiflett via the
value of error_reporting() in your script and PHP Mailing List was to add a global variable to your
decide what to do. Also, if the code that triggered the error function that stores the last time an error was

January 2003 PHP Architect www.phparch.com 61


TIPS & TRICKS

Listing 1
<?php

//get_mtime() would be a user_defined function to


//return the current microtime using mtime()
function my_error_handler($errno, $errmsg, $filename, $lineno)
{
global $last_error;

$time_since_last_error = get_mtime() $last_error;

// rest of code

$last_error = get_mtime();
}

set_error_handler("my_error_handler");

trigger_error("start of loop");

for($x=0;$x<100;$x++)
{
echo $x;
}

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


trigger_error("end of loop");

?>

encountered. Using something similar to whats shown nocache value. Internet Explorer chokes on the
in Listing 1 would enable a dual purpose for your error nocache value and will not download the file. The fix
logging function. for this is to change the session.cache-limiter
Your function could also use it as a timer or bench- setting in php.ini to an empty value or no-store. The
mark for certain parts of your script. If you wanted to Microsoft Knowledge Base article on this bug provides
know how long a certain part of your code was taking, more details:
you could simply use trigger_error() to write a
start of code error to the log, and use it again after http://support.microsoft.com/?kbid=
the code block you are testing to write an end of 323308
code error to the log. When you are developing your

Listing 2
header(Content-Disposition: Attachment; Filename=download.zip);

code, you can display this benchmarking to the screen, Manual Mail on Windows
along with any other errors you come across, or change
your error logging to a file and get it there. Getting mail() to work on windows can be a pain
sometimes and the error messages that mail() spits
File Download Over SSL Bug back arent very helpful. One tip to find out where the
error is occurring when using SMTP on Windows is to
A popular technique in many programs is to use PHP try and manually send a message through the SMTP
as a download manager. Requests will be sent to a PHP server from the web server. To do so, open up a com-
script that validates whether the current user should mand window, type telnet <smtp_server> 25
receive the requested download or not. If its deter- and press Enter. Substitute the SMTP server youre try-
mined that the user should get the file, then a header ing to test (or localhost). This will connect you to port
similar to Listing 2 is sent to the user, followed by the 25, the default for SMTP. You should see a response
data for the file download.zip, for example. similar to the following:
Normally, this works just fine. But the combination of
the above, PHP sessions, and Internet Explorer will 220 <server> Microsoft ESMTP MAIL
cause an error in Internet Explorer. The problem stems Service, Version: 6.0.2600.1106 ready at
from sessions sending a Cache-Limiter header with a Mon, 27 Jan 2003 00:27:03 -0500

February 2003 PHP Architect www.phparch.com 62


TIPS & TRICKS

Listing 3
1 <?php
2
3 $ini_file = preg_replace('/^([a-z0-9_.-]+)\s?=\s?"?(.*)?"?$/im',
4 "<input type=\"text\" name=\"$1\" value=\"$2\"></td></tr><tr><td>\n",
5 $ini_file);
6
7 $ini_file = preg_replace('/^;(.*)$/em', '@htmlentities(stripslashes("$1"));', $ini_file);
8
9 $form = "<form method=\"POST\" action=\"{$_SERVER[SCRIPT_NAME]}\"><table><tr><td>{$ini_file}</td>
</tr><tr><td><input type=\"submit\" value=\"Save All Settings\"></td></tr></form>";
10
11
12 ?>

would actually press the Enter key, and the lines marked
Type in helo me and press Enter and you should with >> are the SMTP servers response and everything
see a response similar to the following: else is entered by you:

250 <server> Hello [<ip_address>] Data <Enter>


>> 354 Please start mail input.
From this point on, youll be entering the email head- Subject: This is my Subject <Enter>

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


ers providing the from and to address, subject, and <Enter>
data for the message. Hopefully, any errors you get This is a test <Enter>
from this point on will be more revealing than whats . <Enter>
shown by mail() simply failing in your script. After >> 250 Mail queued for delivery.
the above message, type in mail from:
your_email@domain.com and press Enter. You If all of this works correctly, then your SMTP server is
should see a response similar to the following: set up correctly so that it can work by using mail() in
your script. The exact responses from the SMTP server
250 2.1.0 will more than likely vary, but the goal is the same. Any
your_email@domain.com....Sender OK errors you encounter as youre doing this will be more
informative than mail() failing and should make it
Next, type in rcpt easier to track down the reason for the error.
to:someone@another.domain.com. This is where you
set the To address for the email message. You should HTML Based Configuration
see a response like:
In the first issue of PHP|Architect I covered how you
250 2.1.5 someone@another.domain.com could use parse_ini_file() to load a php.ini style
configuration file into your script.
Now, type in Data and press Enter. Next you enter parse_ini_file() makes it easy to load the entire
the Subject: header followed by pressing Enter twice. file into an array that you can use. While this is great to
From here on is where you enter your email message. the text-editor qualified people that may be using
For this example, the only text well send is This is a your program, a large amount of people may be
test. To end the text of the message, enter a single expecting an HTML interface to the file, so they can do
period on one line by itself and press Enter. This entire the entire configuration over the web.
dialog will look like this, where <Enter> is where you If you use some regular expressions to replace parts

Listing 4
<?php

foreach($_POST as $key=>$value)
{
if( preg_match ("/[^a-z0-9]/i", $value) )
{ $value = '"' . $value . '"'; }

$ini_file = preg_replace("/^".$key."\s?=.*$/m", "$key = $value", $ini_file);


}

?>

January 2003 PHP Architect www.phparch.com 63


TIPS & TRICKS

of the configuration file with form elements, adding a and will be inserted into the HTML form and table.
<form> tag and submit button, you can turn that file Now, to process the data submitted, you would read
into an HTML form to allow users an alternate way to the old configuration file into a variable again and loop
fill out the values for the file. By looping through the through the $_POST data that was submitted. Each
$_POST or $_GET array upon submission, you can $_POST element will be a variable from your configu-
then write the new values back to the file. ration file, so you can use a loop like that shown in
This method has the benefit that your program still Listing 4 to create your new $ini_file.
uses a plain text configuration file that can be edited The first match will ensure that any value submitted
rather easily through any text editor, while also provid- thats not alpha-numeric is enclosed with double
ing a web interface for those that are less technically quotes and the the second match replaces the old data
astute. with what was entered in the form. You may need to
You would use the regular fopen() and fread() throw in a stripslashes() to $value, depending
functions to read the value of your configuration file upon your magic_quotes_gpc setting. Use the new
into a variable (called $ini_file in the example). value of $ini_file to write over the old configuration
Then, the code shown in Listing 3 would be used to file and now all of the values have been updated.
convert the variable=value pairs in the file into form ele- Note that this method wont account for inline com-
ments. The code also removes the semi-colon from the ments after the variable=value part of a line or notice
beginning of each line (which are comments in the file) when you have commented out a specific
and places everything into an HTML form and table. variable=value line so its not used. However, a com-

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


So, a configuratin file with the lines plete class with error checking and more features could
be built around this method to make it useable with
;Name of Site any system.
site_name = My Site

php|a
will be converted to
John Holmes is a First Lieutenant in the U.S. Army and a freelance PHP
and MySQL programmer. He has been programming in PHP for over 4
Name of Site
years and loves every minute of it. He is currently serving at Ft. Gordon,
<input type=text name=site_name Georgia as a Company Commander with his wife, son, and another one
value=My Site> on the way.

php|architect The Magazine For PHP Professionals

February 2003 PHP Architect www.phparch.com 64


BOOK REVIEWS
BOOK REVIEWS

For Your Reading


Pleasure

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


Building Secure Server Servers With Linux of Linux.
by Michael D. Bauer In addition to not being a thorough source for
Published by OReilly issues regarding firewalls in particular, the book
also limits its coverage of distributions to RedHat,
Debian and SuSE. Even with these limitations,
$44.95 (US)
however, the vast majority of the information pro-
$69.95 (Canada)
vided by Building Secure Servers could easily apply
to any computer running under any operating sys-
A good friend of ours
tem, albeit only in a theoretical way. For those sys-
once said that there is no
tems administrators who use the distributions cov-
right way to build a solid
ered by it, this book is a must-read if you want a
and secure serveronly
low maintenance server that is as secure as it can
less wrong ways. Its
be made.
only after reading Building
The first three chapters of the book alone are, in
Secure Servers With Linux,
my opinion, worth its price. The first deals with
however, that one comes
threat assessment and analysisan invaluable tool
to realize how easy it is to
for a systems administrator that can help prioritize
make mistakes and how
areas of concern and action items. The second
dangerous they can be.
chapter deals with basic perimeter network
Michael Bauer, who is also the author of the
design, and the third with the essential concepts
Paranoid Penguin column that appears in the pop-
of hardening Linux to make it more secure. By
ular Linux Journal, examines how a good Linux-
the time you get to chapter four, two reactions are
based bastion host can be built from the ground
appropriate: if you are about to install your
up. Even though bastion host is a term often
servers, you will feel enlightened. If you are
used for firewalls, this book does not cover firewall
already running a server farm, chances are that
setup per se, though most of the tips provided
you will feel that strange sensation known as
would easily apply to firewalls as well. is only lim-
panic.
ited to the RedHat, Suse and Debian distributions
The chapter on perimeterdesign is particularly

February 2003 PHP Architect www.phparch.com 65


BOOK REVIEWS For Your Reading Pleasure

good. It explains how a network can be designed ponents of PHP, from the basics all the way up to
so that it has a minimum number of entry databases, in a logical and coherent fashion. The
pointsthose where you want to keep your eyes book also includes a section on advanced PHP
open for suspicious activitywithout compromis- that deals with topics like XML management,
ing its redundancy. Any ISP that has three pipes Javascript and COM interoperation and code secu-
coming into their network, four diesel generators rity.
and one router should be reading this book! APIP is loaded with tons of examples, which is
As we mentioned, unfortunately, Building Secure nothing new for any technical publication. Its the
Servers only deals with specific Linux distributions. fact that (for a change) the examples might actu-
While this makes it a very hands-on book (systems ally be useful in a real-life web application that
administrators hate theory) it misses the bigger makes APIP unique. This is a sharp departure from
opportunity of providing a catch-all reference for most books, in which examples are almost an
every Linux or UNIX-based operating system afterthought and often do not offer any real-world
administrator. value.
Let me give you an example of why I think that
A Programmers Introduction to PHP 4.0 this book is worth its price if youre a beginner
by W.J. Gilmore who would like to get started on the right path to
PHP knowledge. One of its chapters is dedicated

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


Published by Apress
exclusively to strings and regular expressions. This
grouping seems completely natural, and yet for
$34.95 (US)
the most part these two topics are always treated
separately (even the PHP manual handles them in
The first aspect of this
two different sections). The result is that most
book that strikes us is its
beginners are so confused about how regex
title, which contains no
worksor even how it interacts with the other
fancy catchwords or
string-manipulation functions in PHPthat they
promises that the reader
end up finding incredibly complex solutions to
will be able to acquire
problems that can be easily solved with a couple
some exotic ability
of calls to the right preg_ or ereg_ function.
through its pages. No, this
Another interesting aspect of APIP is its index,
is an introduction to PHP 4
which spans well over twenty pages and ensures
from a programmers per-
that you can find repeated, consistent value in the
spective, and its refresh-
book as a well thought-out reference well after
ing to see that the author and the publisher are
youve finished reading it to learn PHP.
more interested in getting that message across
In conclusion, APIP is an excellent resource for
than attracting our attention with a flashy title.
learning PHP, regardless of whether youre coming
Then again, Apress is no ordinary publisher,
at it fresh or with a previous understanding of how
either. They were born out of two authors finding
programming works. Furthermore, if youre look-
a need for better material in the world of technical
ing for a good reference book that contains plen-
books (very much like php|a was born) and decid-
ty of examples and is excellently organized, this
ing that they could do it better than the others. As
book is for you!
such, Apress is probably one of the most author-
friendly publishers out thereprobably the only
ones who publish their royalty rates and contracts
on their websiteand their approach has resulted
in some fascinating, if unusual books.
As the title suggests, A Programmers Introduction
to PHP 4.0 (APIP from now on) is a book for devel-
opers who are just discovering PHP for the first
time. However, compared with other books that
cater to the same audience, APIP takes a decided-
php|a
ly different approach by laying out the many com-

January 2003 PHP Architect www.phparch.com 66


exit(0);

Hello, Mr. Gates?


By Marco Tabini

When was the last time you received an e-mail from Bill Gates? How about Scott McNealy?
Unless you count yourself among those in the top echelon of the computer software
industry, chances are that your answer to these questions is just like minenever. Yet, imag-
ine how interesting it would beboth for you and for the vendorsif you could have an
exit(0);

opportunity to discuss your ideas about their software directly with them in a truly open
forum.

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


One of the great advantages of open-source software that everybodyfrom advocates to
consumersalways seems to overlook is the accessibility of the people who are responsible
for each project.
As Im writing this column, Im sitting at our booth at the Open-source Weekend in Ottawa.
In between conversations with visitors, I had a chance to chat with the President and CEO
PostgreSQL Inc., and the organizers of the upcoming PHP conference in Montreal. The very
informal atmosphere here at the OSW affords us the perfect backdrop for throwing around
ideas and discussing the possibilities of OSS products in general and of PHP in particular.
The relevance of the accessibility factor in OSS is huge; being able to contact key people at
the click of a button (or by just walking across the hall when they have a booth next to yours)
makes it possible to create a healthy flow of ideas. This, in turn, ensures that the direction
taken by a particular project reflects the needs of the community that it serves, and that new
concepts are always taken into consideration.
Alas, though you may never have the opportunity to speak with the person responsible for
the development of the .Net Framework, Rasmus Lerdorf, the creator of PHP, is an active par-
ticipant in most of the official PHP mailing lists. Furthermore, Lerdorf, along with the entire
development team, is surprisingly dilligent about responding to messages, suggestions and
requests of all kinds. In addition, thoughts and comments flow through the mailing lists
unfiltered by any public-relations machine. Say what one might about having to sift through
some less-than-mature ideas to get to the truly compelling information, there is still value in
the idea that one click can get anyone in direct contact with those directly responsible for
the future of PHP.
Commercial products, for the most part, lack this capability. This is often because they
respond to the business strategy of entire corporations, which must rationalize their choices
by carefully filtering the information that passes through their respective organizations.
Customer support, when available, is usually only offered as a way to either solve a cus-
tomers problem or generate additional revenue. It is generally (and unfortunately) not used
as a tool for fine tuning a product to more closely match the communicated needs of its user
base.
Naturally, the flip side of free-flowing ideas is that you need some really strong leaders to
make sure that a project is not led astray by the pressure coming at it from all sides. Luckily,
PHP enthusiasts certainly dont have to worry about that.

February 2003 PHP Architect www.phparch.com 67

S-ar putea să vă placă și