Documente Academic
Documente Profesional
Documente Cultură
Enterprise Applications:
PHP in a Multitier Environment
Data Fingerprinting with Hash Functions in PHP
www.phparch.com
Visit www.zend.com
for evaluation version and ROI calculator
Technologies Ltd.
TABLE OF CONTENTS
php|architect
Departments Features
32 Creating a Customized
22 REVIEWS Template-Engine
LogiCreate by Andreas Demmer
67 exit(0);
Hello, Mr. Gates? 55 Data Fingerprinting with
Hash Functions in PHP
by Leon Vismer
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
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
Zend Encoder
REVIEWS
V.3.1.0
By Marco Tabini
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
Figure 1
Licensing Capabilities
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
Time Travel -
Breadcrumbs and
History with PHP
FEATURES
By Peter James
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.
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 {
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.
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
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
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
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( / );
$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
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
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
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-
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
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.
LogiCreate
Tap Internet
REVIEWS
LogiCreate is a web application framework, providing a common development base for web systems. By standardizing
Figure 3
Figure 4
PHP in an N-T
Tiered
World
FEATURES
By Dave Palmer
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-
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
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
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
Creating a Customized
Template-E
Engine
FEATURES
By Andreas Demmer
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
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?
Figure 1
Table 1
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
Figure 2
www.cyberbite.com
CYBERBITE
W
W E
E B
B H
H O
O S
S T
T II N
N G
G
Designed for PHP Programmers Virtual Private Servers & Dedicated Servers
Reliable Internet hosting solutions Guaranteed 99.95% uptime
By Marco Tabini
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
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 ?>
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
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
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-
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
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>
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-
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-
By Tomica Jovanovic
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
article. This code will be modified later to demon- Smarty supports several types of plugins. Some plu-
Listing 3: prefilter.literal_script.php
<?php
?>
Listing 4: prefilter.literal_style.php
<?php
?>
Listing 5: outputfilter.literal_cleanup.php
<?php
?>
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.
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.
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
<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>
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
?>
include_once(debug.inc.php);
include_once(smarty.inc.php);
$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
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
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-
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 )
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
$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§ion=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§ion=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.
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).
Listing 1
<?php
// 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;
}
?>
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
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:
Listing 4
<?php
foreach($_POST as $key=>$value)
{
if( preg_match ("/[^a-z0-9]/i", $value) )
{ $value = '"' . $value . '"'; }
?>
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-
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.
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
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.