http://www.developer.com/

Back to article

PHP 5 OOP: Interfaces Abstract Classes and the Adapter Pattern


May 5, 2006

PHP 5 made significant improvements on the Object Orientated programming model of PHP 4 bringing it more in line with languages such as Visual Basic .NET and Java. The improved object model in PHP 5 makes developing applications using OOP much easier and gives you the programmer, greater flexibility.

In this series of articles I will demonstrate the new features of the PHP 5 object and show you how to create a database abstraction layer similar to PEAR DB. I will also introduce you to a few design patterns that can be applied to common OOP related problems.

In this article, I introduce you to some of the features of the PHP 5 object model. You will see how to create a database abstraction layer similar to the Pear DB abstraction layer. You'll also learn how to adapt this abstraction layer for your own uses.

Introduction

One of PHP's stronger areas is its support for database connectivity. It is able to connect to and talk to just about any database server or interface you can imagine. However, with this comes a few inherent problems; each database system has its own features, functions and in most cases they have their own versions of SQL. Although the functions used to access these databases are similar they do vary subtly meaning that if you were to want to port an application written for MySql to MS SQL server, the refactoring would require manually changing all calls to mysql_query() to mssql_query().

While we are not going to go as far as developing a database independent version of SQL, the new features of PHP 5 will be us build a consistent database API with support for stored procedures (where it is not already present). This will then make switching from one database system to another as painless as changing one line of code.

Interfaces Abstract Classes and the Adapter Pattern

The first features new to PHP 5 to be covered in this article are abstract classes and interfaces. These concepts are nothing more than features added to OOP, which help the programmer follow good coding standards.

Abstract Classes

An abstract class is a class that is only partially implemented by the programmer. It may contain one or more abstract methods. An abstract method is simply a function definition that serves to tell the programmer that the method must be implemented in a child class.

To create an abstract class we use the code shown in Listing 1:

Listing 1: An Abstract PHP class

<?php  
abstract class Weapon  
{  
    private $SerialNumber;  
    abstract public function fire();  


    public function __construct($SerialNumber)  
    {  
        $this->SerialNumber = $SerialNumber;  
    } 



    public function getSerialNumber()  
    {  
        return $this->SerialNumber;  
    }  
}  
?> 

The abstract class in Listing 1 contains some of the methods required for a weapon. The fire() method however, cannot be implemented because each different weapons use different firing different mechanisms. The method is therefore declared as abstract, meaning it will be implemented in a more specific child class.

Because the class is abstract, an instance of it can never be created (remember, it is only a partial implementation). Instead a child class must be created using inheritance and implement the fire method in itself. Failure to do so will result in a fatal error. Listing 2 shows a child class being created from the Abstract Weapon class.

Listing 2: Extending the Abstract Weapons class

<?php  
class Gun extends Weapon  
{  
    public function fire()  
    {  
        if($this->SafetyOff) {  
            return $this->CurrentBullet;  
        }  
    }  
}  


class Cannon extends Weapon  
{  
    public function fire()  
    {  
        $this->NeedsLoading = true;  
        return $this->CurrentCanon;  
    }  
?> 


An instance of the Cannon and Gun classes can now be created because they now fully implemented subclasses of weapon.

Interfaces

An interface is similar to an abstract class; indeed interfaces occupy the same namespace as classes and abstract classes. For that reason, you cannot define an interface with the same name as a class. An interface is a fully abstract class; none of its methods are implemented and instead of a class sub-classing from it, it is said to implement that interface.

An interface will be used in the database abstraction layer you create. This ensures that every time you create a class for a particular database, the same API is exposed. When using an interface, you can then rely on the methods defined for the interface to be part of the class because, if they are not, PHP will not parse it.

The MySql functions will be used as an example because they are the most commonly used amongst PHP programmers. The most commonly used functions are:

  • mysql_connect()
  • mysql_error()
  • mysql_errno()
  • mysql_query()
  • mysql_fetch_array()
  • mysql_fetch_row()
  • mysql_fetch_assoc()
  • mysql_fetch_object()
  • mysql_num_rows()
  • mysql_close()

If all the database class APIs you create can expose the same methods with the same return types then you can be sure that changing from one database to another, such as from MySql to Postgre SQL, will be painless. As such, the interface in listing 3 can be determined for your API.

Listing 3: An Abstracted Database Interface

interface DB 
{ 
    public function connect(); 
    public function error(); 
    public function errno(); 
    public static function escape_string($string); 
    public function query($query); 
    public function fetch_array($result); 
    public function fetch_row($result); 
    public function fetch_assoc($result); 
    public function fetch_object($result); 
    public function num_rows($result); 
    public function close(); 
} 

Any class implementing the interface must define each method that is declared in the interface, and each method must have at least the parameters identified in their interface definitions. It may have more parameters as long as they are optional, but it cannot have less.

Look at a class in Listing 4 that implements the database interface. You should recall that I mentioned the adapter pattern earlier. This is an example of the adapter pattern, which is used by programmers in order to adapt one API. The API you are adapting from could be another object-based API or as being done here, an adaptation from a modular API. If you want to read more about the adapter pattern, you can find a more detailed explanation and examples here.

Notice how the escape_string() method is included as a static method. This method does not require an active connection to a database and should not require and instance of any object which implements the DB interface. In my opinion, this is the single most important method of any database implementation; a poorly implemented escape string method could make your applications vulnerable SQL injection.

Listing 4: Implementing the database interface

class MySqlDB implements DB 
    { 
        private  $link; 
         
        public function connect($server='', $username='', $password='', $new_link=true, $client_flags=0) 
        { 
            $this->link = mysql_connect($server, $username, $password, $new_link, $client_flags); 
        } 
     
        public function errno() 
        { 
            return mysql_errno($this->link); 
        } 

        public function error() 
        { 
            return mysql_error($this->link); 
        } 

        public static function escape_string($string) 
        { 
            return mysql_real_escape_string($string); 
        } 

        public function query($query) 
        { 
            return mysql_query($query, $this->link); 
        } 
         
        public function fetch_array($result, $array_type = MYSQL_BOTH) 
        { 
            return mysql_fetch_array($result, $array_type); 
        } 

        public function fetch_row($result) 
        { 
            return mysql_fetch_row($result); 
        } 
         
        public function fetch_assoc($result) 
        { 
            return mysql_fetch_assoc($result); 
        } 
         
        public function fetch_object($result) 
        { 
            return mysql_fetch_object($result); 
        } 
         
        public function num_rows($result) 
        { 
            return mysql_num_rows($result); 
        } 
         
        public function close() 
        { 
            return mysql_close($this->link); 
        } 

You'll notice that there are many more mysql functions than methods that are adapted in the interface in listing 3. However, this small subset of functions is sufficient to meet the needs of most applications requiring trivial data storage and retrieval. The additional functions can be implemented and I have done this in the attached example file, you may also choose to add additional functionality to the class and the interface.

Listing 5: Creating a database class

$db = new MySqlDb; 
    $db->connect('host', 'username', 'password'); 
    $db->query('use users'); // we could also use $db->select_db here but it is not consistent with the interface 

    $result = $db->query("SELECT username FROM users"); 
         
    while($row = $db->fetch_assoc($reuslt)) { 
        echo($row['username']); 
    } 

As shown in listing 5, you can now create a class for each database you want and as long as it implements the DB interface, switching from one to another is as easy as changing one line of code:

$db = new MsSqlDb; 

Conclusion

In this first article, you've seen how to create an abstraction layer to access a database. Using this, you saw how creating an interface helps you to isolate your application so that you can easily switch from one database to anther without rewriting your own applications.

Downloads

You can download phpcodefiles.zip here.

About the Author

Adam Delves is a university student and web programmer from the UK who is studying computing. He has been a PHP programmer for over 3 years and now runs two small websites and writes articles for PHP builder.com.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date