PHP5: Coming Soon to a Webserver Near You

If you have more than a little to do with PHP, 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 announced PHP5 Beta 1, 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. 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), 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)–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 and Krysalis, 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).

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 and PHP Classes 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). 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 SAX parser
  • the Gnome Project’s libxmllibrary to provide a DOM parser
  • Ginger Alliance’s Sablotron library for XSLT

Of the three, only the Sax extension is something PHP developers can rely upon 100%. The XSLT extension 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 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, as well as Kevin Yank’s article Object Oriented PHP: Paging Result Sets, 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 (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 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 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/.

For some of the reasoning behind the development team’s decision to embed it at the heart of PHP, try this post.

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 has some interesting thoughts on SQLite.

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, and SQL documentation for more information. There’s no official PHP documentation (other than this presentation) available now, but I found the implementation (in terms of PHP functions) conforms to the SQLite C Interface.

<?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, 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 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 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] 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 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 and the article it refers to.

Overloading

Overloading objects in PHP4 became possible via the (experimental) overload extension, which you can find explained in more detail here. 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(), which can be combined with type introspection like is_int() 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 for PHP Unit Testing, raised some very valid concerns about PHP's overloading here.

Aside from integration with other platforms and hacking, overloading provides some interesting opportunities for PHP developers to apply the Aspect Oriented Paradigm (as Markus mentions in his post), and to implement something like Python's Metaclasses.

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 keywooo
j

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

No Reader comments

Comments on this post are closed.