October 31, 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 »

Using Inheritance to Throw Custom Exceptions

In PHP you can throw any object as an exception, but as a rule of thumb the exception should extend PHP's built in exception class. By creating your own custom exception you can record extra information about the nature of the error if any, create an entry in a log, or in fact do anything you like. Your custom exception will do several things:

  • Record the error message from the DB object generated by the query.
  • Give the exact details about the line on which the query error occurred by examining the calling stack.
  • Display the error message and query text when converted to a string.

In order to get error information and the query text, several changes need to be made to the DBQuery object.

  1. A new protected property needs to be added to the class called compiledQuery.
  2. The compile() function updates the query compiledQuery property with the query text.
  3. A function to retrieve the compiled query text should be added.
  4. A function to get the current DB object associated with the DBQuery object should also be added.

Listing 4: Throwing an exception.

class DBQuery 
{ 
    /** 
     * Stored the compiled version of the query. After a call to compile() or execute() 
     * 
     * @var string $compiledQuery 
     */ 
    protected $compiledQuery; 
     
    /** 
     * Returns the compiled query without executing it. 
     * @param mixed $params,... Query Parameters 
     * @return string Compiled Query 
     */ 
    public function compile($params='') 
    { 
        if (! $this->stored_procedure) { 
            throw new Exception("Stored procedure has not been initialized."); 
        } 

        /* substitute parameters */ 
        $params = func_get_args(); // get function arguments 
        $query = preg_replace("/(?compile_callback($params, 1, "2")', $this->query); 

         
        return ($this->compiledQuery = $this->add_strings($query)); // put the strings back into the query 
    } 

    public function getDB() 
    { 
        return $this->db; 
    } 

    public function getCompiledQuery() 
    { 
        return $this->compiledQuery; 
    } 
} 


You can now implement the QueryException class. Note how you traverse the calling stack to find the actual location in the script which caused the error. This comes into play when the DBQuery object which threw the exception is a descendant which is inherited from the DBQuery object.

Listing 5: The QueryException class.

/** 
* Query Exception 
* 
* Thrown by the {@link DBQuery} object if an error occurs while 
* attempting to execute a query. 
* 
*/ 
class QueryException extends Exception 
{ 
    /** 
     * Query Text 
     * 
     * @var string $QueryText; 
     */ 
    protected $QueryText; 

    /** 
     * Error Number / Code from the Database 
     * 
     * @var string $ErrorCode 
     */ 
    protected $ErrorNumber; 

    /** 
     * Error Message from the Database 
     * 
     * @var string $ErrorMessage 
     */ 
    protected $ErrorMessage; 
         
    /** 
     * Class constructor 
     * 
     * @param DBQuery $db Query object which threw this exception. 
     */ 
    public function __construct(DBQuery $query) 
    { 
        /* get the calling stack */ 
        $backtrace = $this->GetTrace(); 

        /* set the line and file - to the location where the error actually occurred */ 
        if (count($backtrace) > 0) { 
            $x = 1; 

            /* if the query class was inherited we need to disregard the calls by the classes descendants */ 
            while((! isset($backtrace[$x]['line'])) || 
                  (isset($backtrace[$x]['class']) && is_subclass_of($backtrace[$x]['class'], 'DBQuery')) || 
                  (strpos(strtolower(@$backtrace[$x]['function']), 'call_user_func')) !== false ) { 

                /* loop while there is no line number or the function called is a descendant of the DBQuery class */ 
                ++$x; 
                 
                /* if we reach the end of the stack, we use the first caller */ 
                if (($x) >= count($backtrace)) { 
                    $x = count($backtrace); 
                    break; 
                } 
            }                
     
            /* if the above loop made at least on iteration, we reduce it by 1 to find the actual line of code 
               which caused the error */ 
            if ($x != 1) { 
                $x -= 1; 
            } 
             
            /* finally we can set the file and line numbers, which should reflect the SQL statement which caused the error */ 
            $this->line = $backtrace[$x]['line']; 
            $this->file = $backtrace[$x]['file']; 
        } 
         

        $this->QueryText = $query->getCompiledQuery(); 
        $this->ErrorNumber = $query->getDB()->errno(); 
        $this->ErrorMessage = $query->getDB()->error(); 
             

        /* call the super class Exception constructor */ 
        parent::__construct('Query Error', 0); 
    }     
         
    /** 
     * Get Query Text 
     * 
     * @return string Query Text 
     */ 
    public function GetQueryText() 
    { 
        return $this->QueryText; 
    } 

    /** 
     * Get Error Number 
     * 
     * @return string Error Number 
     */ 
    public function GetErrorNumber() 
    { 
        return $this->ErrorNumber; 
    } 

    /** 
     * Get Error Message 
     * 
     * @return string Error Message 
     */ 
     public function GetErrorMessage() 
     { 
         return $this->ErrorMessage; 
     } 
         
     /** 
      * Called when the object is casted to a string. 
      * @return string 
      */ 
     public function __toString() 
     { 
         $output = "Query Error in {$this->file} on line {$this->line}nn"; 
         $output .= "Query: {$this->QueryText}n"; 
         $output .= "Error: {$this->ErrorMessage} ({$this->ErrorNumber})nn";             
             
         return $output; 
    } 

} 

The code seen at the beginning of this section now will now work as intended.

Conclusion

In this article you have seen how delegation can mirror the query related DB interface to work on a specific query result. The DBQuery object exposes identical functions, such as fetch_assoc() as the DB object. These however act on a single query. You have also seen how custom exceptions can be used to give detailed information on when and where an error occurred and how they enable finer control on the handling of errors. Next time we will look at how we can use inheritance in conjunction with the template pattern to create application specific instances of our objects.

If you have any comments regarding this tutorial please post them 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.



Page 2 of 2



Comment and Contribute

 


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

 

 


Sitemap | Contact Us

Rocket Fuel