This article concludes a series on PHP5 OOP. In the first article, “PHP 5 OOP: Interfaces Abstract Classes and the Adapter Pattern,” I presented an interface for abstracting database access from the actual database. In the second article, “PHP 5 OOP: Protecting Data With Visibility,” I showed how to expand upon the functionality built in the first article in order to show how to protect your data using visibility modifiers. In the third article, “Delegation and Custom Exceptions,” I showed you how delegation can mirror the query related DB interface to work on a specific query result.
This article will teach you how you can use inheritance in conjunction with the template pattern to create application-specific instances of your objects.
One of the major concepts behind the object-orientated methodology is code reuse. When coding their application, developers are encouraged to split large pieces of functionality up into smaller objects, each designed to accomplish a specific task. These objects should be generic enough to be used in other applications. Your MysqlDB and DBQuery object are two examples of objects that are likely to be used in more than one application.
The Template pattern modifies the logic of a class to make it more complete or more specific. Several examples of this have already been demonstrated earlier in this tutorial through the use of inheritance. The template pattern also can be used, however, to create an instance-specific class; though the use of the constructor, it initializes all the required properties.
Take the MySqlDB object as an example. Before any of its functions can be used, the connect() function must be called and supplied the hostname, user name, and password for the server. A database also needs to be selected. The connection information often will remain constant in your application; therefore, an object template can be created that extends the MySqlDB object.
Listing 1: The Templated MySqlDB class
class ProdMySqlB extends MySqlDB { public function __construct($dbname = null) { /* connection credentials */ $host = 'production.example.com'; $user = 'username'; $password = 'password'; // make the connection $this->connect($host, $user, $password); if (! is_null($dbname)) { $dbname = $this->escape_string($dbname); if (! $this->query("use $dbname")) { throw new Exception("Error selecting database."); } } } } class SatgeMySqlB extends MySqlDB { protected $log; public function __construct($dbname = null) { $log = new Log('dbquery.log'); // create a Log file object /* connection credentials */ $host = 'staging.example.com'; $user = 'username'; $password = 'password'; $this->connect($host, $user, $password); if (! is_null($dbname)) { $dbname = self::escape_string($dbname); if (! $this->query("use $dbname")) { throw new Exception("Error selecting database."); } } } public function query($query) { // add query text to the log $this->log->write_log("Executing query: $query"); return parent::query($query); } }
The above example creates two new subclasses of MySqDB, one for a production server (the live site) and one for a staging server, which is used to test the code before it is moved to the production server. The connection information is now contained within the class constructor that takes an optional single argument that pre-selects the database. You now can create a connection specific instance of either class as follows:
$db = new ProdMySqlDB('exmaple'); // creates production connection // and pre-selects the example // database
What Are the Advantages of This and Why Use It?
You can use the template pattern in this fashion to obtain several advantages:
- Creating instance specific classes for any object in your application means that were the initialization information (in other words, the connection information in the above example) were to change, it needs only be changed in one place.
- As it is an implementation of inheritance, the template pattern enables you to extend the functionality of the object and add extra features. The SatgeMySqlB creates an instance of a Log file object (not defined here) and writes each query executed to the log. This is useful on a staging server where the need to debug code and database queries often arises.
- Lastly, through the use of the template pattern, you can create several versions of the same object with different instance-specific initialization data, as demonstrated above. A factory function then can be defined to select the most appropriate object based on the current environment:
function create_db() { // get the server's IP address $ip = $_SERVER['SERVER_ADDR']; switch($ip) { case '10.10.1.5': // staging server return new StageMySqlDB('example'); default: // all other IPs signify this is the live site return new ProdMySqlDB('example'); } }
Creating an instance of the MySqlDB object and connecting to the database is now as simple as:
$db = create_db(); // executes the factory function
Conclusion
In this series of articles, I have taken you through new features provided by the PHP 5 object model and demonstrated how design patterns (methods of solving common OOP related problems) can be applied through PHP. You now should be equipped with the knowledge needed to effectively use PHP 5’s new object model. In using it effectively, you will produce more efficient, scalable, and robust applications while making the job of coding it easier.
About the Author
Adam Delves is a university student and Web programmer from the UK; he is studying computing. He has been a PHP programmer for over three years and now runs two small Web sites. He also writes articles for PHP builder.com.