PHP 101 (PART 12): BUGGING OUT – PART 2

November 30, -0001

Tutorials

PHP 101 (PART 12): BUGGING OUT – PART 1

Pulling the Trigger
Catching Up



Pulling the Trigger

So far we’ve been talking about handling errors generated by PHP itself, but why stop
there? PHP allows you to use its built-in error handling system to raise your own custom
errors as well.

This is accomplished via a function named trigger_error(), which allows you
to raise any of the three error types reserved for users: E_USER_NOTICE,

E_USER_WARNING and E_USER_ERROR. When these errors are triggered,
PHP’s built-in handler will automatically wake up to handle them.


<?php

// function to test a number

// generates E_USER_WARNING if number is a float

// generates E_USER_ERROR is number is negative

function testNumber($num) {

    // float

    // trigger a warning

    
if (is_float($num)) {

        
trigger_error("Number $num is not an integer", E_USER_WARNING);

    }

    // negative

    // trigger a fatal error

    if ($num < 0) {

        
trigger_error("Number $num is negative", E_USER_ERROR);

    }

}

// test the function with different values

testNumber(100);

testNumber(5.6);

testNumber(-8);

?>

If you’d like to have a custom error handler to handle your custom errors… well, you’re
just hard to please, aren’t you? Take a look at this next example, which rewrites the
previous script to use a user-defined error handler:


<?php

// function to test a number

// generates E_USER_WARNING if number is a float

// generates E_USER_ERROR is number is negative

function testNumber($num) {

    
// float

    // trigger a warning

    
if (is_float($num)) {

        trigger_error("Number $num is not an integer", E_USER_WARNING);

    }

    // negative

    // trigger a fatal error

    
if ($num < 0) {

        
trigger_error("Number $num is negative", E_USER_ERROR);

    }

}

// custom error handler

function myErrorHandler($type, $msg, $file, $line, $context) {

    switch ($type) {

        // warnings

        
case E_USER_WARNING:

            
// report error

            
print "Non-fatal error on line $line of $file: $msg <br />";

            break;

        // fatals

        case E_USER_ERROR:

            
// report error and die()

            
die("Fatal error on line $line of $file: $msg <br />");

            break;

        // notices

        
default:

            
// do nothing

            
break;

    }

}

// set the name of the custom handler

set_error_handler('myErrorHandler');

// test the function with different values

testNumber(100);

testNumber(5.6);

testNumber(-8);

?>

Note that it is the responsibility of the custom handler to die() in the
event of user-generated fatal errors – PHP will not do this automatically.

You can use the same method to deal with exceptions too. Scroll on down, and
let me show you how.


Catching Up

If you’re using PHP 5, you also have an alternative to the techniques discussed
so far in the new Exception model (exception is Geek for error). Exceptions are new
to PHP (although they’ve been in languages like Java and Python for ages) and they’re
stirring up a great deal of excitement.

In the exception-based approach, program code is wrapped in a try() block,
and exceptions generated by it are “caught” and resolved by a catch() block.
Multiple catch() blocks are possible, each one dealing with a different
error type; this allows developers to trap different types of errors and execute
appropriate exception-handling.

Here’s what a typical try-catch() block looks like:


try {

    execute this block

}

catch (exception type 1) {

    execute this block to resolve exception type 1

}

catch (exception type 2) {

    execute this block to resolve exception type 2

}

... and so on ...

When PHP encounters code wrapped within a try-catch() block, it first
attempts to execute the code within the try() block. If this code is
processed without any exceptions being generated, control transfers to the lines
following the try-catch() block. However, if an exception is generated
while running the code within the try() block, PHP stops execution of
the block at that point and begins checking each catch() block to see
if there is a handler for the exception. If a handler is found, the code within the
appropriate catch() block is executed; if not, a fatal error is generated.
It is even possible to handle that fatal error in a nice way using exceptions; see

http://www.php.net/set-exception-handler for more on this.

The exceptions themselves are generated via PHP’s throw statement. The
throw statement needs to be passed a descriptive message, and an optional
error code. When the exception is raised, this description and code will be made
available to the exception handler.

Wanna see how this works? Here’s an example:


<?php

// PHP 5

error_reporting(0);

// try this code

try {

    
$file = 'somefile.txt';

    // open file

    if (!$fh = fopen($file, 'r')) {

        throw new
Exception('Could not open file!');

    }

    // read file contents

    if (!$data = fread($fh, filesize($file))) {

        throw new
Exception('Could not read file!');

    }

    // close file

    
fclose($fh);

    // print file contents

    
echo $data;

}

// catch errors if any

catch (Exception $e) {

    print 'Something bad just happened...';

}

?>

If the file doesn’t exist or is unreadable, the throw statement will
generate an exception (basically, an instance of PHP’s built-in Exception

object) and pass it a message describing the error. When such an exception is generated,
control passes to the first catch() block. If the catch() block
can handle the exception type, the code within the catch() block is executed.
If the first catch() block cannot handle the generated exception, control
passes to the next one.

Don’t worry too much about “exception types” at this point – all will be explained shortly.
For the moment, all you need to know is that the generic catch() block above
will catch all exceptions, regardless of type.

Now, there’s one problem with the previous listing. Although the catch()
block will trap the exception and print a message, it can’t display the descriptive
message sent by the throw statement with the exception. To access this
message, as well as a couple of other interesting pieces of information, it is necessary
to use some of the Exception object’s built-in methods. Take a look at this
revision of the previous script, which illustrates:


<?php

// PHP 5

error_reporting(0);

// try this code

try {

    
$file = 'somefile.txt';

    // open file

    
if (!$fh = fopen($file, 'r')) {

        throw new Exception('Could not open file!', 12);

    }

    // read file contents

    
if (!$data = fread($fh, filesize($file))) {

        throw new Exception('Could not read file!', 9);

    }

    // close file

    
fclose($fh);

    // print file contents

    
echo $data;

}

// catch errors if any

catch (Exception $e) {

    print
'<h2>Exception</h2>';

    print
'Error message: ' . $e->getMessage() . '<br />';

    print 'Error code: ' . $e->getCode() . '<br />';

    print
'File and line: ' . $e->getFile() . '(' . $e->getLine() . ')<br />';

    print 'Trace: ' . $e->getTraceAsString() . '<br />';

}

?>

When you run this script, you’ll see that the message generated by the exception handler contains:

  • the descriptive data sent by throw,
  • an error code (also sent by throw),
  • the file name and line number where the exception occurred, and
  • a stack trace indicating the exception’s progress through the class hierarchy, if there is one.

This data is generated by calling the Exception object’s
getMessage(), getCode(), getFile(),
getLine() and getTraceAsString() methods respectively inside
the catch() block.


Adding Some Class

You can handle different exceptions in different ways, by sub-classing the generic
Exception object and using more than one catch() block. The
following example is a simple illustration of this:


<?php

// PHP 5

// sub-class the Exception class

class NegativeNumException extends Exception {}

class
OutOfRangeException extends Exception {}

class
FloatException extends Exception {}

// function to test a number

function testNumber($num) {

    
// float

    // trigger an exception

    
if (is_float($num)) {

        throw new
FloatException($num);

    }

    // negative

    // trigger an exception

    
if ($num < 0) {

        throw new
NegativeNumException($num);

    }

    // out of range

    // trigger an exception

    if ($num > 1000 || $num < 100) {

        throw new
OutOfRangeException($num);

    }

}

// try this code

try {

    
testNumber(-19);

}

// catch errors, if any

catch (NegativeNumException $e) {

    print
'A negative number was provided ('.$e->getMessage().'). Please provide a positive integer between 100 and 1000.<br />';

}

catch (OutOfRangeException $e) {

    print
'The number provided is out of range ('.$e->getMessage().'). Please provide a positive integer between 100 and 1000.<br />';

}

catch (
FloatException $e) {

    print 'The number provided is not an integer ('.$e->getMessage().'). Please provide a positive integer between 100 and 1000.<br />';

}

catch (
Exception $e) {

    print
'Error message: ' . $e->getMessage() . '<br />';

    print 'Error code: ' . $e->getCode() . '<br />';

    print
'File and line: ' . $e->getFile() . '(' . $e->getLine() . ')<br />';

    print 'Trace: ' . $e->getTraceAsString() . '<br />';

}

?>

In this case, I’ve created three new Exception sub-classes from the base
object, one for each possible error. Next, I’ve set up catch() blocks for
each exception type, and written exception-handling code that is specific to each type.
Depending on which exception occurs (you can generate different ones by sending the
testNumber() function different values), the appropriate catch()
block will be invoked and a different error message will be printed.

Note that because PHP will always use the first catch() block that matches
the exception type, and because the generic Exception class matches all
exceptions, the catch() blocks must be arranged in the order of most
specific first. This has been done in the example above, where the generic
catch() block appears last on the list.

Here’s another example, this one illustrating a more useful application – using the
exception model in a user authentication class to provide easy-to-understand error
handling. Take a look:


<?php

// PHP 5

// class definition

class userAuth {

    
// define properties

    
private $username;

    private
$passwd;

    private
$passwdFile;

    // constructor

    // must be passed username and non-encrypted password

    
public function __construct($username, $password) {

            
$this->username = $username;

            
$this->passwd = $password;

    }

    // set .htaccess-style file to check for passwords

    
public function setPasswdFile($file) {

        
$this->passwdFile = $file;

    }

    // perform password verification

    public function authenticateUser() {

        
// check that the file exists

        
if (!file_exists($this->passwdFile)) {

            throw new
FileException("Password file cannot be found: " . $this->passwdFile);

        }

        // check that the file is readable

        
if (!is_readable($this->passwdFile)) {

            throw new
FileException("Unable to read password file: ". $this->passwdFile);

        }

        // read file

        
$data = file($this->passwdFile);

    

        
// iterate through file

        
foreach ($data as $line) {

            $arr = explode(":", $line);

            
// if username matches, test password

            
if ($arr[0] == $this->username) {

                // get salt and crypt(), assuming encryption

                
$salt = substr($arr[1], 0, 2);

                // if match, user/pass combination is correct

                
if ($arr[1] == crypt($this->passwd, $salt)) {

                    echo "User was authenticated";

                    
// do some other stuff

                
}

                
// otherwise return exception

                
else {

                    throw new
AuthException("Incorrect password");

                    break;

                }

            }

            else {

                
// could not find a username match

                // return exception

                throw new AuthException("No such user");

            }

        }

    }

// end class definition

}

// subclass exceptions

class FileException extends Exception {};

class AuthException extends Exception {};

// try the code

try {

    
// create instance

    
$ua = new userAuth("joe", "secret");

    // set password file

    
$ua->setPasswdFile("password.txt");

    // perform authentication

    
$ua->authenticateUser();

}

// catch authentication failures, if any

catch (FileException $e) {

    
// print file errors

    
print "A file error occurred. ".$e->getMessage();

}

catch (
AuthException $e) {

    
// an authentication error occurred

    
print "An authentication error occurred. ".$e->getMessage();

    // more normally, redirect to new page on auth errors, e.g.

    // header ('Location: login_fail.php');

}

catch (
Exception $e) {

    print
"An unknown error occurred";

}

?>




Here, depending on the type of error, either a FileException() or an
AuthException() will be generated – and handled by the corresponding
catch() block. Notice how easy the exception handling framework is to read
and extend. It’s precisely this ease of use and extensibility that helps the new PHP 5
model score over the earlier, more primitive techniques of handling application errors.

Well, that’s about it for the moment. Come back soon, for more PHP 101!


PHP 101 (PART 12): BUGGING OUT – PART 1

Copyright Melonfire, 2004 (http://www.melonfire.com). All rights reserved.

2 Responses to “PHP 101 (PART 12): BUGGING OUT – PART 2”

  1. _____anonymous_____ Says:

    It’s really a great article. I am sure you spent hours writing this. Very well done!

  2. _____anonymous_____ Says:

    Very informative and well written article. Thanks!