April 23, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

PHP 5 OOP: Delegation and Custom Exceptions

  • May 23, 2006
  • By Adam Delves
  • Send Email »
  • More Articles »

This article continues 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 this article you will learn how delegation can mirror the query related DB interface to work on a specific query result.

The DBQuery Object

At present our DBQuery object simply mimics (all be it - rather simply) a stored procedure. Once executed a result resource is returned which you must store and pass the MySqlDB object if you wish to use functions such as num_rows() or fetch_row() on the result set. Would it not be nice if the DBQuery object were able to implement the functions which the MySqlDB object implements; that are designed to work on the result of an executed query? Take the code in your previous example and let us assume that the DBQuery object now manages the result resource for us:

Listing 1: Using the DBQuery class.

require 'mysql_db.php'; 
    require_once 'query.php'; 


    $db = new MySqlDb; 
    $db->connect('host', 'username', 'pass'); 
    $db->query('use content_management_system'); 

    $query = new DBQuery($db); 
     
    $query->prepare('SELECT fname,sname FROM users WHERE username=:1S AND pword=:2S AND expire_time<:3I'); 

    try { 
        if($query->execute("visualad", "apron", time()))->num_rows() == 1) { 
            echo('Correct Credentials'); 
        } else { 
            echo('Incorrect Credentials / Session Expired'); 
        } 
    } catch (QueryException $e) { 
        echo('Error executing query: ' . $e); 
    } 

The main lines of interest in the modified code are the catch statement and the execute statement.

  • The execute statement no longer returns a result resource, it now returns the DBQuery object itself.
  • The DBQuery object now implements the num_rows() function which we are familiar with from the DB interface.
  • If the query fails to execute, it throws an exception of type QueryException. Which when converted to a string returns details of the error which occurred.

To implement this, you need to use delegation. You have already been using delegation in our DBQuery object, but will now use it to a greater extent to tie it in completely with the MySqlDB object. The DBQuery object is already initialized with an object which implements the DB interface and it already contains a member function called execute, which, invokes the query() method of the DB object to execute the query. The DBQuery object its self does not actually query the database, it leaves that to the DB object. This is delegation - a process by which one object implements a particular behavior by sending messages to another object which implements the same or a similar behavior.

You are going to modify the DBQuery object to include all functions which work on a result resource from the DB object. You will use the result stored when the query is executed to invoke the corresponding function of the DB object and return its result. The following functions will be added:

Listing 2: Extending the DBQuery class with delegation.

class DBQuery 
{ 
    ..... 

    public function fetch_array() 
    { 
        if (! is_resource($this->result)) { 
            throw new Exception('Query not executed.'); 
        } 

        return $this->db->fetch_array($this->result); 
    } 

    public function fetch_row() 
    { 
        if (! is_resource($this->result)) { 
            throw new Exception('Query not executed.'); 
        } 

        return $this->db->fetch_row($this->result); 
    } 

    public function fetch_assoc() 
    { 
        if (! is_resource($this->result)) { 
            throw new Exception('Query not executed.'); 
        } 

        return $this->db->fetch_assoc($this->result); 
    } 

    public function fetch_object() 
    { 
        if (! is_resource($this->result)) { 
            throw new Exception('Query not executed.'); 
        } 

        return $this->db->fetch_object($this->result); 
    } 

    public function num_rows() 
    { 
        if (! is_resource($this->result)) { 
            throw new Exception('Query not executed.'); 
        } 

        return $this->db->num_rows($this->result); 
    } 
} 

The implementation of each function is quite simple. It first checks to ensure the query has been executed, then, delegates the task to the DB object, returning its result as if it were the query object itself called the underlying database function.

Type Hinting

For delegation to work, we need to ensure that the $db variable of the DBQuery object is an instance of an object that implements the DB interface. Type Hinting is a new facility in PHP 5, which enables you to force function arguments to be objects of specific types. Before PHP 5 the only way to ensure that function arguments were of a specific object type was to use the type checking functions provided PHP (i.e: is_a() ). Now you can simply enforce the object type by preceding the function argument with type name. You have already seen type hinting in our DBQuery object, to ensure that an object which implements the DB interface is passed to the objects constructor.


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

Not only can you specify object types, you may also specify abstract classes and interfaces when using type hinting too.

Throwing Exceptions

You may have noticed from the above code that you are catching an exception called, QueryException (we will implement this object later). An exception is similar to an error, however, more generic. The best way to describe an exception is an emergency. While an emergency might not necessarily be fatal, it must be dealt with. When an exception is thrown in PHP, the current scope of execution is immediately terminated, whether it be a function, try..catch block or the script itself. The exception then travels up the calling stack terminating each execution scope until it is either caught in try..catch block or it reaches the top of the calling stack where it will generate a fatal error.

Exception handling is another new feature in PHP 5 which, when used in conjunction with OOP, allows for fine control over error handling and reporting. A try..catch block acts as a mechanism to deal with an exception. Once caught, execution of the script continues from the next line of the scope from which the exception was caught and handled.

You need to change your execute function to throw an exception if the query fails. You will throw a custom exception object called QueryException, which is passed the DBQuery object that caused the error.

Listing 3: Throwing an exception.

    /** 
     * Executes the current Query 
     * 
     * Executes the current query replacing any place holders with the supplied 
     * parameters. 
     * 
     * @param mixed $queryParams,... Query parameter 
     * @return resource A reference to the resource representing the executed query. 
     */ 
    public function execute($queryParams = '') 
    { 
        //example: SELECT * FROM table WHERE name=:1S AND type=:2I AND level=:3N 
        $args = func_get_args(); 

        if ($this->stored_procedure) { 
            /* call the compile function to get the query */ 
            $query = call_user_func_array(array($this, 'compile'), $args); 
        } else { 
            /* a stored procedure was not initialized, so execute this as a standard query */ 
            $query = $queryParams; 
        } 
             
        $result = $this->db->query($query); 

        if (! $result) { 
            throw new QueryException($this); 
        } 

        $this->result = $result; 

        /* notice how we now return the object itself, this enables us to us 
            to call member function from the return result of this function */ 
        return $this; 
    } 





Page 1 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel