Saturday, 2 November 2013
PHP 101 (PART 12): BUGGING OUT – PART 2
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.
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
to raise any of the three error types reserved for users:
trigger_error(), which allows youto 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) {
// 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);
}
// trigger a warning
if (is_float($num)) {
trigger_error("Number $num is not an integer", E_USER_WARNING);
}
// negative
// trigger a fatal error
// trigger a fatal error
// test the function with different values
testNumber(100);
testNumber(100);
testNumber(5.6);
testNumber(-8);
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:
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)) {
// 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)) {
// negative
// trigger a fatal error
if ($num < 0) {
trigger_error("Number $num is negative", E_USER_ERROR);
// 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) {
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;
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;
// report error and die()
die("Fatal error on line $line of $file: $msg <br />");
break;
// notices
default:
// do nothing
break;
}
}
default:
// do nothing
break;
}
}
// test the function with different values
testNumber(100);
testNumber(5.6);
testNumber(-8);
testNumber(100);
testNumber(5.6);
testNumber(-8);
?>
Note that it is the responsibility of the custom handler to
event of user-generated fatal errors – PHP will not do this automatically.
die() in theevent 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.
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.
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
and exceptions generated by it are “caught” and resolved by a
Multiple
error type; this allows developers to trap different types of errors and execute
appropriate exception-handling.
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 differenterror 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
attempts to execute the code within the
processed without any exceptions being generated, control transfers to the lines
following the
while running the code within the
the block at that point and begins checking each
if there is a handler for the exception. If a handler is found, the code within the
appropriate
It is even possible to handle that fatal error in a nice way using exceptions; see
try-catch() block, it firstattempts to execute the code within the
try() block. If this code isprocessed without any exceptions being generated, control transfers to the lines
following the
try-catch() block. However, if an exception is generatedwhile running the code within the
try() block, PHP stops execution ofthe block at that point and begins checking each
catch() block to seeif 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
error code. When the exception is raised, this description and code will be made
available to the exception handler.
throw statement. Thethrow statement needs to be passed a descriptive message, and an optionalerror 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
// try this code
try {
$file = 'somefile.txt';
try {
$file = 'somefile.txt';
// open file
// read file contents
// print file contents
echo $data;
}
echo $data;
}
// catch errors if any
catch (Exception $e) {
catch (Exception $e) {
print 'Something bad just happened...';
}
}
?>
If the file doesn’t exist or is unreadable, the
generate an exception (basically, an instance of PHP’s built-in
throw statement willgenerate 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
can handle the exception type, the code within the
If the first
passes to the next one.
control passes to the first
catch() block. If the catch() blockcan handle the exception type, the code within the
catch() block is executed.If the first
catch() block cannot handle the generated exception, controlpasses 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
will catch all exceptions, regardless of type.
For the moment, all you need to know is that the generic
catch() block abovewill catch all exceptions, regardless of type.
Now, there’s one problem with the previous listing. Although the
block will trap the exception and print a message, it can’t display the descriptive
message sent by the
message, as well as a couple of other interesting pieces of information, it is necessary
to use some of the
revision of the previous script, which illustrates:
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 thismessage, 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 thisrevision of the previous script, which illustrates:
<?php
// PHP 5
// try this code
try {
$file = 'somefile.txt';
try {
$file = 'somefile.txt';
throw new Exception('Could not open file!', 12);
}
}
throw new Exception('Could not read file!', 9);
}
}
// print file contents
echo $data;
echo $data;
}
// catch errors if any
catch (Exception $e) {
print '<h2>Exception</h2>';
print 'Error message: ' . $e->getMessage() . '<br />';
// 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 '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
the
Exception object’sgetMessage(), getCode(), getFile(),getLine() and getTraceAsString() methods respectively insidethe
catch() block.Adding Some Class
You can handle different exceptions in different ways, by sub-classing the generic
following example is a simple illustration of this:
Exception object and using more than one catch() block. Thefollowing 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 {}
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);
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);
}
// 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);
}
}
if ($num > 1000 || $num < 100) {
throw new OutOfRangeException($num);
}
}
// try this code
try {
testNumber(-19);
}
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 (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) {
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 />';
}
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 'File and line: ' . $e->getFile() . '(' . $e->getLine() . ')<br />';
print 'Trace: ' . $e->getTraceAsString() . '<br />';
}
}
?>
In this case, I’ve created three new
object, one for each possible error. Next, I’ve set up
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
block will be invoked and a different error message will be printed.
Exception sub-classes from the baseobject, one for each possible error. Next, I’ve set up
catch() blocks foreach 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
the exception type, and because the generic
exceptions, the
specific first. This has been done in the example above, where the generic
catch() block that matchesthe exception type, and because the generic
Exception class matches allexceptions, the
catch() blocks must be arranged in the order of mostspecific 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:
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;
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;
// 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;
}
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 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);
if (!is_readable($this->passwdFile)) {
throw new FileException("Unable to read password file: ". $this->passwdFile);
}
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
// 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
}
throw new AuthException("No such user");
}
}
}
// end class definition
}
// subclass exceptions
class FileException extends Exception {};
class FileException extends Exception {};
class AuthException extends Exception {};
// try the code
try {
// create instance
$ua = new userAuth("joe", "secret");
try {
// create instance
$ua = new userAuth("joe", "secret");
// set password file
$ua->setPasswdFile("password.txt");
$ua->setPasswdFile("password.txt");
// perform authentication
$ua->authenticateUser();
$ua->authenticateUser();
}
// catch authentication failures, if any
// 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();
// 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";
}
// header ('Location: login_fail.php');
}
catch (Exception $e) {
print "An unknown error occurred";
}
?>
Here, depending on the type of error, either a
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.
FileException() or anAuthException() will be generated – and handled by the correspondingcatch() block. Notice how easy the exception handling framework is to readand 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!












0 Comments:
Post a Comment