There was a certain section of my code that I needed to completely ignore if it failed. So I trapped it all in a try/catch and utilized the set_error_message, which in essence is like a pseudo-goto clutch. The reason for it all was if it failed during the code, I didn't want it to execute the rest of the respective section but continue outside of it. In the hopes it might help someone else out, here's what I did.
I'm using PHP 5.2.1
<?php
....
try {
set_error_handler(create_function('', "throw new Exception(); return true;"));
...code...
} catch(Exception $e) {
}
restore_error_handler();
?>
Table of Contents
PHP 5 has an exception model similar to that of other programming languages. An exception can be thrown, and caught ("catched") within PHP. Code may be surrounded in a try block, to facilitate the catching of potential exceptions. Each try must have at least one corresponding catch block. Multiple catch blocks can be used to catch different classes of exeptions. Normal execution (when no exception is thrown within the try block, or when a catch matching the thrown exception's class is not present) will continue after that last catch block defined in sequence. Exceptions can be thrown (or re-thrown) within a catch block.
When an exception is thrown, code following the statement will not be executed, and PHP will attempt to find the first matching catch block. If an exception is not caught, a PHP Fatal Error will be issued with an "Uncaught Exception ..." message, unless a handler has been defined with set_exception_handler().
Example 20.1. Throwing an Exception
<?php
try {
$error = 'Always throw this error';
throw new Exception($error);
// Code following an exception is not executed.
echo 'Never executed';
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
// Continue execution
echo 'Hello World';
?>
A User defined Exception class can be defined by extending the built-in Exception class. The members and properties below, show what is accessible within the child class that derives from the built-in Exception class.
Example 20.2. The Built in Exception class
<?php
class Exception
{
protected $message = 'Unknown exception'; // exception message
protected $code = 0; // user defined exception code
protected $file; // source filename of exception
protected $line; // source line of exception
function __construct($message = null, $code = 0);
final function getMessage(); // message of exception
final function getCode(); // code of exception
final function getFile(); // source filename
final function getLine(); // source line
final function getTrace(); // an array of the backtrace()
final function getTraceAsString(); // formated string of trace
/* Overrideable */
function __toString(); // formated string for display
}
?>
If a class extends the built-in Exception class and re-defines the constructor, it is highly recomended that it also call parent::__construct() to ensure all available data has been properly assigned. The __toString() method can be overriden to provide a custom output when the object is presented as a string.
Example 20.3. Extending the Exception class
<?php
/**
* Define a custom exception class
*/
class MyException extends Exception
{
// Redefine the exception so message isn't optional
public function __construct($message, $code = 0) {
// some code
// make sure everything is assigned properly
parent::__construct($message, $code);
}
// custom string representation of object
public function __toString() {
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
}
public function customFunction() {
echo "A Custom function for this type of exception\n";
}
}
/**
* Create a class to test the exception
*/
class TestException
{
public $var;
const THROW_NONE = 0;
const THROW_CUSTOM = 1;
const THROW_DEFAULT = 2;
function __construct($avalue = self::THROW_NONE) {
switch ($avalue) {
case self::THROW_CUSTOM:
// throw custom exception
throw new MyException('1 is an invalid parameter', 5);
break;
case self::THROW_DEFAULT:
// throw default one.
throw new Exception('2 isnt allowed as a parameter', 6);
break;
default:
// No exception, object will be created.
$this->var = $avalue;
break;
}
}
}
// Example 1
try {
$o = new TestException(TestException::THROW_CUSTOM);
} catch (MyException $e) { // Will be caught
echo "Caught my exception\n", $e;
$e->customFunction();
} catch (Exception $e) { // Skipped
echo "Caught Default Exception\n", $e;
}
// Continue execution
var_dump($o);
echo "\n\n";
// Example 2
try {
$o = new TestException(TestException::THROW_DEFAULT);
} catch (MyException $e) { // Doesn't match this type
echo "Caught my exception\n", $e;
$e->customFunction();
} catch (Exception $e) { // Will be caught
echo "Caught Default Exception\n", $e;
}
// Continue execution
var_dump($o);
echo "\n\n";
// Example 3
try {
$o = new TestException(TestException::THROW_CUSTOM);
} catch (Exception $e) { // Will be caught
echo "Default Exception caught\n", $e;
}
// Continue execution
var_dump($o);
echo "\n\n";
// Example 4
try {
$o = new TestException();
} catch (Exception $e) { // Skipped, no exception
echo "Default Exception caught\n", $e;
}
// Continue execution
var_dump($o);
echo "\n\n";
?>
Exceptions
30-Apr-2007 11:37
24-Jan-2007 05:52
Further to dexen at google dot me dot up with "use destructors to perform a cleanup in case of exception". The fact that PHP5 has destructors, exception handling, and predictable garbage collection (if there's a single reference in scope and the scope is left then the destructor is called immediately) allows for the use of the RAII idiom.
http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization and my own http://www.hackcraft.net/RAII/ describe this.
16-Jan-2007 05:43
If you need a Java-like exception with a sort of "Throwable" cause to report the whole stackTrace, you can use this class I wrote and I release under the LGPL GNU Lesser General Public Liscence.
It looks more or less like the original PHP "Exception" but you might want to change the formatting of the returned string in the two last functions. Enjoy :)
<?php
/* Author : Romain Boisnard */
/* Liscenced under the LGPL GNU Lesser General Public Liscence, report the actual liscence for details.
/* LinkedException */
// Java-like exception with a cause
class LinkedException extends Exception {
private $cause;
function __construct($_message = null, $_code = 0, Exception $_cause = null) {
parent::__construct($_message, $_code);
$this->cause = $_cause;
}
public function getCause() {
return $this->cause;
}
public function getStackTrace() {
if ($this->cause !== null) {
$arr = array();
$trace = $this->getTrace();
array_push($arr, $trace[0]);
unset($trace);
if (get_class($this->cause) == "LinkedException") {
foreach ($this->cause->getStackTrace() as $key => $trace) {
array_push($arr, $trace);
}
}
else {
foreach ($this->cause->getTrace() as $key => $trace) {
array_push($arr, $trace);
}
}
return $arr;
}
else {
return $this->getTrace();
}
}
public function showStackTrace() {
$htmldoc = "<p style=\"font-family: monospace; border: solid 1px #000000\"><span style=\"font-weight: bold; color: #000000;\">An exception was thrown :<br/></span>";
$htmldoc.= "Exception code : $this->code<br/>";
$htmldoc.= "Exception message : $this->message<br/>";
$htmldoc.= "<span style=\"color: #0000FF;\">";
$i = 0;
foreach ($this->getStackTrace() as $key => $trace) {
$htmldoc.= $this->showTrace($trace, $i);
$i++;
}
$htmldoc.= "#$i {main}<br/>";
unset($i);
$htmldoc.= "</span></p>";
return $htmldoc;
}
private function showTrace($_trace, $_i) {
$htmldoc = "#$_i ";
if (array_key_exists("file",$_trace)) {
$htmldoc.= $_trace["file"];
}
if (array_key_exists("line",$_trace)) {
$htmldoc.= "(".$_trace["line"]."): ";
}
if (array_key_exists("class",$_trace) && array_key_exists("type",$_trace)) {
$htmldoc.= $_trace["class"].$_trace["type"];
}
if (array_key_exists("function",$_trace)) {
$htmldoc.= $_trace["function"]."(";
if (array_key_exists("args",$_trace)) {
if (count($_trace["args"]) > 0) {
$args = $_trace["args"];
$type = gettype($args[0]);
$value = $args[0];
unset($args);
if ($type == "boolean") {
if ($value) {
$htmldoc.= "true";
}
else {
$htmldoc.= "false";
}
}
elseif ($type == "integer" || $type == "double") {
if (settype($value, "string")) {
if (strlen($value) <= 20) {
$htmldoc.= $value;
}
else {
$htmldoc.= substr($value,0,17)."...";
}
}
else {
if ($type == "integer" ) {
$htmldoc.= "? integer ?";
}
else {
$htmldoc.= "? double or float ?";
}
}
}
elseif ($type == "string") {
if (strlen($value) <= 18) {
$htmldoc.= "'$value'";
}
else {
$htmldoc.= "'".substr($value,0,15)."...'";
}
}
elseif ($type == "array") {
$htmldoc.= "Array";
}
elseif ($type == "object") {
$htmldoc.= "Object";
}
elseif ($type == "resource") {
$htmldoc.= "Resource";
}
elseif ($type == "NULL") {
$htmldoc.= "null";
}
elseif ($type == "unknown type") {
$htmldoc.= "? unknown type ?";
}
unset($type);
unset($value);
}
if (count($_trace["args"]) > 1) {
$htmldoc.= ",...";
}
}
$htmldoc.= ")<br/>";
}
return $htmldoc;
}
}
?>
18-Sep-2006 01:45
Summary:
* use destructors to perform a cleanup in case of exception.
PHP calls method __destruct() on instance of class when variable storing the instance goes out-of-scope (or gets unset). This works for function leave by Exception, aside of plain return. (same as for C++, AFAIK)
aFunction() {
$i = new LockerClass();
throw new MinorErrorEx('Warn user & perform some other activity');
// $i->__destruct() gets called before stack unwind begins, unlocking whatever get locked by new LockerClass();
return $bar;
}
(A lengthy) example:
Let's say you need to perform a series of operaions on SQL database that should not get disrupted. You lock the tables:
<?php
function updateStuff() {
DB::query('LOCK TABLES `a`, `b`, `c` WRITE');
/* some SQL Operations */
someFunction();
/* more SQL Operations */
DB::query('UNLOCK TABLES');
} ?>
Now, let's supouse that someFunction() may throw an exception. This would leave us with the tables locked, as the second DB::query() will not get called. This pretty much will cause the next query to fail. You can do it like:
<?php
function updateStuff() {
DB::query('LOCK TABLES `a`, `b` WRITE');
/* some SQL Operations */
try {
someFunction(); }
catch ( Exception $e ) {
DB::query('UNLOCK TABLES');
throw $e;
}
/* more SQL Operations */
DB::query('UNLOCK TABLES')
} ?>
However, this is rather ugly as we get code duplication. And what if somebody later modifies updateStuff() function in a way it needs another step of cleanup, but forget to add it to catch () {}? Or when we have multiple things to be cleaned up, of which not all will be valid all the time?
My solution using destructor: i create an instance of class DB holding a query unlocking tables which will be executed on destruction.
<?php
function updateStuff() {
$SQLLocker = DB::locker( /*read lock list*/array('a', 'b'), /*write lock list*/array('b') );
/* some SQL Operations */
someFunction();
/* $SQLLocker gets destructed there if someFunction() throws an exception */
DB::query('UNLOCK TABLES');
/* other SQL Operations */
/* $SQLLocker gets destructed there if someFunction() does not throw an exception */
}
class DB {
function locker ( $read, $write ) {
DB::query( /*locks*/);
$ret = new DB;
$ret->onDestruct = 'UNLOCK TABLES';
return $ret;
}
function _destructor() {
if ( $this->onDestruct )
DB::query($this->onDestruct);
}
}
?>
08-Aug-2006 05:18
Sometimes you want a single catch() to catch multiple types of Exception. In a language like Python, you can specify multiple types in a catch(), but in PHP you can only specify one. This can be annoying when you want handle many different Exceptions with the same catch() block.
However, you can replicate the functionality somewhat, because catch(<classname> $var) will match the given <classname> *or any of it's sub-classes*.
For example:
<?php
class DisplayException extends Exception {};
class FileException extends Exception {};
class AccessControl extends FileException {}; // Sub-class of FileException
class IOError extends FileException {}; // Sub-class of FileException
try {
if(!is_readable($somefile))
throw new IOError("File is not readable!");
if(!user_has_access_to_file($someuser, $somefile))
throw new AccessControl("Permission denied!");
if(!display_file($somefile))
throw new DisplayException("Couldn't display file!");
} catch (FileException $e) {
// This block will catch FileException, AccessControl or IOError exceptions, but not Exceptions or DisplayExceptions.
echo "File error: ".$e->getMessage();
exit(1);
}
?>
Corollary: If you want to catch *any* exception, no matter what the type, just use "catch(Exception $var)", because all exceptions are sub-classes of the built-in Exception.
19-Jul-2006 09:06
Here is a basic example of a way to mimick the convenience of exception handling in PHP4:
<?php
do {
if (!$test_condition) {
$error = 'test condition failed';
break;
}
if (!test_function()) {
$error = 'test function failed';
break;
}
echo 'success!';
} while (false);
if (isset($error)) {
echo $error;
}
?>
Obviously this falls far short of PHP5 real exception handling in terms of normalisation. Also, $error won't propogate up the call stack like a real exception (i.e. test_function() can't itself throw an exception, nor call 'break'). But for me, the most important thing about exception handling is to be able to run through code and deal with errors separately, and not have to have a million nested IFs. Compare that code to this:
<?php
if (!$test_condition) {
$error = 'test condition failed';
} elseif (!test_function()) {
$error = 'test function failed';
} else {
echo 'success!';
}
if (isset($error)) {
echo $error;
}
?>
At first this seems no more cumbersome, but what if test_function took arguments that required complicated preparation? You would need a mess like this:
<?php
if (!$test_condition) {
$error = 'test condition failed';
} else {
$fooRes = mysql_query('SELECT foo FROM bar LIMIT 1');
$fooRow = mysql_fetch_assoc($fooRes);
if (!test_function($fooRow)) {
$error = 'test function failed';
} else {
echo 'success!';
}
}
?>
Obviously this could get out of hand quickly. In the first example, you can just prepare the argument before the 'if (!test_function($fooRow))', and on the same nesting level.
This method is also somewhat more flexible in that you can generate 'exceptions', and then deal with them much later (which may be desirable if they aren't really threatening). In PHP5, a catch block must always follow a try block directly.
10-Jul-2006 01:26
Carlos Konstanski: You can't directly extract previous lexical environment in non-functional languages.
What you probably _can_ do is to create a function that dumps the current lexical environment (to an external file or to stdout) and then use it to recreate past states.
function log_lex ($vars, $functions) {
$f = fopen('lex_env.log', 'a');
fwrite($f, serialize(array($vars, $functions, get_defined_constants(), debug_backtrace()));
fclose($f);
}
log_lex (get_defined_vars(), get_defined_functions());
Writing the parser/browser for dumps is the difficult part. Then just sprinkle the code with log_lex() and see how the program runs.
Sure, it works better in Lisp, but Lisp isn't supported on the webservers I work with, and making PHP more like Lisp would probably make it as popular as Lisp is.
@armenio at inmidiaweb dot com dot br:
When echoing into JavaScript code must use htmlspecialchars(addslashes($this->msg)) - think what would happen if msg was "')</script>";
10-May-2006 08:23
<?php
class alert{
public $return;
public $msg;
public function __construct($value){
$this->msg = $value;
}
public function OutPut(){
$this->return = '<script language="JavaScript">';
$this->return .= ' alert("'.$this->msg.'")';
$this->return .= '</script>';
return $this->return;
}
}
$msg = new alert('classe criada com sucesso');
$msg->OutPut();
?>
26-Apr-2006 08:58
This code will turn php errors into exceptions:
<?php
function exceptions_error_handler($severity, $message, $filename, $lineno) {
throw new ErrorException($message, 0, $severity, $filename, $lineno);
}
set_error_handler('exceptions_error_handler');
?>
However since <?php set_error_handler()?> doesn't work with fatal errors, you will not be able to throw them as Exceptions.
14-Nov-2005 05:39
Good PHP5 article about exceptions. I have read quite a few now and this is the only one I've liked:
http://www.andreashalter.ch/phpug/20040115/
30-Sep-2005 05:17
Being able to catch the exception up the call stack from where it is thrown is a good idea, in that it lets you handle the exception closer to where it ought to be handled - in the calling code. Not as good as common lisp though, where you can call a condition handler that resides up the stack without actually unwinding the stack. With this additional feature, you have the lexical environment at the point where the exception occured, combined with the relocation of the handler to a place where the lower-level function's failure can be addressed from the standpoint of the calling code.
Even so, this fluke of PHP that lets you place the try...catch block up the stack from the actual point of error could be used to implement a neat shift in error-handling architecture, where the caller gets to decide how to proceed after an error.
07-May-2005 02:15
PHP5 supports exception throwing inside a function, and catching it outside that function call. There is no mention of this in documentation but it works just fine, as tested by this sample code:
<?php
function exceptionFunction() {
throw new Exception("Throwing an exception!");
}
try {
exceptionFunction();
} catch (Exception $e) {
echo "Exception caught!\n";
}
?>
The result in PHP 5.0.3 is "Exception caught!"
Further tests show that nested functions with exceptions, methods throwing exceptions, etc all work the same way. This is like declaring all classes (or methods) in Java as "class ClassName throws Exception". While I consider this a good thing, you should be aware that any thrown exception will propagate up your stack until it is either caught or runs out of stack.
29-Oct-2004 08:34
<?php
/*I add here example for nested Exception Handling.*/
try
{
try
{
throw new exception();
}
catch(exception $m)
{
print $m;
print "<br>";
print "inner exception"."<br>";
}
throw new exception();
}
catch(exception $e)
{
print $e;
print "outer exception";
}
?>
10-Oct-2004 02:00
To re-throw an exception, you must specify the name of the variable after throw in the catch block, i.e.,
<?php
try {
try {
throw new Exception("Unknown error");
}
catch (Exception $ie) { // Inner Catch Block
throw; // this will NOT work; parse error
throw $ie; // this will re-throw the exception to the outer catch block
}
}
catch (Exception $oe) { // Outer Catch Block
echo $oe;
}
?>
07-Oct-2004 01:16
The base exception class, which is build in PHP5 has also a function getLine(). This is as expected if you look at the class define. But it is not noticed there.
I had just this code:
<?php
try
{
throw new Exception("test")
}
catch (Exception $e)
{
echo $e->getLine()
}
?>
And this worked.
01-Oct-2004 11:23
Like Java php5 also supports nesting of try catch.
21-Aug-2004 01:16
Really good article about exceptions in php5: http://www.zend.com/php5/articles/php5-exceptions.php
