Results 1 to 7 of 7

Thread: Tutorial: PHP 5 OOP

Threaded View

  1. #6

    Thread Starter
    VBA Nutter visualAd's Avatar
    Join Date
    Apr 2002
    Location
    Ickenham, UK
    Posts
    4,906

    Using Deleation and Type Hinting

    Throwing Exceptions

    You may haven noticed from the above code that we 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 an 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.

    We need to change our execute function to throw an exception, if the query fails. We will throw a custom exception object called, QueryException, which is passed the DBQuery object that caused the error.
    PHP Code:
        /**
         * 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 initialised, so executre 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;
        } 
    Using Inheritance to Throw Custom Exceptions

    In PHP we 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 our own customer exception, we can record extra information about the nature of the error if any, create an entry in a log, in fact, you can do anything you like. Our 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.
    • A new protected property has been added to the class called compiledQuery
    • The compile() function updates the query compiledQuery property with the query text.
    • A function to retrieve the compiled query text has been added.
    • A function to get the current DB object associated with the DBQuery object has also been added.

    PHP Code:
    class DBQuery
    {
        
    /**
         * Stored the compiled version of the query. After a calle 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("/(?<!\\\\)\:(\d+)([SIN])/e"'$this->compile_callback($params, \1, "\2")'$this->query);

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

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

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

    We can now implement the QueryException class. Note how we 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.
    PHP Code:
    /**
     * 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 mader 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 superclass 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}\n\n";
             
    $output .= "Query: {$this->QueryText}\n";
             
    $output .= "Error: {$this->ErrorMessage} ({$this->ErrorNumber})\n\n";            
                
             return 
    $output;
        }


    The code seen at the beginning of this section now will now work. Through the use of the adaptor pattern, polymorphism, delegation and with a little help from PHP's new object model, we have developed a robust database access API which can be applied to any database. The updated DBQuery object and the QueryException object code are attached to this post. 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, not in this thread.
    Attached Files Attached Files
    Last edited by visualAd; Apr 24th, 2006 at 12:14 AM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width