SitePoint Sponsor
Article
PHP5: Coming Soon to a Webserver Near You
If you have more than a little to do with PHP [1], the Internet’s most popular server side programming language, you'll no doubt be aware that the successor to the current PHP version 4 is waiting in the wings.
Over the past couple of years, PHP5 has been a murmur on the fringes on PHP discussions, often surfacing in discussions of PHP4's Object Model (the support PHP provides for Object Oriented Programming).
For some, PHP5 is the "holy grail" that will deliver essential features they've been struggling to survive without. For others, PHP5 is vaguely unsettling, as it appears to threaten the status quo with suggestions of its adoption of a Java-like programming discipline.
Whatever your take on the situation (or if you weren't even aware of it), the hour of PHP5 is fast approaching. On 29th June, 2003 Sterling Hughes [2] announced PHP5 Beta 1 [3], the first release intended to give the PHP community as a whole a taste of what's coming.
I've been watching PHP5's evolution from the sidelines and mailing lists, trying to avoid getting distracted from real work with PHP4, but with the Beta 1 announcement, my curiosity finally got the better of me. In this article I'll be giving you a fairly in depth tour of the important features PHP5 delivers, based on the Beta release. We’ll also consider its significance for the future of PHP as a technology.
Up front, I can already say (for anyone who’s nervous about the impact of version 5 on their ability to hack with PHP), don't panic! The core PHP development team is well aware of PHP's greatest strengths including rapid prototyping, its ease of use, and down to earth approach to problem solving. In developing version 5 it's clear these principles remained the team’s top priority, despite the fact that the driving force was to make PHP more attractive to professional software developers.
Today we’ll talk about:
- Why PHP5?
- Test Drive: The new object model, a new database, exception handling, new XML engine, and more...
- Making Waves: the future with PHP
Important Note: PHP5 Beta 1 should not be used on a live Web server; there are still many issues to resolve. The beta release is intended for developers to get a feel for what's on offer, and help the PHP group with bug hunting.
Why PHP5?
Everyone loves PHP, right? Well everyone who uses it perhaps, but PHP4 has a number of areas which attract criticism from developers used to languages like Java. A large number of these criticisms result from PHP's history; PHP began life as a rudimentary procedural programming language.
Procedural Vs. Object Oriented
A procedural programming language is one in which the developer writes a list of instructions for a computer to follow in a "batch". The focus of a procedural language is the function, which is given priority over data (variables). Procedural programming is often very effective for one-off, "throw away" problem solving, such as a command line script to help backup a database, or a simple Web page with a form. The downside of procedural programming makes itself apparent when you start to build larger, more complex applications. Variables, being available from all areas, can easily be overwritten by mistake, and the general structure of the application typically leads to routines being reproduced, which results in big headaches when the inevitable—change—occurs.
One widely accepted alternative approach is the Object Oriented paradigm, which takes the view that data (or variables) has first priority, and should be carefully protected, while functions "back" the data with routines that handle their input and output. OOP leads to a different approach to application design, as, under this paradigm, developers are more concerned with what goes in and what comes out, than they are with the specific flow of a batch of procedures. OOP has proven a successful approach to dealing with change, helping developers to "abstract out" the elements of an application that are likely to vary, compiling them into well defined units that can be easily maintained.
Design Patterns
Arguably the greatest success of the Object Oriented paradigm is the notion of Design Patterns [4]. Attempting to do justice to Design Patterns in a short paragraph is not something I'm going to attempt (if you want some opinions try the Advanced PHP Forum [5]), but, in general, the argument "design is dead" could also be applied to programming. Design Patterns represent the condensed experience of the world’s leading software architects and make it possible for developers like you and I to learn from other people’s successes, rather than wasting time painfully learning from our own mistakes.
Some say that PHP4's support for OOP was added as an afterthought. Certainly, compared to a language like Java, PHP4 implements only the most rudimentary language features to make object oriented programming possible. Perhaps an indicator of the problem is the way references work in PHP4 (more info here [6])–essentially, they work in a way that’s the direct opposite of a language like Java. PHP4 references have, no doubt, confused everyone from beginners taking the next step from HTML, to experienced developers coming from Java, C++, and other backgrounds.
Experienced PHP4 developers have proved themselves capable of achieving high degrees of abstraction in their designs, resulting in powerful and flexible applications such as eZ publish [7] and Krysalis [8], but the degree of discipline required to reach this level of complexity is huge. What's more, where applying Design Patterns is concerned, PHP4 often requires ugly, non-standard workarounds (for example the Singleton Pattern [9]).
This combination of factors impacts the overall "development experience" with PHP4. For a large library of classes, problems are magnified, which makes it difficult to develop really solid, cohesive libraries that stand the test of time.
Back to Reality
Now, that's all very fine in theory. Soon we may all be talking about the ROI of our content management systems (which we knocked up one evening), growing beards and developing a serious demeanour. But let me put all this another way...
Perhaps the #1 question that highlights the flaw in the way today’s PHP applications are built is "How do I integrate the rest of my site with phpBB/vBulletin/Invision Board/***insert PHP forum here***?" Generally speaking, the answer is "Hack!"
Because a procedural approach to developing PHP applications has been the most popular method used with PHP4, re-use is low. Attempting to integrate existing PHP applications with your own usually means hours of studying and re-writing third party code.
As a result, most PHP projects today begin more or less from scratch, implementing authentication systems, form handling logic, template mechanisms, and more—stuff that’s been done countless times before, by thousands of other developers. Part of the problem here is that PHP4's object model has only partially succeeded in aiding the evolution of standard application components. Repositories like PEAR [10] and PHP Classes [11] highlight both the success and failure of the PHP4 object model.
The bottom line is that PHP developers are wasting many hours losing their hair as they re-invent wheels unnecessarily. PHP5 may well be the hair tonic you're looking for...
Error Handling
While I'm airing PHP4's dirty laundry, another weakness is the way in which errors are typically handled.
The way your code reacts when something goes wrong (as it inevitably will) is very important. Imagine your MySQL server was momentarily "down", for example. Did your site's visitor just lose an extensive, detailed rant they were about to post in your forum?
Today’s PHP developers have adopted many different approaches to error handling. Some have developed relatively complex error handling mechanisms (e.g. PEAR Error [12]). Others simply ignore error handling entirely, other than the occasional die() when someone goes horribly wrong.
The problem is not that you can't deal with errors at all, today, but that there is no standard, language-level solution to error handling. This means that when you use third party code, you'll often have problems unifying different error handling mechanisms. Also, your own approach to error handling may vary, so that code you wrote six months ago may not fit with code you write today.
The good news is that PHP5 has the answer!
XML Support
Given that rendering some subset of XML (e.g. HTML) is what PHP is really about, its varying support for XML leaves definite room for improvement.
Today, PHP uses three sets of third party libraries to make XML "happen":
- James Clark's Expat [13] SAX parser
- the Gnome Project’s libxml [14]library to provide a DOM parser
- Ginger Alliance's Sablotron [15] library for XSLT
Of the three, only the Sax extension [16] is something PHP developers can rely upon 100%. The XSLT extension [17] is pretty solid, although there have been occasional platform-related bugs and the extension is not widely supported by Web hosts. PHP's DOM XML [18] extension has been the weakest link, as a result of numerous bugs and API changes, and remains experimental today.
The problem with using three separate third party libraries is the maintenance effort required of the PHP group. Expat is stable and largely unchanging but has a limited feature set, while libxml and Sablotron are still evolving. Trying to keep concurrent the PHP extensions that use them is a headache. In PHP5, the foundations have been laid to make PHP's XML support exceptional, as we'll see shortly.
With that off my chest, it’s time to take a ride with PHP5...
Test Drive
As the preceding discussion suggests, the new features that PHP5 delivers are what you might call "advanced", in terms of the way we use PHP as a language. If you're not already used to PHP4's object model, or you have no experience using OOP in another language, you'll have to forgive me if the following examples go over your head. But if you've been stalling on learning about OOP in PHP, now would be a very good time to start, as the version 4 syntax is pretty simple.
Help is at hand, in the form of a selection of Advanced PHP Resources [19], as well as Kevin Yank's article Object Oriented PHP: Paging Result Sets [20], which serves as a great primer on PHP4's class syntax. For a more in-depth primer in Object Oriented Programming, I recommend Bruce Eckel's Thinking in Java [21] (available for free in electronic form). In case you're wondering why I'm recommending a book on Java to PHP programmers, "Thinking in Java" provides a very solid summary of all the fundamental aspects of the object oriented paradigm, and the code that illustrates them is simple and to the point. Java's syntax is close enough to PHP4, OOP-wise, for someone with a grasp of the PHP syntax to understand what Java is doing.
That said, if OOP really turns you off, there are a few other new features in PHP5 that might interest you, particularly the areas of SQLite, exception handling, and SimpleXML, all of which I'll be looking at in a moment.
When it comes to installing PHP5, I'm going to leave it to the Advanced PHP Forum [22] to help you out. Here, you'll find people who've successfully installed it under Linux, Mac OSX and Windows. I managed to install successfully under Suse 8.1 Linux and Windows 2000, the process being relatively painless and essentially the same as with PHP4 (see the manual on installation [23] for more). I did hit a few problems with Windows XP, where Apache crashed upon running any PHP scripts, though that issue may be specific to my system.
One tip for Windows users (which comes under the heading "Quick and Dirty") is to copy iconv.dll from the php5/dlls to the php5/sapi directory (to deal with the fact that PHP is unable to find php4apache.dll).
Regarding Apache configuration, first, make sure you're using a 1.3.x release of Apache (as PHP4 and 5 have not been declared stable with Apache 2.x). In httpd.conf, where, for PHP4 you would normally have had something like:
LoadModule php4_module libexec/libphp4.so" # Unix
#LoadModule php4_module "c:/php4/sapi/php4apache.dll" # Windows
You now have:
LoadModule php5_module libexec/libphp5.so" # Unix
#LoadModule php5_module "c:/php4/sapi/php4apache.dll" # Windows
Note that on Windows, you're still using php4apache.dll with PHP5 (no, that's not a typo).
Be aware that the example code I'll be showing you here may not be valid when PHP5 reaches a full version. The complete code is available as a ZIP at the end of this article.
SQLite
To get the ball rolling, the first big news is that PHP5 comes with its own, embedded database engine called SQLite, which you can begin to use the moment PHP is installed.
Stunned?
SQLite is a lightweight, public domain database engine written in C, that was developed by D. Richard Hipp of Hipp, Wyrick & Company, Inc., and is freely available from http://www.hwaci.com/sw/sqlite/ [24].
For some of the reasoning behind the development team’s decision to embed it at the heart of PHP, try this post [25].
SQLite is not intended as a replacement for existing database servers such as MySQL and Postgresql.
If you thought MySQL was rudimentary in terms of what it offers (e.g. lack of triggers, stored procedures etc.), SQLite manages to make it look feature-rich. What SQLite has going for it is speed and the capability to store data in both files and memory. Benchmarks suggest it's roughly twice as fast as MySQL for common operations, and MySQL certainly isn't slow.
Some of the tasks to which I might consider putting SQlite might be application configuration information, storing sessions, storing serialized objects between page requests and possibly to store user login information, depending on the design of the application. Whether I'd use it as my primary data store remains to be seen—I haven't seen how SQLite stands up on a live Website with a lot of traffic. John Lim of PHP Everywhere [26] has some interesting thoughts on SQLite [27].
Here's how you might use SQLite with PHP5. Note that this won't be an extensive tutorial, but is designed to give you a rough idea of how it works. Have a read of the SQLite FAQ [28], and SQL [29] documentation for more information. There's no official PHP documentation (other than this presentation [30]) available now, but I found the implementation (in terms of PHP functions) conforms to the SQLite C Interface [31].
<?php
// Create SQLite Database and Table
// Display some version information
echo ( 'SQLite Version: '.sqlite_libversion().'<br />');
echo ( 'SQLite Encoding: '.sqlite_libencoding().'<br />');
// The file path to the database
$sqliteDb='/www/sitepoint/php5/sitepoint.sqlite';
// Connect to the database (creates the file)
if ( !$db = sqlite_open($sqliteDb, 0666, $err) )
die($err);
// A query to create a table
$sql = "CREATE TABLE
users
(
id INTEGER PRIMARY KEY,
login STRING UNIQUE,
password STRING,
email STRING
)";
// Run the query
if ( !sqlite_query($sql, $db) )
// Die if errors happen, displaying the error message
die(sqlite_last_error($db).': '.
sqlite_error_string(sqlite_last_error($db)));
echo ( "Database $sqliteDb created successfully" );
// Close the connection
sqlite_close($db);
?>
Script: sqlite_create.php
The above example creates a table "users" in the database file "/www/sitepoint/php5/sitepoint.sqlite" (the file is created automatically if it doesn't exist).
Now, to put some data in the table;
<?php
// Insert some data into the table
// Connect to the database
$sqliteDb='/www/sitepoint/php5/sitepoint.sqlite';
if ( !$db = sqlite_open($sqliteDb, 0666, $err) )
die($err);
// Some data to insert
$users = array(
array(
'login'=>'jbloggs',
'password'=>md5('secret'),
'email'=>'jbloggs@yahoo.com'
),
array(
'login'=>'jsmith',
'password'=>md5('secret'),
'email'=>'jsmith@php.net'
) );
foreach ( $users as $user ) {
// An INSERT query
$sql = "INSERT INTO
users (login, password, email)
VALUES
(
'".$user['login']."',
'".$user['password']."',
'".$user['email']."'
)";
// Perform the query
if ( !sqlite_query($sql, $db) )
// Die if errors happen, displaying the error message
die(sqlite_last_error($db).': '.
sqlite_error_string(sqlite_last_error($db)));
}
echo ( 'Values inserted successfully' );
// Close connection
sqlite_close($db);
?>
Script: sqlite_insert.php
Because the "id" column is an integer primary key, SQLite handles auto increments for me (like MySQL).
Finally, I select some data from the table;
<?php
// Performs a select on the table
// Connect
$sqliteDb='/www/sitepoint/php5/sitepoint.sqlite';
if ( !$db = sqlite_open($sqliteDb, 0666, $err) )
die($err);
// Select the data
$sql = "SELECT
*
FROM
users";
// Get the result
if ( !$result = sqlite_query($sql, $db) )
// Die if errors happen, displaying the error message
die(sqlite_last_error($db).': '.
sqlite_error_string(sqlite_last_error($db)));
echo ( '<h2>User List</h2>' );
// Fetch the results into an array, row by row
while ($row = sqlite_fetch_array($result, SQLITE_ASSOC)) {
echo ( $row['id'].'. <b>Login:</b> '.$row['login'].
' <b>Email:</b> '.$row['email'].'<br />' );
}
// Close connection
sqlite_close($db);
?>
Script: sqlite_select.php
It’s pretty simple—more or less the same as working with MySQL in PHP.
As mentioned, SQLite can also store data in memory, which may offer an alternative to PHP's shared memory functions [32], which have never worked well on Windows. It also has an alternative object oriented API to the PHP functions I used above. See this presentation [33] for more info.
The PHP5 Object Model
First, a warning—from here on, things get more intense. There's a lot of ground to cover and I'll be assuming you have knowledge of OOP, as well as a rough idea of how things are in PHP4 today. If you want more detail, please feel free to add to the discussion at the end of this article. Mostly, I'll be leaving it to the code to speak for itself, so you'll probably need to examine it fairly carefully. Feel free to drop questions into the discussion at the end of this article or the Advanced PHP Forums [34] if anything is unclear.
If you've ever done any work with Java, PHP5's object model will be very familiar to you. That's not to say it's the same as Java; there are significant differences in terms of developer-friendliness, and the fact that PHP is a dynamically typed language.
Here, I'll be taking you on a tour with examples. There are already a number resources online (see the end of this article) which offer more information.
Private, Protected and Public
Addressing what is perhaps the #1 complaint leveled at PHP4, you can now declare member variables and methods in your class as being "off limits" for code that’s external to the class. With PHP4, you could get by through exerting some self-discipline, but for some (perhaps those lacking the requisite discipline!) this wasn't enough. So, with PHP5 you can now protect the internals of objects from the outside world, providing access only via the API (Application Program Interface) you've defined.
Private (and Public)?
Here's and example of a private keyword being use to protect a class variable and a class method;
<?php
class Color {
// Available only from within the class
private $rgb = array();
// Note "public" and "var" are basically the same thing
public $colorname = '';
// This method is only available from within the class
private function setRGB ($rgb) {
$this->rgb=$rgb;
}
// "public" is optional
public function setColor($color) {
$colorMap = array (
'white' => array ('r'=>255,'g'=>255,'b'=>255),
'gray' => array ('r'=>190,'g'=>190,'b'=>190),
'yellow' => array ('r'=>255,'g'=>255,'b'=>0),
'black' => array ('r'=>0,'g'=>0,'b'=>0)
);
// Use private method
$this->setRGB($colorMap[$color]);
$this->colorname = $color;
}
// More public methods
function r() {
return $this->rgb['r'];
}
function g() {
return $this->rgb['g'];
}
function b() {
return $this->rgb['b'];
}
function name() {
return $this->colorname;
}
}
$color = new Color();
# Private property - FATAL Error!
// $color->rgb = array ('r'=>255,'g'=>255,'b'=>0);
# Private method - FATAL Error!
// $color->setRGB(array ('r'=>255,'g'=>255,'b'=>0));
$color->setColor('yellow');
echo ( 'Yellow is made from:<br />' );
echo ( 'Red: '.$color->r().' ' );
echo ( 'Green: '.$color->g().' ' );
echo ( 'Blue: '.$color->b().' ' );
?>
Script: private.php
Note that I've used the keyword "public" in the above example as well, just to show it's possible. Effectively, "public" is optional, as any method or class variable that’s not declared as "private" or "protected" defaults to "public" (allowing for backwards compatibility).
Anything declared as "private" is accessible only from inside the class where it’s declared. That means subclasses are barred from access as well. If you create a subclass, you can declare methods and variables with the same name, but they will be separate entities from those declared in the parent.
Protected
Here's an example with a protected variable;
<?php
class Color {
// Accessible only inside this class and subclasses of this class
protected $rgb = array();
var $colorname = '';
private function setRGB ($rgb) {
$this->rgb=$rgb;
}
function setColor($color) {
$colorMap = array (
'white' => array ('r'=>255,'g'=>255,'b'=>255),
'gray' => array ('r'=>190,'g'=>190,'b'=>190),
'yellow' => array ('r'=>255,'g'=>255,'b'=>0),
'black' => array ('r'=>0,'g'=>0,'b'=>0)
);
// Use private method
$this->setRGB($colorMap[$color]);
$this->colorname = $color;
}
function r() {
return $this->rgb['r'];
}
function g() {
return $this->rgb['g'];
}
function b() {
return $this->rgb['b'];
}
function name() {
return $this->colorname;
}
}
class RedFilter extends Color {
function setColor($color) {
parent::setColor($color);
$this->rgb['r'] = 0;
}
}
$color = new RedFilter;
# Protected property - FATAL Error!
// $color->rgb = array ('r'=>255,'g'=>255,'b'=>0);
$color->setColor('yellow');
echo ( 'Yellow filtered by Red is made from:<br />' );
echo ( 'Red: '.$color->r().' ' );
echo ( 'Green: '.$color->g().' ' );
echo ( 'Blue: '.$color->b().' ' );
?>
Script: protected.php
A protected variable (or method) is available to a subclass.
Constructors and Destructors
PHP5 offers an alternative to PHP4 for the class constructor method. Rather than giving the constructor the same name as the class itself, you can use a method called __construct() as the constructor. The advantage of doing this is that should you rename the class, you won't need to rename the constructor (or any code in subclasses of the class you've renamed). Note that you can still use the old approach to constructors (backwards compatibility is preserved).
Here's an example:
<?php
class Foo {
var $variable;
/**
* Constructors now have the common __construct()
* function.
*/
function __construct() {
$this->variable = 'Something';
}
}
echo 'Constructing Foo with the new __construct()<br />';
$f = new Foo();
echo ( $f->variable );
?>
Script: constructor.php
PHP5 also introduces the destructor method, __destruct(), which you can use to make sure that an object is properly "cleaned up" (for example closing the connection to a database) once you've finished with it. PHP's garbage collection mechanism will call the destructor when it removes the object from memory; alternatively, you can call the destructor directly from your code.
For example:
<?php
class File
{
var $resource;
function __construct($name) {
$this->resource = fopen($name,'r');
}
function read() {
if ( !feof($this->resource) )
return fgets($this->resource);
else
return false;
}
function __destruct() {
fclose($this->resource);
}
}
$file = new File('example.html');
echo ('<pre>');
while ( $contents = $file->read() ) {
echo ( htmlspecialchars($contents) );
}
echo ('</pre>');
?>
Script: destructor.php
The __destruct() method above makes sure the opened file is closed when the object is trashed by PHP's garbage collector.
Note that calling __destruct() yourself doesn't actually remove the object from memory—it simply executes the code you defined with the __destruct() method. In the original Zend 2 Engine Overview [PDF] [35] there was talk of a delete statement for removing objects from memory; my attempts to use it with the Beta resulted in syntax errors.
Abstract
PHP5 has formalized abstract classes and methods at an engine level. An abstract class (or method) is one which should not be used directly; instead, it should be used via a concrete subclass. The point in having abstract classes is to allow a group of subclasses to share the parent implementation, while warning other developers not to use the parent directly. You might have an abstract class called HTML_Widget, for example, which is extended by classes such as HTML_Table and HTML_Form; you want people to use only the subclasses rather than the parent itself.
A common PHP4 convention was to place die() statements inside classes and methods that were supposed to be abstract, but in PHP5 we now have a standard approach (which is another Good Thing).
Here's an example:
<?php
abstract class Senior {
protected $speech = 'Hello World!';
function speech() {
return $this->speech;
}
}
class Junior extends Senior {
protected $speech = 'Goodbye World!';
abstract function think() {
sleep(5);
}
}
# Attempt to instantiate abstract class: FATAL Error!
// $g = new Senior();
$g = new Junior();
# Attempt to instantiate abstract method: FATAL Error!
// $g->think();
echo ( $g->speech() );
?>
Script: abstract.php
You get fatal errors if you try to instantiate the abstract class Senior or the think() method of the Junior class.
Interfaces
Interfaces are a new feature of PHP5, which were possible for disciplined developers using PHP4, but only as a loose convention.
Interfaces are essentially a "contract" which any concrete class that implements the interface must obey. The advantage of having interfaces is that they allow developers to state their intentions within a library of classes, and so help other developers who add their own code to conform to the standards of the library. Interfaces are also useful when need to identify an object by introspection.
Introspection refers to the ability of your code to examine data at runtime and determine the type, methods available (if it's an object), and so on. PHP already provides a number of introspection functions, such as is_a [36] and a new keyword with PHP5, "instanceof", which does effectively the same thing as is_a() but should improve readability. Introspection may receive many more features by the time PHP5 is stable.
An interface class simply declares a set of method names without the methods having a body. For example:
<?php
interface Iterator {
function fetch();
function howMany();
}
class Presidents implements Iterator {
private $presidents = array();
function __construct() {
$this->presidents = array (
'Jimmy Carter','Ronald Reagan','George Bush',
'Bill Clinton','George W. Bush'
);
}
function fetch() {
$president = each($this->presidents);
if ( $president ) {
return $president['value'];
} else {
reset ( $this->presidents );
return false;
}
}
# Commented out to produce error
/*
function howMany() {
return count($this->presidents);
}
*/
}
#Fatal Error - Presidents does not implement the howMany() method in Iterator!
$presidents = new Presidents();
while ( $president = $presidents->fetch() ) {
echo ( $president.'<br />' );
}
?>
Script: interface.php
In the above example, the Iterator interface defines two methods that classes implementing it must provide. It's down to the implementing class to provide the logic for the methods. Because I commented out the howMany() method in the Presidents class, when I instantiate it PHP gives me a fatal error reminding me I need to make the howMany() method available again.
Note: regarding Iterators, see "That's not all folks..." later in this article.
While PHP5 was evolving, there was some argument over multiple inheritance (as is the case with C++) vs. interfaces (the Java way). Multiple inheritance (the extension of multiple parent classes) is perhaps more powerful, but it also introduces a lot more complexity to a language and (arguably) often results in bad design. The PHP group chose the Java approach, which may not please everyone, but is certainly sensible.
Personally, I think interfaces are the better way to go; PHP needs more focus on standardization rather than choice (of which it already offers a lot). At the same time, questions of performance will likely raise their heads where interfaces are concerned, as PHP is interpreted rather than compiled.
For more depth on Interfaces vs. Multiple Inheritance try this discussion [37] and the article it refers to [38].
Overloading
Overloading objects in PHP4 became possible via the (experimental) overload extension [39], which you can find explained in more detail here [40]. With PHP5, overloading has become a central feature of the language, and will no doubt prove important when dealing with Web services as well as integration with other platforms like Java and .NET.
Overloading, in a PHP context, is not the same as method overloading in (statically typed) languages like C++ and Java. PHP provides the function func_num_args() [41], which can be combined with type introspection like is_int() [42] should you need to achieve the same effect. In practice though, as PHP is dynamically typed, you rarely need Java/C++ style method overloading.
In PHP, overloading refers to the ability to call methods and object properties which have not been explicitly declared within the class. PHP allows the use of three "magic" methods __get(), __set() (both of which apply to object properties) and __call() (which applies to object methods) within which you can place code for dealing an "unexpected" call.
Here's an example using the __call() method in PHP5 to allow native PHP functions to be called via an object:
<?php
class MyString {
private $string;
function __construct($string) {
$this->string = $string;
}
function __call($method,$params) {
switch ($method) {
case'strlen':
return strlen($this->string);
break;
case'substr':
return substr($this->string,$params[0],$params[1]);
break;
default:
return false;
break;
}
}
}
$myString = new MyString('Hello World!');
echo ( 'The string length is '.$myString->strlen().'<br />' );
echo ( 'Substr: '.$myString->substr(3,6).'<br />' );
?>
Script: overload.php
The native PHP functions substr() and strlen() are now callable via an instance of the MyString class, without which you need to explicitly declare them as methods.
A Little Decoration
Overloading might also be used to implement a Decorator pattern (a Decorator pattern provides an alternative to subclassing). For example:
<?php
class FileList {
var $listing;
function __construct($path) {
$this->listing = array();
$path = realpath($path);
$dir = Dir($path);
while (false !== ($entry = $dir->read())) {
if ( is_file($path.'/'.$entry) ) {
$file = array();
$file['name'] = $entry;
$file['size'] = filesize($path.'/'.$entry);
$file['ctime'] = filectime($path.'/'.$entry);
$file['atime'] = fileatime($path.'/'.$entry);
$this->listing[]=$file;
}
}
}
function fetch() {
$file = each ( $this->listing );
if ( $file ) {
return $file['value'];
} else {
reset ( $this->listing );
return false;
}
}
function getFileByName($name) {
foreach ( $this->listing as $file ) {
if ( $file['name'] == $name ) {
return $file;
}
}
return false;
}
}
class FileSortDecorator {
var $fileList;
function __construct($fileList,$sortBy='name') {
$this->fileList = $fileList;
$sort = array();
foreach ( $this->fileList->listing as $file ) {
$sort[]=$file[$sortBy];
}
array_multisort($sort,$this->fileList->listing);
}
function __call($method,$params) {
return call_user_func_array(
array($this->fileList,$method),$params);
}
}
$fileList = new FileList('./');
$sortDecorator = new FileSortDecorator($fileList,'size');
echo ('<b>fetch:</b>');
while ( $file = $sortDecorator->fetch() ) {
echo ('<pre>');
print_r($file);
echo ('</pre>');
}
echo ('<b>getFileByName:</b><pre>');
print_r($sortDecorator->getFileByName('overload.php'));
echo ('</pre>');
?>
Script: overload_decorator.php
The FileSortDecorator class decorates the FileList class by sorting the list of files by a particular array key. At the same time, it allows the FileList methods to be called through it, using the __call() method.
Warning: overloading is very powerful but will constitute a "hack" in most circumstances. How can you perform introspection on an overloaded class, for example? Marcus Baker, author of the excellent Simple Test Framework [43] for PHP Unit Testing, raised some very valid concerns about PHP's overloading here [44].
Aside from integration with other platforms and hacking, overloading provides some interesting opportunities for PHP developers to apply the Aspect Oriented Paradigm [45] (as Markus mentions in his post), and to implement something like Python's Metaclasses [46].
Note: PHP does not support operator overloading (so no $myObject++).
Static, Final and Constant
PHP5 provides three more important keywords; static, final and constant.
Static
The static keyword can be declared against class variables or methods, to allow them to be accessed externally without instantiating the class itself. With PHP4 this was already possible for class methods, using the :: operator, but the important step forward is it can now be applied to class variables.
Here's a static class member variable:
<?php
class MyStatic {
static $foo;
var $bar;
}
MyStatic::$foo = 'Red';
# Fatal error: Access to undeclared static property: mystatic::$bar
// MyStatic::$bar = 'Blue';
echo ( MyStatic::$foo );
?>
Script: static_var.php
Where class methods are concerned, the static keyword is less important, as methods can be called statically anyway (otherwise PHP5 would break a lot of backward compatibility with PHP4):
<?php
class MyStatic {
static function Foo() {
return 'This is foo()';
}
function Bar() {
return 'This is bar()';
}
}
echo ( MyStatic::foo().'<br />' );
echo ( MyStatic::bar().'<br />' );
$obj = new MyStatic();
# Fatal error - cannot call static method via object instance
// echo ( $obj->foo().'<br />' );
echo ( $obj->bar().'<br />' );
?>
Script: static_method.php
Essentially, all methods can be called statically in PHP5 anyway, as was possible in PHP4. However, the static keyword prevents a method declared with it from being called via an object instance, as the example demonstrates.
A Singleton
One advantage of the static keyword is how easy it makes the implementation of the Singleton pattern:
<?php
class Singleton {
/**
* The singleton instance is stored here
*/
static private $instance = false;
private $text = 'Empty message';
/**
* Make the constructor private to prevent the class being
* instantiated directly
*/
private function __construct() {}
/**
* Use this static method to get a singleton instance
*/
static function instance() {
if(!Singleton::$instance) {
Singleton::$instance = new Singleton();
}
return Singleton::$instance;
}
function setText($text) {
$this->text = $text;
}
function getText() {
return $this->text;
}
}
class Hello {
function __construct() {
$single = Singleton::instance();
$single->setText('Hello World!');
}
}
class Goodbye {
function __construct() {
$single = Singleton::instance();
$single->setText('Goodbye World!');
}
}
$single = Singleton::instance();
echo ( $single->getText().'<br />' );
$hello = new Hello();
echo ( $single->getText().'<br />' );
$hello = new Goodbye();
echo ( $single->getText().'<br />' );
?>
Script: static_singleton.php
When you run this script, you'll notice that although I call the same method via the same object reference each time ( $single->getText() ), constructing the Hello and Goodbye classes modifies the output. This is because they're all working with the same copy of the object (a Singleton). Singleton's can be valuable when you have a class that many objects need to use (perhaps a database connection) but you don't want to have multiple instances of the object (e.g. you don't want to connect to the database 10 times). Having the static keyword allows PHP developers to standardize Singleton classes, rather than having to resort to the workarounds required in PHP4.
Final
The final keyword, for which there was basically no workaround in PHP4, allows you to prevent a class member variable or method from being over-ridden in a subclass. For example:
<?php
class Father {
final function youreGrounded() {
return 'Do your homework!';
}
}
class Child extends Father {
# Fatal error: Cannot override final method father::youregrounded()
/*
function youreGrounded() {
return 'Outta here!';
}
*/
}
$child = new Child();
echo ( $child->youreGrounded().'<br />' );
?>
Script: final.php
Attempting to override a method declared as final results in a fatal error.
Const
The keyword const allows you to declare constants within a class, which means, for example, that they can be accessed statically. With PHP4, constants could not be stored in classes; rather, all existed in the global namespace (which could result in conflicts). The following example should give you a clue as to how this can be useful:
<?php
class Config {
const host = 'localhost';
const user = 'harryf';
const pass = 'secret';
}
mysql_connect(Config::host,Config::user,Config::pass);
?>
Script: const.php
References, Cloning and Dereferencing
In PHP4, references were a subject of confusion, the default behaviour, when passing objects around, being to copy rather than reference the object.
With PHP5 the default behaviour is now to pass objects by reference (using the same approach as Java, in other words). What does this mean to you, the developer? Well, if you didn't understand how references worked in PHP4, you can now pretty much forget the subject completely.
A note for those aware of the problem with foreach{} loops and object references: this has now also been solved.
Be warned, though—normal variables (i.e. those which aren’t objects) are still copied (from my experiments with the beta), so if you need to pass one by reference you still need to use the & reference operator.
Cloning
When you need a copy of an object, the __clone() method can be used. The following example should give you an idea of cloning.
<?php
class Message {
private $text='Empty message';
function setText($text) {
$this->text = $text;
}
function getText() {
return $this->text;
}
function __clone() {
return $this;
}
}
$msg1 = new Message();
$msg1->setText('This is message 1');
echo ( 'From $msg1: '.$msg1->getText().'<br />' );
$msg2 = $msg1;
$msg2->setText('This is message 2');
$msg3 = $msg1->__clone();
$msg3->setText('This is message 3');
echo ( 'From $msg2: '.$msg2->getText().'<br />' );
echo ( 'From $msg1 again: '.$msg1->getText().'<br />' );
echo ( 'From $msg3: '.$msg3->getText().'<br />' );
?>
Script: clone.php
The output of the above script looks like this:
From $msg1: This is message 1
From $msg2: This is message 2
From $msg1 again: This is message 2
From $msg3: This is message 3
Because $msg3 is a clone of $msg1, it is unaffected by change to the $text member variable that happened here when $msg2->setText() was called.
Dereferencing
One final reference-related feature that PHP5 provides is dereferencing.
Dereferencing allows you to access an object via another object’s method, basically providing a short cut to reduce the number of lines of code. For example:
<?php
class Factory {
static function create() {
return new Message();
}
}
class Message {
private $text = 'This is a message';
function getText() {
return $this->text;
}
}
echo ( Factory::create()->getText().'<br />' );
?>
Script: dereference.php
Using the above classes with PHP4 might look like;
$message = Factory::create();
echo ( $message->getText().'<br />' );
Although this may not look too impressive in the above example, it may prove useful for complex objects, such as when you use the DOM XML extension, saving a lot of coding.
AutoLoading
PHP, as you know, is an interpreted language where your source code has to be re-examined each time a script is executed. That's great for developers, as it allows us to bypass the long wait that comes with compiled languages, and gives us plenty of opportunities to experiment and prototype quickly. The downside is that the more code you have, the longer it takes the PHP parser to work through it all on each execution—bad news for end user performance.
Where classes are concerned, this is certainly a problem. Typically, you'll need to include all the classes that will ever be used by your application, even if the current "operation" only uses a few of them. That means the PHP parser has to do a lot of unnecessary work.
PHP5 provides a handy new function, __autoload(), thanks to Ivan Ristic [47], which allows you to have a class included only when it's instantiated.
For example, take the following class file:
<?php
echo ( 'Message class now being autoloaded.<br />' );
// Autoloaded
class Message {
private $text = 'This is a message';
function getText() {
return $this->text;
}
}
?>
Script: Message.php
Let’s include this file in a script that uses it:
<?php
function __autoload($name) {
require_once($name.'.php');
}
if ( isset($_GET['autoload']) ) {
$message = new Message();
echo ( $message->getText().'<br />' );
}
echo ( 'Execution finished' );
?>
Script: autoload.php
You'll notice that if you view the script normally, without adding any variables to the URL, it outputs 'Execution Finished'. But appending a "?autoload" will display "Message class now being autoloaded." as well as the output from the getText() method.
No doubt there are other situations where __autoload() can come in handy, such as when you need to unserialize a stored object.
Type Hinting
It may come as a surprise to you to hear that, by some definitions, PHP is a strongly typed language (other definitions call it weakly typed). PHP isn't a typeless language (which would mean it had no types at all). It has types such as string, int, array, object, etc.
Where PHP differs from languages like Java and C++, which are statically typed (types must be identified in the source code), is that PHP is dynamically typed—variable types are dealt with at runtime rather than at compile time.
Some say that dynamically typed languages are more "dangerous". Passing the wrong type to a function or class method could lead to "disaster". The checks a compiler performs on a statically typed language reduce the number of bugs in an application.
Others, such as Bruce Eckel [48] have a more enlightened point of view. They argue that the tests a compiler performs are really only a subset of unit testing [49]. But that's another story...
PHP5 introduces an interesting new feature called type hinting, which can be used to require that arguments passed to a function or class method are instances of a particular class. For example:
<?php
class Message {
private $text = 'This is a message';
function getText() {
return $this->text;
}
}
class Foo {
private $text = 'This is a foo';
function getText() {
return $this->text;
}
}
class Messenger {
private $message;
function setMessage(Message $message) {
$this->message = $message;
}
function getMessage() {
return $this->message;
}
}
$message = new Message();
$foo = new Foo();
$messenger = new Messenger();
$messenger->setMessage($message);
# Fatal error: Argument 1 must be an instance of message
// $messenger->setMessage($foo);
echo ( $messenger->getMessage()->getText().'<br />' );
?>
Script: typehint.php
In the above example, if I try to pass an instance of Foo to the Messenger's setMessage() class, I'll get a fatal error.
When dealing with type hinting, PHP also looks any parent class from which an object descends, so if I made Foo as subclass of Message, I would be allowed to pass it to the setMessage() method of Messenger. This should prove an important feature for developers who want to deliver a really rock-solid API.
Right now, as far as I'm aware, type hinting cannot be used to check for PHP's native, primitive data types (string, int, etc.) and there also doesn't seem to be a mechanism for "type hinting" the value returned from a method or function. Hopefully that's something the PHP group are looking at—it would prove useful in situations when statically typed languages act as a client to a PHP application, such as a Web service.
One particular problem that PHP (and other dynamically typed languages like Perl and Python) has with Web services is the generation of WSDL documents. For languages like Java and C#, a WSDL document can be built automatically by examining the source code for a SOAP service. Today, PHP libraries like PEAR::SOAP [50] have had to define some form of interface definition language, which requires a separate coding effort to make it reflect the real API it’s describing. With full type hinting, it may be possible to generate WSDL automatically from PHP source code, either using some form of introspection, or the tokenizer [51] functions. But I digress...
Namespaces?
Namespaces (and nested classes) are a mechanism common to languages like Java that allow developers to define their own naming scopes, allowing you to declare classes with the same name, for example. These were part of the original plan for PHP5.
Unfortunately, due to implementation issues, they have been dropped for the time being, and are unlikely to return by the time PHP5 reaches a full release.
The reason for this decision is highlighted here [52]. In basic terms, the design that was planned for namespaces in PHP5 turned out to be one which could significantly impact on PHP's performance. Confronted with a choice of stalling the release of PHP5 indefinitely to get this right, or shipping it without namespaces (and adding them at some later date), the team decided on the latter option. My guess is most people would prefer to see PHP5 sooner rather than later.
The common PHP4 workaround for namespaces is to place underscores in the class names, e.g. class UI_HTML_Table{}, which is what we have to keep on doing for the near future.
Error Handling
PHP5 introduces a new error handling mechanism that’s in line with languages like Java and (dare I say it) JavaScript.
To understand how it works, it's worth looking at the current situation with PHP4. Essentially, the problem PHP developers face right now is how to separate error notifications in their code from application data. For example, consider the following:
<?php
function searchColors ($color) {
// An error: $color must be longer than three characters
if ( strlen($color) < 3 )
return false;
$colors = array ('red','blue','green');
if ( in_array($color,$colors) )
// Yes there was a search match
return true;
else
// No there was no match
return false;
}
// Initialize variable
if ( !isset($_GET['color']) )
$_GET['color']='white';
if ( !searchColors($_GET['color']) ) {
echo ( 'Search for '.$_GET['color'].' returned no matches' );
} else {
echo ( $_GET['color'].' was found!' );
}
?>
Script: exception.php
The searchColors() function is intended to return true if the color argument is found in the $colors array, and false if it isn't found. Both of these returned values could be considered as valid application data. But there's a problem. The $color value submitted to the searchColors() function must be at least three characters long (perhaps in a real world example this is because my database server can only perform FULLTEXT searches on search strings of a minimum length). Should it not meet the three character requirement, the script returns false. The problem is, how does the code calling the function know which "false" is valid application data, and which is an error?
Perhaps I could return -1 if the string length test fails, to distinguish it from a false value, but that's going to lead to some pretty ugly code when I use the function to perform an additional check for that value. Also, I should use that approach consistently throughout my application, which, if I'm forgetful or working in a team, may be a problem.
Alternatively, I might consider turning to PHP's trigger_error() [53] function, which will at least allow the error to be dealt with separately from the code that calls the function. It can define a "global" error handler with set_error_handler [54], but trying to get the handler to understand where the error came from, and what the appropriate response is, will be awkward. I really want to be able to deal with this error somewhere near the point at which the searchColors() function is called.
Note: For an in depth look at error handling with version 4, try Error Handling in PHP [55].
PHP5's new mechanism introduces a control structure, the try/catch condition, which is a lot like an if/else, but has been designed specifically for handling a special type of value—the Exception. If you haven't come across try/catch type error handling in another programming language, it may seem a little strange at first; once you get used to it, though, I'm sure you'll find it's a big relief. It's worth remembering that the idea behind this approach is to separate application data from errors.
Using the new mechanism, the above example might look like this:
<?php
function searchColors ($color) {
// An error: $color must be longer than three characters
if ( strlen($color) < 3 )
// throw an Exception
throw new Exception('Color is too short. 3 chars min');
$colors = array ('red','blue','green');
if ( in_array($color,$colors) )
// Yes there was a search match
return true;
else
// No there was no match
return false;
}
// Initialize variable
if ( !isset($_GET['color']) )
$_GET['color']='white';
// Try the search colors function
try {
if ( !searchColors($_GET['color']) ) {
echo ( 'Search for '.$_GET['color'].' returned no matches' );
} else {
echo ( $_GET['color'].' was found!' );
}
// Catch an thrown exceptions here
} catch (Exception $e) {
echo ( $e->getMessage() );
}
?>
Script: exception1.php
First, notice that the searchColors() function, when encountering a search string that’s too short, now uses the keyword throw to create an object of class Exception. This class is built into PHP5, so it’ll always be available. More on Exception in a moment.
Now in the code that uses the searchColors() function, I place my old PHP4 code inside the try{} block. It now only has to deal with application data, not errors. If a search string that’s too short is passed to searchColors(), an Exception object is thrown and execution of searchColors() immediately stops. Control now passes to the catch{} block where the error can be handled separately from the normal flow of the application, in this case displaying a message that the color provided was too short.
Using try/catch exception handling, in some situations I may be able to have my code "recover" from the error. For example:
<?php
// Define my own subclass
class DivideByZeroError extends Exception {
function __construct($message) {
parent::Exception($message);
}
}
function divide($by) {
if ( $by == 0 )
throw new DivideByZeroError ('Divide by Zero Error');
return 1 / $by;
}
// Initialize variable
if ( !isset($_GET['divideBy']) ) {
$_GET['divideBy'] = 1;
}
try {
echo ( divide($_GET['divideBy']) );
} catch (DivideByZeroError $e) {
// Make a small value slightly bigger than zero
$_GET['divideBy'] = 0.0000000000000001;
// Re-execute the divide() function
echo ( divide($_GET['divideBy']) );
}
?>
Script: exception2.php
The first thing to notice in the above example is that I've defined my own exception class, DivideByZeroError, which extends the built-in Exception class. Note that with the PHP5 Beta, the built in Exception class does not have a __construct() method so inside the DivideByZeroError class I have to call the parent constructor by name.
The catch{} block is on the look out for DivideByZeroError exceptions. Should it encounter one, it modifies the value being divided by to a small number very slightly bigger than zero then re-runs the divide() function. This, I've decided, is acceptable within the context of my application.
So, as you’re aware, the Exception class (currently) has four built-in methods, and is able to accept an additional error code argument to its constructor:
<?php
try {
throw new Exception ( 'This is an error',54321 );
} catch (Exception $e) {
echo ( 'Error message: '.$e->getMessage().'<br />' );
echo ( 'Error code: '.$e->getCode().'<br />' );
echo ( 'Script Name: '.$e->getFile().'<br />' );
echo ( 'Line Number: '.$e->getLine().'<br />' );
}
?>
Script: exception3.php
As well as being able to define your own subclasses of Exception (to which you could add additional methods), you might use the error codes to distinguish between two exceptions of the same type.
By defining your own Exception subclasses, you can also define multiple catch{} blocks, allowing you to respond to different types of errors in different ways. The following example should make this a little clearer:
<?php
class ValueError extends Exception {
function __construct($message) {
parent::Exception($message);
}
}
class TypeError extends Exception {
function __construct($message) {
parent::Exception($message);
}
}
function getForm($error=false) {
$form=<<<EOD
<form action="{$_SERVER['PHP_SELF']}" method="POST">
<input type="text" name="firstName"><br />
<input type="submit" name="submit" value="Send">
</form>
EOD;
if ( $error ) {
$form.="<br>\n<b>".$error."</b>\n";
}
return $form;
}
if ( isset ( $_POST['submit'] ) ) {
try {
if ( empty($_POST['firstName']) ) {
throw new ValueError('Please enter your name(!)');
} else if ( preg_match("/[^A-z$]/",$_POST['firstName']) ) {
throw new TypeError('You must enter a string value');
} else {
echo ( 'Hello '.$_POST['firstName'] );
}
} catch ( ValueError $e ) {
echo ( getForm($e->getMessage()) );
} catch ( TypeError $e ) {
echo ( 'You have been banned from this site!' );
}
} else {
echo ( getForm() );
}
?>
Script: exception4.php
Notice how the two catch{} blocks "listen" for different exceptions?
When the form is submitted, if the user forgot to enter their name, a ValueError is thrown and a polite message is displayed to remind the user to complete the form field.
Meanwhile, (taking a somewhat extreme view) if the user does fill in the name field, but enters a character that’s not a letter of the alphabet, they are banned from the site (in practice, the site then ignores further requests from their IP address), the assumption being that they tried an SQL injection attack or some other dodgy maneuver.
It's also possible to take some control over errors which would normally generate PHP errors. For example:
<?php
class IOError extends Exception {
function __construct($message) {
parent::Exception($message);
}
}
class FileReader {
private $resource;
function __construct($filename) {
if ( ! $this->resource = @fopen($filename,'r') )
throw new IOError('Unable to open '.$filename);
}
function read() {
if ( !feof($this->resource) )
return fgets($this->resource);
else
return false;
}
function __destruct() {
fclose($this->resource);
}
}
try {
$fileReader = new FileReader('doesnotexist.txt');
while ( $content = $fileReader->read() ) {
echo ( $content );
}
} catch ( IOError $e ) {
echo ( $e->getMessage() );
}
?>
Script: exception5.php
Here I've suppressed the PHP error message that would occur when fopen() attempted to open an non-existent file; this allows me to handle it in my code as an Exception.
Note that with the current Beta, it's unclear (at least to me) to what degree Exception handling can be used in conjunction with native PHP errors. Certainly it would be nice to be able to "map" some PHP error codes to Exceptions for situations like type hinting (which I looked at above), so that when a PHP error occurs, it causes an exception to be thrown, rather than generating a PHP error. To some degree, it may be possible to work around this using PHP4-style custom error handlers.
One final example of exception handling shows you how try/catch blocks can be nested and errors can be re-thrown, allowing them to be handled in the correct context:
<?php
class IOError extends Exception {
function __construct($message) {
parent::Exception($message);
}
}
class SQLError extends Exception {
function __construct($message) {
parent::Exception($message);
}
}
// Class for SQLite connections
class SQLite {
private $db;
function __construct($dbPath) {
if ( !$this->db = @sqlite_open($dbPath, 0666, $error) )
throw new IOError($error);
}
function query($sql) {
if ( !$result = @sqlite_query($sql, $this->db) )
throw new SQLError(sqlite_last_error($this->db).
': '.sqlite_error_string(sqlite_last_error($this->db)));
return new SQLiteResult($this,$result);
}
function __destruct() {
sqlite_close($this->db);
}
}
// Result class returned from the query method above
class SQLiteResult {
private $db;
private $result;
function __construct($db,$result) {
$this->db = $db;
$this->result = $result;
}
function fetch() {
return sqlite_fetch_array($this->result, SQLITE_ASSOC);
}
}
try {
$db = new SQLite('/www/sitepoint/php5/sitepoint.sqlite');
try {
# Simulate bad connection
// throw new IOError('Lost connection to database');
$result = $db->query('SELECT * FROM users');
while ( $row = $result->fetch() ) {
echo ( '<pre>' );
print_r($row);
echo ( '</pre>' );
}
} catch ( SQLError $e ) {
echo ( 'There is something wrong with the query: '.$e->getMessage() );
} catch ( Exception $e ) {
// Re-throw exception
throw ($e);
}
} catch ( IOError $e ) {
echo ( 'Major problems!' );
error_log ( date('YmdHis').": ".$e->getMessage()."\n",3,'errors.txt' );
} catch ( Exception $e ) {
echo ( 'Hmmm - no idea how to deal with this: '.$e->getMessage() );
}
?>
Script: exception6.php
I've created my own Exception subclasses, IOError and SQLError above then created two classes for connecting to SQLite and fetching the results of a query.
Notice that I've got a try/catch block nested within the first try{} block. The nested block only "knows" about SQLError exceptions - any other errors it encounters it re-throws with;
} catch ( Exception $e ) {
// Re-throw exception
throw ($e);
}
...this passes the error to the "parent" try/catch block to see if it knows what to do.
A possible scenario that might arise with an application using a database is this. Imagine that at the point at which the connection to the database was established, everything was OK, but if in the brief interval between connecting and executing a query, somehow the connection was lost. In this situation, an error may occur that the code performing the query knows nothing about. You can simulate this by un-commenting this line:
// throw new IOError('Lost connection to database');
The IOError is not understood within the current context, so it’s re-thrown.
Note that in the above example it's not strictly necessary to re-throw the exception, as PHP will automatically pass the un-caught exceptions up to the parent block. However, there may be situations where you want the same exception to be caught more than once, perhaps first to display a message to the user, then later to update a "globally handled" error log.
Note for Java developers expecting a finally{} [56] block, PHP5 (at least for the foreseeable future) won't provide one. Arguably, you can get by most of the time without it, as I’ve done in this example:
<?php
echo ( 'Start execution<br />' );
try {
echo ( 'In try block<br />' );
throw new Exception('Some error');
} catch ( Exception $e ) {
echo ( 'In catch block<br />' );
}
echo ( 'Finally execution continues...<br />' );
?>
Script: exception7.php
That said, if you have nested blocks and you're re-throwing errors, or have un-caught errors, you'll experience problems.
PHP5's new exception mechanism is an important step forward and will be a big relief to many, as it introduces a standard approach to handling errors that does much to help third party class libraries fit together nicely. It will also (hopefully) encourage PHP developers to write more reliable PHP applications, addressing the argument that open source programmers stink at error handling [57]…
XML Support
Probably around 95% of all PHP applications deal with the rendering of (X)HTML in some form. Now, HTML is a subset of XML (well SGML, in fact), right? Clearly, being able to deal with XML in general is important to PHP.
One thing that became clear with PHP4 is that XML support was not up to scratch, as I mentioned at the start of this article. Only the SAX parser is distributed with PHP by default, which means applications like Krysalis [58] can only be used on enlightened hosts, while raising the subject of the PHP DOM parser elicits many embarrassed coughs in PHP circles.
With PHP5 there's very good news on this front. As I mentioned before, part of PHP's XML "problem" results from the involvement of three third party XML libraries, which significantly increased the workload for the PHP group. The decision for PHP5 was to throw out the Expat and Ginger Alliance XSLT libraries, and replace them (seamlessly) with the Gnome XML Parser [59], libxml, which is currently used for the DOM extension.
While this move may seem insane at first glance, given that PHP's DOM extension has historically been the weakest link, the problem does not lie with libxml itself. Instead, the issue relates more to PHP's DOM implementation of libxml, which has struggled through a change of maintainers and numerous other problems.
The Gnome libxml library is (according to its own benchmarks) one of fastest XML parsers out there. As one of the younger implementations of XML related standards, it has had the advantage of being able to learn from the mistakes of older XML parsers. What's more, it comes with support for almost everything you're likely to be interested in doing with XML today, providing:
- SAX and DOM APIs,
- validation against DTDs and XML Schema (the latter being important to web services),
- XSLT, XPath, Xpointer and XInclude support, and
- parsers for HTML and docbook XML
plus the possibility of a whole bunch of other stuff, like XMLSec, which you can read about on the libxml Website [60].
Overall, this is an important step forward for PHP, and may well turn it into one of the leading technologies for dealing with XML on the Web.
Already the SAX and XSLT extensions have had their underlying libraries swapped out for the libxml equivalent. From a script perspective, this should not affect any code using the XML-related functions, as the APIs having remained the same.
Other news is the DOMXML extension is packing some new features, and for those that have "issues" with XML, there's a new extension which could make your life much easier (we’ll be looking at both of these in a moment).
Given that the PHP group have assigned four of their brightest minds to work on PHP's XML support, it's likely that many of the other XML technologies supported by libxml will find their way into PHP5 in the near future.
All in all, very good news.
My personal wish is it will become a policy to bundle all the XML-related extensions into the core distribution so they become widely supported by Web hosts everywhere. Of course, others may disagree.
New DOM Features
The DOM extension is still a work in progress, but the good news is the core DOM-related functionality seems to be stabilizing. There's also more been done to the XPath and XSLT support it provides and, perhaps most interesting of all, it's now capable of parsing HTML documents without choking (more or less).
Here's a simple HTML document that contains some classic badly-formed XML but still valid HTML:
<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title> Example Page </title>
</head>
<body>
<h2>Parsing HTML with DOM</h2>
<p>This page has some examples of classic badly formed HTML.
<br>
<form>
<select>
<option>Red
<option selected>Blue
<option>Green
</select>
</form>
</body>
</html>
Using the DOM extension, I can now happily parse it like so:
<?php
// Open the HTML document
$doc = html_doc_file('/www/sitepoint/php5/example.html');
// Get the <head /> element
$head = $doc->get_elements_by_tagname('head');
$head = $head[0];
// Get the <body /> element
$body = $doc->get_elements_by_tagname('body');
$body = $body[0];
// Extracts the contents of <title />
function getTitle ($head) {
$headers = $head->child_nodes();
foreach ( $headers as $header ) {
if ( $header->tagname() == 'title' )
echo ("<b>Page Title:</b> ".$header->get_content()."<br />\n");
}
}
// Parses the <body /> element
function parseBody($body) {
$contents = $body->child_nodes();
foreach ( $contents as $content ) {
switch ( $content->tagname() ) {
case 'h2':
echo ( "<b>Header 2:</b> ".$content->get_content()."<br />\n");
break;
case 'p':
echo ( "<b>Paragraph:</b> ".$content->get_content()."<br />\n");
break;
case 'form':
$inputs = $content->child_nodes();
foreach ( $inputs as $input ) {
if ( isset ( $input->tagname )
&& $input->tagname == 'select' )
parseSelect($input);
}
break;
}
}
}
// Extract the contents of a <select />
function parseSelect($select) {
echo ( "<b>Select:</b>\n<ul>\n" );
$options = $select->child_nodes();
foreach ( $options as $option ) {
echo ( "<li>".$option->get_content() );
if ( $option->has_attribute('selected') )
echo ( " <<< SELECTED" );
echo ( "</li>\n" );
}
echo ( "</ul>\n" );
}
getTitle($head);
parseBody($body);
?>
Script: dom_html.php
The badly-formed tags and attributes, such as the unclosed <p> tags, and the "selected" attribute, cause no problems whatsoever.
Aside from helping PHP developers to "mine" other Websites (whether you should is a question I leave to your own personal ethics), this will no doubt prove important for "transforming" HTML into other formats, be it XHTML, or something else—like PDF.
Simple XML
The Simple XML extension introduces a new type of parser to PHP, using what is generally known as an "Object Mapping XML API" ( O'Reilly recently ran A Survey of APIs and Techniques for Processing XML [61]—see it for more detail ).
If you're familiar with the DOM API, you'll know that it creates from an XML document a tree structure containing objects that represent the XML elements it found. As the above DOM HTML parsing example demonstrates, there's still a lot of work for a developer to convert these into something they can use within an application.
An "Object Mapping XML API" takes a different view, which can help make life far easier for developers. Like the DOM API, it builds a tree structure upon reading an XML document but, unlike the DOM API, it converts the XML elements into the native data types used by the programming language in question. This means that the moment the document is parsed, you can begin to use the results directly.
The SimpleXML extension to PHP provides a mechanism to quickly convert simple XML documents into native PHP types. For example, if I have an XML document like this:
<?xml version="1.0"?>
<config>
<email>sysadmin@sitepoint.com</email>
<database>
<host>localhost</host>
<type>MySQL</type>
<user>harryf</user>
<pass>secret</pass>
<dbname>sitepoint</dbname>
</database>
</config>
Parsing it now looks like:
<?php
$xml = file_get_contents('config.xml');
$config = simplexml_load_string($xml);
echo ('<pre>');
print_r($config);
echo ('</pre>');
?>
Script: simplexml.php
The output is:
simplexml_element Object
(
[email] => sysadmin@sitepoint.com
[database] => simplexml_element Object
(
[host] => localhost
[type] => MySQL
[user] => harryf
[pass] => secret
[dbname] => sitepoint
)
)
In practice, this might mean the following code is possible:
<?php
// Get the file
$xml = file_get_contents('config.xml');
// Convert the file to native PHP variables
$config = simplexml_load_string($xml);
// Copy the database node to a new variable
$db = $config->database;
try {
// Connect to MySQL using the database information from the XML doc
if ( !@mysql_connect($db->host,$db->user,$db->pass) )
throw new Exception('Database Connect Error'.mysql_error());
} catch (Exception $e) {
// Send an email using the email variable
mail ( $config->email,$e->getMessage() );
}
?>
Script: simplexml1.php
Notice how, after parsing the XML document with SimpleXML, I can immediately begin using the data?
The SimpleXML extension does have limitations. In particular, I found it ignores XML attributes and can only deal with XML documents of up to three levels of node in depth, including the root node. At first glance that may seem like a serious limitation, but when you consider the DOM extensions dump_node() [62] method, SimpleXML could be a very useful partner when using DOM to parse an XML document, eliminating a whole bunch of (irritating) calls you have to make to get to the data you want. Note, however, that with the Beta, there seems to be a bug which prevents SimpleXML and DOMXML being used in the same script.
Otherwise, given libxml's support for XML schema, the approach taken by SimpleXML could be expanded upon to convert complete XML documents straight into native PHP types; very nice indeed, thank you!
That's not all folks...
Now if all this wasn't enough, there are some other important new features that are either already available, or are coming soon.
Streams
Wez Furlong has done an outstanding job to unify essentially all PHP IO operations around the Streams API [63]. Some of this is already available with PHP4; for example you can use the file function fopen() to connect to a remote Website like this:
fopen('http://www.sitepoint.com','r');
At an engine level, practically all IO related functionality within PHP5 should be unified under Streams, which means things like:
fopen('ftp://www.ftpserver.com','r');
are very possible.
What's more, it offers some very interesting possibilities for those PHP developers implementing clients for other network protocols (such as the mail protocol SMTP), as suggested by the stream_wrapper_register() [64] function.
Also interesting is how the Streams API provides an alternative to the socket [65] functions, which can be a great help if you're planning to write some kind of stand alone (from Apache, that is) PHP server, as demonstrated here [66].
A full presentation on Streams is available here [67].
SPL
SPL stands for the Standard PHP Library, and for OOP enthusiasts, this is certainly a fascinating prospect. On the label, it's described as an extension that "allows you to register a set of standard engine level interfaces."
In practice, what it seems to mean is that you'll be able to write classes in PHP which become available for use via the native PHP constructs, as an alternative to requiring users work with the API you've defined.
Right now, the focus of the SPL extension has been to make it possible to implement Iterators [68], which become available for use via PHP's foreach() construct.
That may not sound too interesting, but for anyone who's struggled with "binding" a result from a database to a class that, say, renders an HTML table, this will likely put a very big grin on your face.
The SPL extension wasn't available with the Beta (unless I missed something), but in theory using it might look something like this:
<?php
// Note: doesn't work with the PHP5 beta
class Colors implements spl_iterator {
var $collection;
function __construct() {
$this->collection = array ('red','blue','green');
}
function new_iterator() {
return new ColorIterator($this);
}
}
class ColorIterator implements spl_forward {
private $colors;
private $pos = 0;
private $max;
function __construct($colors) {
$this->colors = $colors;
$this->max = count ( $this->colors->collection );
}
function current() {
return $this->colors->collection[$this->pos];
}
function next() {
$this->pos++;
}
function has_more() {
return $this->pos < $this->max;
}
}
$colors = new Colors();
foreach($colors as $color) {
echo ( $color.'<br />' );
}
?>
In other words, the code that's using the collection (the collection being the Colors class in above example) no longer has to care about knowing the correct iterator methods required to fetch the data. Personally, I think that's a big step forward for code re-use in PHP.
Given the name of this extension, it would seem that the authors’ plan is not to stop at iterators. Perhaps we'll see other well known design patterns that are often implemented within the class libraries of languages like Java, such as the Observer pattern [69], making their way in here.
ADT
ADT stands for Abstract Data Types, an extension for which the source is online here [70]. The idea behind the ADT extension is to implement in PHP some of the more unusual data types (the real Computer Science stuff) including Stacks, Binary Trees, Heaps and Graphs. Certainly these will be important if PHP wants to become a serious general purpose language, rather than one focused on Web development. And they might also open the door to new possibilities, such as building fast, pure PHP search engines.
At present, it doesn't look like the ADT extension will be available as part of the core of PHP5 (or even as a stable extension), as it’s not much more than a prototype at the moment. Other priorities (such as PHP's day job as a solution for Web programming) are taking precedent, but if you're interested in knowing more, try this presentation [71].
Backwards Compatibility
In general, my guess is that 90% of PHP4 scripts will be able to run under PHP5 without any modification. Of course, I haven't done any intensive tests here, but most of the new features PHP5 provides are add-ons, rather than replacements, to PHP4. The old style constructors, for example, are still supported, as is the reference operator, which is effectively ignored where objects are concerned in PHP5.
If your code is primarily procedural there should be very few problems, if any at all.
However, you will have problems if you have PHP4 classes called "abstract" or "interface". Also I noticed PHP4’s built-in "stdClass" has disappeared, which may affect some.
As a vague rule of thumb, if you've been writing code which you might describe as "pushing the limit" of PHP4's intended use, then you probably need to take a serious look the impact of PHP5.
You may also need to be careful if you're using the SAX or XSLT extensions (and DOM, as always). The switch to libxml should be invisible, but as with all things computer-oriented, you never can be 100% sure until you've tried it. Otherwise, the impact of PHP5 will (hopefully) mean business as usual.
All that said, when the full PHP5 release comes around, make sure you test your code before running it in live environment.
Making Waves
Phew! You got through all that. So what about the real question: what does PHP5 mean for PHP's future? This is my subjective, but hopefully informed opinion.
Laying it one the line, a recent comment on SitePointForums [72] highlights a major issue with PHP today:
"The problem is that PHP desperately needs a decent class library of small, cohesive and reusable components."
This point re-surfaces as a fascinating and very insightful discussion: Java API as base for PHP library? [73] (highly recommended reading).
At first glance, that may be a depressing thought. However, it's worth reminding yourself that despite its problems, PHP is a massive success.
Recent estimates from Zend place the number of active PHP developers at somewhere around 500,000. Back in June 2002 Netcraft's Survey placed PHP ahead of ASP [74] (essentially making PHP the most popular technology for the Web), and since then, PHP has only continued to grow in popularity [75]. Otherwise Adam Tractenberg, author of O'Reilly's PHP Cookbook, has a concise summary of PHP's history, "The battle for middleware: PHP versus the world" (a PDF at http://www.trachtenberg.com/PHP.pdf [76]), which serves as a reminder of why PHP is where it is now.
But back to the "problem". From where I stand, PHP5 is all about addressing precisely this issue -- providing the basis for code re-use and component oriented design.
But PHP5 is not just a matter of a re-vamped object model. There are other signs, some part of PHP5, others surrounding the use of PHP, which indicate a very bright future.
Engine Level Standards
As you may have noticed while reading this article, I've used the word "standard" quite a bit. For an Open Source project like PHP, the development of PHP applications is fragmented. The more standards apply to the development of PHP code, the lesser will be the effort required to integrate any of the multitude of freely available PHP applications [77] out there.
At some level, PHP developers need to consider their code in the context of how it fits with other projects. Taking this approach is like investing in your own future -- the stronger PHP appears as a technology, the better your chances of getting paid work with it. That means becoming conversant in the Object Oriented Paradigm, design patterns, and so on. Two reads well worth your time are Martin Fowler's Refactoring: Improving the Design of Existing Code [78], and Patterns of Enterprise Application Architecture [79], along with the original Gang of Four’s Design Patterns [80].
That said, one thing that’s obvious from the release of PHP5 is the development team’s willingness to implement fundamental standards within the PHP engine itself. Most obvious is the SPL extension, which I've already looked at, but other moves, such as the object oriented API to SQLite, and a new MySQL API called mysqli [81], which offers both functional and object-oriented APIs to MySQL, make me wonder whether we're on the way to seeing database abstraction at an engine level in PHP.
By implementing fundamental standards (such as Iterators) at an engine level, there's a very good chance that PHP developers will actually agree to use them, allowing development effort to be focused on implementing "architectural" classes that fit together nicely.
Of PHP, J2EE and .NET
It’s not unreasonable to say that over the next five years, choosing a server side platform for Web-based intranet, extranet and Internet applications will, for most, boil down to three main options: J2EE, .NET and PHP. This is not to say that others like mod_perl, mod_python/Zope and Coldfusion will disappear, but that none of those have the critical mass required to be regarded as a "major player" for the Internet.
So of "The Big Three", the first thing to spot is the platform that encourages use of dynamic typing. Unless you count JScript.NET or Jython, of the three options only PHP is committed to providing that sort of language level flexibility. Think of it this way: how often do you hear of a Perl, PHP or Python project that failed to deliver anything?
More interesting to consider is how PHP sits relative to the other two major alternatives.
In looking at the Object Oriented Evolution of PHP [82], one of the features Zeev emphasised was how PHP can interact with external component models [83] such as Java, COM and .NET. In other words, you needn't necessarily choose PHP or… Instead, you can choose PHP and…
Also notable is Sun's and Microsoft's views on PHP.
One of the fascinating developments from this year's JavaOne was that Sun and Zend are going to collaborate to Push Scripting for Java [84], which should deliver a Java Specification for Scripting Pages in Java Web Applications [85], PHP being the reference implementation. That's great news for PHP, as well as Java. We may start to see a growing number of developers with a Java background contribute their input to PHP, which would certainly boost standards of development, and increase the acceptance of PHP as an enterprise technology, although it's already getting there [86] on its own. Should IBM, for example, make PHP a core component in WebSphere, it will be time to dust of those resumes...
Meanwhile, rumor has it [87] that PHP is on Microsoft's radar as the number one "threat" to ASP.NET. Microsoft recently published Head-to-Head: PHP [version 4] vs. ASP.NET [88], which seems to be an attempt to enlighten PHP developers of the errors of their ways. Aside from being badly researched [89] as far as its comments regarding PHP are concerned (the authors, according to Google, usually spend their time writing about .NET, which may explain the problem), this report also fails to come to a clear conclusion for or against either technology. And there are other signs [90] that Microsoft hopes to sway PHP developers towards .NET.
My view on understanding these opposing strategies is that Java/J2EE has pretty much sown up the high end enterprise Web space with application servers like SunOne, IBM's WebSphere, JBoss etc. plus support in Oracle. At the same time, J2EE is not being seriously pushed as a technology for "the masses". Put another way, Sun et al. regard PHP as a complementary technology that could introduce Java to new markets, rather than as a competing technology.
At the other end of the spectrum, Eweek recently published .Net: 3 Years of the 'Vision' Thing [91], which suggests .NET has so far failed to make a serious dent in Java’s market share. Microsoft had hoped that, by riding on the back of Web services, they could gain a significant lead on Java, but the infancy of Web services has held them up, allowing Java to even the playing field. Pushing .NET into big companies represents an uphill battle. Meanwhile, there's this massive market of lower-end applications and sites built with PHP, which, when considered as a whole, represent very significant revenue to those selling the software used to build and run those sites.
If you’ve spent a little time playing with ASP.NET, you’ll know that when you compare it with PHP from a theoretical software engineering point of view, ASP.NET comes out looking very shiny indeed. ASP.NET is a fascinating study in OOP, and Web Forms in particular is something I'd like to see in PHP [92] (watch this space [93]).
But, by crass analogy, ASP.NET is much like driving a BMW 8 series, while PHP is more like a VW Golf. Although the extra comfort of a luxury car is nice, who cares when you just want to get reliably from A to B for a reasonable price? Put in blunt business terms, this comment [94] pretty much sums it up.
One other issue that separates PHP from J2EE and .NET is that the latter two practically require the developer to use some form of IDE. Statically typed languages like Java and C# are too verbose, with class libraries that are too big to memorize -- a fact that deters most developers from spending much time hand coding them. Bruce Eckel made an interesting comment [95] along the same lines in a recent interview, comparing Java and Python (from which PHP isn't so far removed):
"Another number that used to be bandied about is that programmers can produce an average of ten working lines of code per day. Say I open up a file and read in all the lines. In Java, I've probably already used up my ten working lines of code for that day. In Python, I can do it in one line. I can say, "for line in file('filename').readlines():," and then I'm ready to process the lines. And I can remember that one liner off the top of my head, so I can just really flow with that."
Now, maybe you're happy working with IDE's like Visual Studio, NetBeans and Eclipse. Or perhaps you're like me -- you get nervous and frustrated by having to learn how to use a tool that gives you a "dumbed down" version of events, and you’d rather stick to the humble text editor.
Given the budget constraints and productivity expectations that today’s developers have to deal with, only dynamically typed languages like Perl, Python and PHP make realistic choices if you like to work in a pure ASCII environment. In fact, I'd argue that a PHP developer can at least keep pace with a Java / C# developer working in an IDE when it comes to measurable results.
Anyway, the point here is not to get sidetracked into some kind of PHP vs. X debate, but rather to realise that PHP delivers something that appeals to both developers and businesses, and which continues to spur it to greater popularity. High brow software theory would argue that a language that doesn’t enforce the idea that "everything is an object" is fundamentally flawed. Instead of listening to "reason", PHP5 delivers something that, in my opinion, represents the best of both worlds: an object model approaching that of Java's, as well as continued support for the procedural paradigm.
Whether you see it as "the best of both worlds" or simply "a mess" is up to you, but like it or not, PHP just keeps on growing.
PHP: The Next Generation
PHP5 provides the foundations on which we can construct modular, component-based PHP applications. With the features PHP5 puts at our disposal, we can begin building the next generation of PHP applications with a mind to re-use, scalability, flexibility and all that Jazz.
Going back to the question of PHP's "problem" today – that of integration with existing PHP applications – this next generation might manifest in the developers of PHP discussion forums providing us with a set of authentication and messaging components that we could easily re-use to build well integrated Websites that were customized to our own particular requirements, the forum components acting as the "core" of our site.
Developing an ecommerce site to sell pets, for example, might simply be a case of combining a collection of third party classes available in other applications which have some of the features you need, then building the code specific to your site on top of these.
Starting a new PHP project would longer mean writing everything from scratch; it would be an effort that focused on the specific problems that project was trying to solve.
In other words less hair loss, more free time!
PHP6!
Just when thought it was finally safe to go back into the water, I need to tell you that the letters 'PHP' and the number '6' have been used in earnest.
One subject of note at this year's OSCON [96] Parrot [97]. Parrot is a virtual machine that’s being used as the underlying engine for Perl 6, and which is also being implemented by Ponie [98], allowing Perl 5 scripts to run happily alongside Perl 6.
What's interesting about Parrot is that it may eventually run Python [99] and has caught Sterling's eye [100] -- he's even tried a prototype [101].
Of course, they’re all pipe dreams at the moment, but imagine a world where you write PHP with both Perl's CPAN and Python’s classes at your disposal. The mind boggles...
Don't forget to download all the code included in this review [102].
Further PHP5 Reading
- PHP5 Introduction [103] - the author does a great job of touring through the new PHP5 features.
- Introduction to PHP5 [104] - PHPBuilder gives a rundown of new features with examples.
- Presentation Introducing PHP5 [105] - the "official" PHP5 tour, presented to OSCON 2003 and LinuxTag.
- Ask the Experts - About the Future of PHP [106]- Zeev Suraski addresses specific PHP5-related questions.
[1] http://www.php.net
[2] http://www.edwardbear.org/blog/
[3] http://news.php.net/article.php?group=php.internals&article=2971
[4] http://c2.com/cgi/wiki?DesignPatterns
[5] http://www.sitepointforums.com/forumdisplay.php?forumid=147
[6] http://www.phppatterns.com/index.php/article/articleview/23/1/2/
[7] http://ez.no/developer
[8] http://www.interakt.ro/products/Krysalis/
[9] http://www.phppatterns.com/index.php/article/articleview/6/1/1/
[10] http://pear.php.net
[11] http://www.phpclasses.org
[12] http://www.php-mag.net/itr/online_artikel/psecom,id,330,nodeid,114.html
[13] http://www.jclark.com/xml/expat.html
[14] http://xmlsoft.org/
[15] http://www.gingerall.com/charlie/ga/xml/p_sab.xml
[16] http://www.php.net/xml
[17] http://www.php.net/xslt
[18] http://www.php.net/domxml
[19] http://www.sitepointforums.com/showthread.php?threadid=78687
[20] http://www.sitepoint.com/article/662
[21] http://www.mindview.net/Books/TIJ/
[22] http://www.sitepointforums.com/forumdisplay.php?forumid=147
[23] http://www.php.net/manual/en/installation.php
[24] http://www.hwaci.com/sw/sqlite/
[25] http://news.php.net/article.php?group=php.internals&article=2715
[26] http://php.weblogs.com
[27] http://php.weblogs.com/discuss/msgReader$2727
[28] http://www.hwaci.com/sw/sqlite/faq.html
[29] http://www.hwaci.com/sw/sqlite/lang.html
[30] http://talks.php.net/show/php5-intro-oscon-2003/33
[31] http://www.hwaci.com/sw/sqlite/c_interface.html
[32] http://www.php.net/shmop
[33] http://talks.php.net/show/php5-intro-oscon-2003/33
[34] http://www.sitepointforums.com/forumdisplay.php?forumid=147
[35] http://www.zend.com/engine2/ZendEngine-2.0.pdf
[36] http://www.php.net/is_a
[37] http://www.artima.com/forums/flat.jsp?forum=32&thread=2959
[38] http://www.artima.com/intv/abcs.html
[39] http://www.php.net/overload
[40] http://www.phppatterns.com/index.php/article/articleview/28/1/2/
[41] http://www.php.net/func_num_args
[42] http://www.php.net/is_int
[43] http://www.lastcraft.com/simple_test.php
[44] http://www.sitepointforums.com/showpost.php?postid=840152&postcount=52
[45] http://lambda.weblogs.com/discuss/msgReader$4816?mode=day
[46] http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html
[47] http://www.webkreator.com/php/configuration/automatic-php-class-loading.html
[48] http://mindview.net/WebLog/log-0025
[49] http://www.phppatterns.com/index.php/article/articleview/33/1/2/
[50] http://pear.php.net/package-info.php?package=SOAP
[51] http://www.php.net/tokenizer
[52] http://www.sitepointforums.com/showpost.php?postid=810895&postcount=34
[53] http://www.php.net/trigger_error
[54] http://www.php.net/set_error_handler
[55] http://www.devshed.com/Server_Side/PHP/ErrorHandling/
[56] http://www.churchillobjects.com/c/11012d.html
[57] http://www.linuxworld.com/story/32854.htm
[58] http://www.interakt.ro/products/Krysalis/
[59] http://www.xmlsoft.org
[60] http://xmlsoft.org/
[61] http://www.xml.com/pub/a/2003/07/09/xmlapis.html/
[62] http://www.php.net/manual/en/function.domnode-dump-node.php
[63] www.php.net/stream/
[64] http://www.php.net/manual/en/function.stream-wrapper-register.php
[65] http://www.php.net/sockets
[66] http://talks.php.net/show/php5-intro-oscon-2003/30
[67] http://talks.php.net/show/wez-streams2003
[68] http://www.phppatterns.com/index.php/article/articleview/50/1/1/
[69] http://www.phppatterns.com/index.php/article/articleview/27/1/1/
[70] http://www.php.net/~sterling/adt/
[71] http://talks.php.net/show/adt-amdam/
[72] http://www.sitepointforums.com/showpost.php?postid=816463&postcount=22
[73] http://www.sitepointforums.com/showthread.php?threadid=116878
[74] http://newsforge.com/article.pl?sid=02/06/11/011243&mode=thread&tid=5
[75] http://www.php.net/usage.php
[76] http://www.trachtenberg.com/PHP.pdf
[77] http://www.hotscripts.com/PHP/Scripts_and_Programs/index.html
[78] http://www.amazon.com/exec/obidos/ASIN/0201485672/
[79] http://www.amazon.com/exec/obidos/ASIN/0321127420/
[80] http://www.amazon.com/exec/obidos/ASIN/0201633612/
[81] http://www.php.net/manual/en/ref.mysqli.php
[82] http://www.devx.com/webdev/Article/10007/
[83] http://www.devx.com/webdev/Article/10007/0/page/4
[84] http://www.infoworld.com/article/03/06/12/HNsunscripting_1.html
[85] http://eai.ebizq.net/news/news0612903.html
[86] http://www.dreamlab.ca/technology/phpsites/
[87] http://www.infoworld.com/article/03/05/09/19OPcringely_1.html
[88] http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/phpvsaspnet.asp
[89] http://blogs.phparch.com/b2index.php?m=200305#128
[90] http://blogs.phparch.com/mt/archives/000051.html
[91] http://www.eweek.com/article2/0,,1184728,00.asp
[92] http://www.sitepointforums.com/showthread.php?threadid=112379
[93] http://sourceforge.net/projects/wact
[94] http://www.sitepointforums.com/showpost.php?postid=834914&postcount=277
[95] http://www.artima.com/intv/aboutme2.html
[96] http://www.onlamp.com/pub/a/onlamp/2003/07/09/oscon_report.html
[97] http://www.parrotcode.org/
[98] http://opensource.fotango.com/ponie/
[99] http://slashdot.org/articles/01/03/28/1742237.shtml
[100] http://www.edwardbear.org/blog/archives/000213.html
[101] http://www.edwardbear.org/blog/archives/000156.html
[102] http://www.sitepoint.com/examples/php5/php5_code.zip
[103] http://www.phpvolcano.com/articles/php5/index.php
[104] http://www.phpbuilder.com/columns/argerich20030411.php3
[105] http://talks.php.net/show/php5-intro-oscon-2003/
[106] http://www.zend.com/php/ask_experts.php
SitePoint Marketplace
Buy and sell Websites, templates, domain names, hosting, graphics and more.
Download sample chapters of any of our popular books.

Harry has been working in corporate IT since 1994, with everything from start-ups to Fortune 100 companies. Outside of office hours he runs

