Interfaces Abstract Classes and the Adapter Pattern
Interfaces Abstract Classes and the Adapter Pattern
The first feature we will visit which is new to PHP 5, is abstract classes and interfaces. These concepts are nothing more than features added to OOP which force the programmer to follow good coding standards.
Abstract Classes
An abstract class is a class which is only partially implemented by the programmer. It may contain 1 or more abstract methods. An abstract method is simply a function definition and serves to tell the programmer that the method must be implemented in a child class. To create an abstract class we use the following:
PHP Code:
<?php
abstract class Weapon
{
private $SerialNumber;
abstract public function fire();
public function __construct($SerialNumber)
{
$this->SerialNumber = $SerialNumber;
}
public function getSerialNumber()
{
return $this->SerialNumber;
}
}
?>
The above abstract class contains some of the methods required for a weapon, but, the fire method cannot be implemented because each different weapon fires using a different mechanism. We therefore declare this as abstract. Because the class is abstract an instance of it can never be created (remember, it is only a partial implementation), instead we must create a child class using inheritance and implement the fire method in the subclass, failure to do so will result in a fatal error.
PHP Code:
<?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;
}
?>
We can now create an instance of the Cannon and Gun classes because they now fully implemented subclasses of weapon.
Interfaces
An interface is similar to an abstract class, indeed the occupy the same namespace as classes and abstract classes, (hence you cannot define an interface with the same name as a class). An interface however, is a fully abstract class; none of its method are implemented and, instead of a class subclassing from it; it is said to implement that interface.
We are going to use an interface in our database abstraction layer, to ensure that every time we create a class for a particular database, it exposes the same API. When using them, we can then rely on the methods defined in the interface being part of the class, because, if they are not, PHP will not parse it.
We are going to take the MySql functions as an example as it is the most commonly used database amongst PHP programmers. The most commonly used functions are:
If all our database classes expose the same methods with the same return types we can be sure that changing from MySql to Postgre SQL will be painless. We therefore arrive at the following interface:
PHP Code:
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();
}
Each method declared in the interface must be defined by any class which implements it having at least the parameters identified. It may have more parameters (as long as they are optional), but it cannot have less. So let us look at a class which implements the DB interface. Didn't I mention something called the adapter pattern in the title? Yes, I did. Well, this is it – the adapter pattern is used by programmers in order to adapter one API to another using the original API. The API we are adapting from could be another object based API or like we are doing an adaption from a modular API. If you want to read more about the adapter pattern, you can find a more detailed explanation here.
Notice how we have included an escape_string() method 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; an poorly implemented escape string method could make your applications vulnerable SQL injection.
PHP Code:
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 we have adapted. However, this small subset of functions are sufficient to meet the needs of most applications which require trivial data storage an retrieval. The additional functions can be implemented and I have done this in the example file attached, you may also choose to add additional functionality to the class and the interface.
PHP Code:
$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");
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:
PHP Code:
$db = new MsSqlDb;
Conclusion
In this article you have seen how to protect and restrict the visibility of object data using the accessibility modifiers private, protected and public when declaring class variables. These modifiers are used in the MysqlDB and DBQuery classes to protect data important to the internal workings of the objects.
In our next article I'll present another new feature in PHP 5, type hinting. You will also get to extend the DBQuery to allow for support of all the functions in the DB interface that operate on the result of an executed query. This will be accomplished using delegation.
If you have any comments regarding this tutorial please post them here, not in this thread.
Last edited by visualAd; Apr 24th, 2006 at 12:13 AM.