Section 7.3. PEAR Errors


7.3. PEAR Errors

PEAR has its own error-reporting mechanism based around the principle of errors as types, and the ability to pass around errors as values. Many extras were built around this principle, to the point where PEAR errors almost function like a poor man's (in this case, PHP 4 users') exception.

Where PHP's built-in error mechanism typically displays a message and a function returns false, a function returning a PEAR error gives an object back that is an instance of PEAR_Error or a subclass:

 <?php require_once 'DB.php'; $dbh = DB::connect('mysql://test@localhost/test'); if (PEAR::isError($dbh)) {     die("DB::connect failed (" . $dbh->getMessage() . ")\n"); } print "DB::connect ok!\n"; ?> 

In this introductory example, we try connecting to a MySQL database through PEAR DB. If the connection fails, DB::connect returns a PEAR error. The PEAR::isError() static method returns a boolean that tells whether a value is a PEAR error. If the return value from DB::connect is a PEAR error, the connection attempt has failed. In this case, we call getMethod() in the error object to retrieve the error message, print it, and abort.

This is a simple example of how PEAR's error handling works. There are many ways of customizing it that we will look at later. First, we examine the different ways of raising and catching PEAR errors, and get an overview of the PEAR_Error class.

7.3.0.1. Catching Errors

Unless an error handler that aborts execution is configured, the return value of a function failing with a PEAR error will be the error object. Depending on the error-handling setup, some kind of action may have been taken already, but there is no provided way of telling.

One of the code design implications of this is that PEAR error-handling defaults should always be set by the driving script, or the script that PHP started executing. If some included library starts setting up error-handling defaults or global resources such as INI entries, trouble awaits.

7.3.0.2. PEAR::isError()

bool PEAR::isError(mixed candidate)

This method returns TRue or false depending on whether candidate is a PEAR error. If candidate is an object that is an instance of PEAR_Error or a subclass, PEAR::isError() returns TRue.

7.3.0.3. Raising Errors

In PEAR terminology, errors are "raised," although the easiest way of raising a PEAR error is returning the return value from a method called throwError. This is simply because tHRowError is a simplified version of the original raiseError method. PEAR uses the term raising to avoid confusion with PHP exceptions, which are thrown.

The relative cost of raising a PEAR error compared to triggering a PHP error is high, because it involves object creation and several function calls. This means that you should use PEAR errors with carekeep them for failures that should not normally happen. Prefer using a simple Boolean return value for the normal cases. This same advice is given in regards to using exceptions in PHP, as well as C++, Java, or other languages.

When you use PEAR packages in your code, you need to deal with errors raised by the package. You can do this in one of two ways: whether you are in an object context, and whether your current class inherits the PEAR class.

If your code does not run in an object context, such as from the global scope, inside a regular function or in a static method you need to call the PEAR::throwError() static method:

 <?php require_once 'PEAR.php'; if (PEAR::isError($e = lucky())) {     die($e->getMessage() . "\n"); } print "You were lucky, this time.\n"; function lucky() {     if (rand(0, 1) == 0) {         return PEAR::throwError('tough luck!');     } } ?> 

When errors are raised with static method calls, the defaults set with PEAR::setErrorHandling() are applied. The other way of raising errors is when your class has inherited PEAR, and your code is executed in an object context:

 <?php require_once 'PEAR.php'; class Luck extends PEAR {     function testLuck() {         if (rand(0, 1) == 0) {             return $this->throwError('tough luck!');         }         return "lucky!";     } } $luck = new Luck; $test = $luck->testLuck(); if (PEAR::isError($test)) {     die($test->getMessage() . "\n"); } print "$test\n"; ?> 

When throwError() is called in an object context, defaults set in that object with $object->setErrorHandling() are applied first. If no defaults are set for the object, the global defaults apply, as with errors raised statically (like in the previous example).

7.3.0.4. PEAR::throwError()

([object PEAR::throwError([string message], [int code], [string userinfo])

This method raises a PEAR error, applying default error-handling settings. Which defaults are actually applied depends on how the method is called. If throwError() is called statically, such as PEAR::throwError(), the global defaults are applied. The global defaults are always set with PEAR::setErrorHandling() and called statically. When throwError() is called from an object context, such as $this->throwError(), the error-handling defaults of $this are applied first. If the defaults for $this are undefined, the global defaults are applied instead.

If you are not intimate with the semantics of $this in PHP, you may be in for some surprises when using PEAR error defaults. If you call a method statically from within an object (where $this has a value), the value of $this will actually be defined inside the statically called method as well. This means that if you call PEAR::throwError() from inside an object, $this will be defined inside PEAR::throwError() and refer to the object from which you called PEAR::throwError(). In most cases, this has no effect, but if you start using PEAR's error-handling mechanism to its fullest, you should be aware of this so you are not surprised by the wrong error-handling defaults being applied.

7.3.0.5. PEAR::raiseError()

object PEAR::raiseError([string message], [int code], [ int mode], [mixed options], [string userinfo], [string error_class], [bool skipmsg])

This method is equivalent to throwError() but with more parameters. Normally, you would not need all these extra options, but they may come in handy if you are making your own error system based on PEAR errors. message, code, and userinfo are equivalent to the same throwError() parameters. mode and options are equivalent to the same PEAR_Error constructor parameters (see the following PEAR_Error description). The two remaining parameters are error_class and skipmsg:

 string $error_class (default "PEAR_Error") 

This class will be used for the error object. If you change this to something other than PEAR_Error, make sure that the class you are giving here extends PEAR_Error, or PEAR::isError() will not give correct results.

 bool $skipmsg (default false) 

This rather obscure parameter tells the raiseError() implementation to skip the message parameter completely, and simply pretend there is no such parameter. If skipmsg is TRue, the constructor of the error object is called with one less parameter, without message as the first parameter. This may be useful for extended error mechanisms that want to base everything on error codes.

7.3.1. The PEAR_Error Class

The PEAR-Error class is PEAR's basic error-reporting class. You may extend and specialize it for your own purposes if you need, PEAR:isError() will still recognize it.

7.3.1.1 PEAR_Error constructor

void PEAR_Error([string message], [int code], [int mode], [mixed options], [string userinfo])

All PEAR_Error's constructor parameters are optional and default to the null value, except message, which defaults to unknown error. However, normally, you do not create PEAR errors with the new statement, but with a factory method such as PEAR::throwError() or PEAR::raiseError().

 string $message (default "unknown error") 

This is the error message that will be displayed. This parameter is optional, but you should always specify either $message or $code.

 int $code (default 1) 

The error code is a simple integer value representing the nature of the error. Some PEAR error-based mechanisms (such as the one in PEAR DB) use this parameter as the primary way of describing the nature of errors, and leave the message for a plain code to text mapping. Error codes are also good in conjunction with localized error messages, because they provide a language-neutral description of errors.

It is good practice to always specify an error code, if nothing else to allow for cleaner, more graceful error handling.

 int $mode (default PEAR_ERROR_RETURN) 

This is the error mode that will be applied to this error. It may have one of the following values:

  • PEAR_ERROR_RETURN

  • PEAR_ERROR_PRINT

  • PEAR_ERROR_DIE

  • PEAR_ERROR_TRIGGER

  • PEAR_ERROR_CALLBACK

The meaning of the different error modes is discussed in the following "Handling PEAR Errors" section.

 mixed $options 

This parameter is used differently depending on what error mode was specified:

  • For PEAR_ERROR_PRINT and PEAR_ERROR_DIE, the $options parameter contains a printf format string that is used when printing the error message.

  • For PEAR_ERROR_TRIGGER, it contains the PHP error level used when triggering the error. The default error level is E_USER_NOTICE, but it may also be set to E_USER_WARNING or E_USER_ERROR.

  • Finally, if $mode is PEAR_ERROR_CALLBACK, the $options parameter is the callable that will be given the error object as its only parameter. A callable is either a string with a function name, an array of class name and method name (for static method calls), or an array with an object handle and method name (object method calls).

 string $userinfo 

This variable holds extra information about the error. An example of content would be the SQL query for failing database calls, or the filename for failing file operations. This member variable containing user info may be appended to with the addUserInfo() method.

7.3.1.2 PEAR_Error::addUserInfo()
 void addUserInfo(string info) 

This variable appends info to the error's user info. It uses the character sequence " ** " to separate different user info entries.

7.3.1.3 PEAR_Error::getBacktrace([frame])

array getBacktrace([int frame])

This method returns a function call backtrace as returned by debug_backtrace() from the PEAR_Error constructor. Because PEAR_Error saves the backtrace before raising the error, using exceptions through PEAR errors will preserves the backtrace.

The optional integer argument is used to select a single frame from the backtrace, with index 0 being the innermost frame (frame 0 will always be in the PEAR_Error class).

7.3.1.4 PEAR_Error::getCallback()
 mixed getCallback() 

This method returns the "callable" used in the PEAR_ERROR_CALLBACK error mode.

7.3.1.5 PEAR_Error::getCode()
 int getCode() 

This method returns the error code.

7.3.1.6 PEAR_Error::getMessage()

string getMessage()

This method returns the error message.

7.3.1.7 PEAR_Error::getMode()
 int getMode() 

This method returns the error mode (PEAR_ERROR_RETURN and so on).

7.3.1.8 PEAR_Error::getType() string getType()

This method returns the type of PEAR error, which is the lowercased class name of the error class. In most cases, the type will be pear_error (in lowercase), but it varies for packages that implement their own error-handling classes inheriting PEAR_Error.

7.3.1.9 PEAR_Error::getUserInfo()
 string getUserInfo() 

This method returns the entire user info string. Different entries are separated with the string " ** " (space, two asterisks, space).

7.3.2. Handling PEAR Errors

The default behavior for PEAR errors is to do nothing but return the object. However, it is possible to set an error mode that will be used for all consequent errors raised. The error mode is checked when the PEAR_Error object is created, and is expressed by a constant:

 <?php require_once 'DB.php'; PEAR::setErrorHandling(PEAR_ERROR_DIE, "Aborting: %s\n"); $dbh = DB::connect('mysql://test@localhost/test'); print "DB::connect ok!\n"; ?> 

This previous example is simplified here by using a global default error handler that applies to every PEAR error that has no other error mode configured. In this case, we use PEAR_ERROR_DIE, which prints the error message using the parameter as printf format string, and then die. The advantage of this approach is that you can code without checking errors for everything. It is not very graceful, but as you will see later in the chapter, you may also apply temporary error modes during operations that need more graceful handling.

7.3.2.1 PEAR::setErrorHandling()

void PEAR::setErrorHandling(int mode, [mixed options])

This method sets up default error-handling parameters, globally or for individual objects. Called statically, it sets up global error handling defaults:

 PEAR::setErrorHandling(PEAR_ERROR_TRIGGER); 

Here, we set the global default error handling to PEAR_ERROR_TRIGGER, which makes all PEAR errors trigger PHP errors.

Called when part of an object, this method sets up error-handling defaults for that object only:

 $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, 'my_error_handler'); 

In this example, we set the defaults so every error object raised from within the $dbh object is passed as a parameter to my_error_handler().

7.3.3. PEAR Error Modes

7.3.3.1 PEAR_ERROR_RETURN

This default error mode does nothing beyond creating the error object and returning it.

7.3.3.2 PEAR_ERROR_PRINT

In this mode, the error object automatically prints the error message to PHP's output stream. You may specify a printf format string as a parameter to this error mode; we will look at that later in this chapter.

7.3.3.3 PEAR_ERROR_DIE

This mode does the same thing as PEAR_ERROR_PRINT, except it exits after displaying the error message. The printf format string is still applied.

7.3.3.4 PEAR_ERROR_TRIGGER

The trigger mode passes the error message on to PHP's built-in trigger_error() function. This mode also takes an optional parameter which is the PHP error level used in the trigger_error() call (one of E_USER_NOTICE, E_USER_WARNING and E_USER_ERROR). Wrapping PHP errors inside PEAR errors may be useful, for example, if you want to exploit the flexibility of PEAR errors but all the different built-in logging capabilities of PHP's own error handling.

7.3.3.5 PEAR_ERROR_CALLBACK

Finally, if none of the preceding error modes suits your needs, you may set up an error-handling function and do the rest yourself.

7.3.4. Graceful Handling

7.3.4.1 PEAR::pushErrorHandling()

bool PEAR::pushErrorHandling(int mode, [mixed options])

This method pushes another error-handling mode on top of the default handler stack. This error mode will be used until popErrorHandling() is called.

You may call this method statically or in an object context. As with other methods that have this duality, global defaults are used when called statically, and the object defaults when in an object context.

Here is an extended version of the first example. After connecting, we insert some data into a table, and handle duplicate keys gracefully:

 <?php require_once 'PEAR.php'; require_once 'DB.php'; PEAR::setErrorHandling(PEAR_ERROR_DIE, "Aborting: %s\n"); $dbh = DB::connect('mysql://test@localhost/test'); // temporarily set the global default error handler PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $res = $dbh->query("INSERT INTO mytable VALUES(1, 2, 3)"); // PEAR_ERROR_DIE is once again the active error handler PEAR::popErrorHandling(); if (PEAR::isError($res)) {     // duplicate keys will return this error code in PEAR DB:     if ($res->getCode() == DB_ERROR_ALREADY_EXISTS) {         print "Duplicate record!\n";     } else {         PEAR::throwError($res);     } } ?> 

First, we set up a default error handler that prints the error message and exits. After successfully connecting to the database (the default error handler will make the script exit if the connection fails), we push PEAR_ERROR_RETURN as the global default error mode while executing a query that may return an error. Once the query is done, we pop away the temporary error mode. If the query returned an error, we check the error code to see if it is a situation we know how to handle. If it was not, we re-throw the error, which causes the original global defaults (PEAR_ERROR_DIE) to apply.

7.3.4.2 PEAR::popErrorHandling()
 bool PEAR::popErrorHandling() 

This is the complimentary method to PEAR::pushErrorHandling() and will pop (remove) the topmost mode from the error handling stack. It may be called statically or in an object context, as with pushErrorHandling().

7.3.4.3 PEAR::expectError()
 int expectError(mixed expect) 

This method is a more specific approach to the same problem that pushErrorHandling() tries to solve: making an exception (in the traditional sense of the word) for errors we want to handle differently. The expectError() approach is to look for one or more specified error codes or error messages, and force the error mode to PEAR_ERROR_RETURN for matching errors, thus disabling any handlers.

If the expect parameter is an integer, it is compared to the error code of the raised error. If they match, any specified error handler is disabled, and the error object is silently returned.

If expect is a string, the same thing is done with the error message, and as a special case the string "*" matches every error message. Thus, expectError('*') has the same effect as pushErrorHandling(PEAR_ERROR_RETURN).

Finally, if expect is an array, the previous rules are applied to each element, and if one matches, the error object is just silently returned.

The return value is the new depth of the object's expect stack (or the global expect stack if called statically).

Let's repeat the last example using expectError() instead of pushError Handling():

 <?php require_once 'PEAR.php'; require_once 'DB.php'; PEAR::setErrorHandling(PEAR_ERROR_DIE, "Aborting: %s\n"); $dbh = DB::connect('mysql://test@localhost/test'); // temporarily disable the default handler for this error code: $dbh->expectError(DB_ERROR_ALREADY_EXISTS); $res = $dbh->query("INSERT INTO mytable VALUES(1, 2, 3)"); // back to PEAR_ERROR_DIE again: $dbh->popExpect(); if (PEAR::isError($res) && $res->getCode() == DB_ERROR_ALREADY_EXISTS) {     print "Duplicate record!\n"; } ?> 

In this example, we use the per-object default error handling in the $dbh object instead of the global default handler to implement our graceful duplicate handling. The main difference from the pushErrorHandling() approach is that we don't have to re-throw/raise the error because our "duplicate handling code" is called only if a duplicate error occurred, and not if any error occurred as would have been the case with pushErrorHandling().

7.3.4.4 PEAR::popExpect()
 array popExpect() 

This method compliments expectError(), and removes the topmost element in the expect stack. As with the other error-handling methods, it applies to object or global defaults depending on whether it is called statically or in an object context.

The return value is an array with the expected error codes/messages that were popped off the expect stack.

7.3.4.5 PEAR::delExpect()
 bool delExpect(mixed error_code) 

This method removes error_code from every level in the expect stack, returning true if anything was removed.



    PHP 5 Power Programming
    PHP 5 Power Programming
    ISBN: 013147149X
    EAN: 2147483647
    Year: 2003
    Pages: 240

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net