phpfunk.com


Object Oriented Programming with PHP 5

The interface

What if instead of writing lines of code you worked with components and assembled them to create software applications. The components "know what to do" when you tell them do to something. That is object oriented programming and the component is the interface.

The interface is the public contract that a class agrees to comply with. As a user of components you don't care how the internal parts work, you just want something that implements the interface so you can use that component in your system.

Example of Interface: Database abstraction layer

When you make an abstraction to something you are really creating an interface that people can use to access that thing. The DBWrapper interface provides common methods you would need to work with a database irrespective of the database vendor (eg: mysql, mssql, postgresql, oracle, etc). Thats because you don't care how the internal bits work, you simply use the exposed interface.

Here's a portion of the DBWrapper class. Notice the actual name of the class is PhpFunk_DB_DBWrapper but for sake of brevity I'm omitting the PhpFunk_DB_ parts when I type. PhpFunk is the vendor who wrote the software and DB is the package the interface lives in.

interface PhpFunk_DB_DBWrapper {

    /**
     * Returns TRUE on successful connection
     *
     * @param  string  $host
     * @param  string  $user
     * @param  string  $password
     * @param  string  $database
     * @return boolean
     */
     function connect($host, $user, $password, $database);
    
    /**
     * @return void
     */
     function disconnect();
    
    /**
     * @return boolean
     */
     function isConnected();
    
    /**
     * Returns rows of assoc array or NULL if no matches are found.
     *
     * Returns FALSE on error.
     *
     * @return mixed
     * @param  string  $sql     SQL statement
     * @param  mixed   $args    argument or array of args
     * @param  string  $FILE    name of calling file
     * @param  int     $LINE    line number where called in $FILE
     */
     function selectRows($sql, $args='', $FILE='', $LINE='');
    
    /**
     * @return int number of rows affected
     * @param  string  $sql     SQL statement
     * @param  mixed   $args    argument or array of args
     * @param  string  $FILE    name of calling file
     * @param  int     $LINE    line number where called in $FILE
     */
     function execute($sql, $args='', $FILE='', $LINE='');
    
    // more methods
}

Notice that each method contains simply a method signature and no method body. The signature is the reserved word "function" followed by the name of the function and its argument list, which could be empty. The method body is the stuff between the curly braces {} which you don't see here because an interface doesn't define a method body. The classes implementing the interface provides the internal workings.

If you are not familiar with the commenting style shown, you must learn it. Its javadoc style comments and phpdocumentor will parse them to create really nice looking API. You need the API because it tells you how to use the interface. Here is an example of phpdocumentor generated web pages.

Implementation Example

Now lets look at a class that implements the DBWrapper interface. This class specifically works with mysql databases and has the terrible name of MySQLDriver. The class has all the same functions as DBWrapper except it provides an implementation of each function (ie: method body).

class PhpFunk_DB_MySQLDriver implements PhpFunk_DB_DBWrapper 
{
    
    var $db = FALSE;
    var $prepared;
    
    function MySQLDriver()
    {
        $this->db = FALSE;
        $this->prepared = '';
    }
    
    function connect($host, $user, $password, $database) 
    {
        if(($db = mysql_connect($host, $user, $password)) === FALSE) {
            //failed to connect
            return FALSE;
        }
        if( mysql_select_db( $database, $db ) === FALSE ) {
            //failed to select $database
            return FALSE;
        }
        $this->db =& $db;
        return TRUE;
    }
    
    function disconnect() 
    {
        if( $this->isConnected() ) {
            mysql_close($this->db);
            $this->db = FALSE;
        }
    }
    
    function isConnected() 
    {
        return $this->db !== FALSE;
    }
    
    
    function selectRows( $sql, $args='', $FILE='', $LINE='') 
    {
        if( $args ) {
            if( !is_array($args)){
                $args = array($args);
            }
        }
        if( is_array($args) ) {
            $sql = $this->escapeString( $sql, $args );
        }
        if( ($res = mysql_query($sql, $this->db)) === FALSE ) {
            trigger_error(mysql_error() . $FILE . $LINE, E_USER_WARNING);
            return FALSE;
        }
        if( mysql_num_rows($res) == 0 ) {
            return NULL;
        }
        $data = array();
        while( $row = mysql_fetch_assoc($res)) {
            $data[] = $row;
        }
        mysql_free_result($res);
        return $data;
    }

    function execute( $sql, $args='', $FILE='', $LINE='' ) 
    {
        if( $args && !is_array($args) ) {
            $args = array($args);
        }
        if( $args ) {
            $sql = $this->escapeString( $sql, $args );
        }
        if( mysql_query($sql, $this->db) === FALSE ) {
            trigger_error(mysql_error() . " on $FILE at $LINE", E_USER_WARNING);
            return FALSE;
        }
        return mysql_affected_rows($this->db);
    }
}

In theory you would write classes for each specific database vendor that implement the DBWrapper interface.

Summary

An interface is nothing more than a bunch of public methods. Each method may take one or more arguments of a particular type, or no arguments at all. Each method returns something, whether its a string, int, object or void. The methods themselves do not even have a method body, only a method signature. The method signature consists of its access (in this case always public), the method name, list of arguments and the return type (which includes returning void, ie nothing).

The "interface" cannot do anything, you need a class that implements that interface to actually use the interface. So including DBWrapper is not enough to use the database abstraction layer. You need to include MySQLDriver class that implements DBWrapper in order to use the abstraction with a mysql database.

  • Author: Tom Pimienta
  • Revised: 18 Apr 2008