PHP has an exception model similar to that of other programming languages. An exception can bethrown, and caught ("catched") within PHP. Code may be surrounded in atry block, to facilitate the catching of potential exceptions. Eachtry must have at least one correspondingcatch orfinally block.
If an exception is thrown and its current function scope has nocatch block, the exception will "bubble up" the call stack to the calling function until it finds a matchingcatch block. Allfinally blocks it encounters along the way will be executed. If the call stack is unwound all the way to the global scope without encountering a matchingcatch block, the program will terminate with a fatal error unless a global exception handler has been set.
The thrown object must be aninstanceofThrowable. Trying to throw an object that is not will result in a PHP Fatal Error.
As of PHP 8.0.0, thethrow keyword is an expression and may be used in any expression context. In prior versions it was a statement and was required to be on its own line.
catch Acatch block defines how to respond to a thrown exception. Acatch block defines one or more types of exception or error it can handle, and optionally a variable to which to assign the exception. (The variable was required prior to PHP 8.0.0.) The firstcatch block a thrown exception or error encounters that matches the type of the thrown object will handle the object.
Multiplecatch blocks can be used to catch different classes of exceptions. Normal execution (when no exception is thrown within thetry block) will continue after that lastcatch block defined in sequence. Exceptions can bethrown (or re-thrown) within acatch block. If not, execution will continue after thecatch block that was triggered.
When an exception is thrown, code following the statement will not be executed, and PHP will attempt to find the first matchingcatch 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 withset_exception_handler().
As of PHP 7.1.0, acatch block may specify multiple exceptions using the pipe (|) character. This is useful for when different exceptions from different class hierarchies are handled the same.
As of PHP 8.0.0, the variable name for a caught exception is optional. If not specified, thecatch block will still execute but will not have access to the thrown object.
finally Afinally block may also be specified after or instead ofcatch blocks. Code within thefinally block will always be executed after thetry andcatch blocks, regardless of whether an exception has been thrown, and before normal execution resumes.
One notable interaction is between thefinally block and areturn statement. If areturn statement is encountered inside either thetry or thecatch blocks, thefinally block will still be executed. Moreover, thereturn statement is evaluated when encountered, but the result will be returned after thefinally block is executed. Additionally, if thefinally block also contains areturn statement, the value from thefinally block is returned.
Another notable interaction is between an exception thrown from within atry block, and an exception thrown from within afinally block. If both blocks throw an exception, then the exception thrown from thefinally block will be the one that is propagated, and the exception thrown from thetry block will be used as its previous exception.
If an exception is allowed to bubble up to the global scope, it may be caught by a global exception handler if set. Theset_exception_handler() function can set a function that will be called in place of acatch block if no other block is invoked. The effect is essentially the same as if the entire program were wrapped in atry-catch block with that function as thecatch.
Note:
Internal PHP functions mainly useError reporting, only modernObject-oriented extensions use exceptions. However, errors can be easily translated to exceptions withErrorException. This technique only works with non-fatal errors, however.
Example #1 Converting error reporting to exceptions
<?php
functionexceptions_error_handler($severity,$message,$filename,$lineno) {
throw newErrorException($message,0,$severity,$filename,$lineno);
}
set_error_handler('exceptions_error_handler');
?>
TheStandard PHP Library (SPL) provides a good number ofbuilt-in exceptions.
Example #2 Throwing an Exception
<?php
functioninverse($x) {
if (!$x) {
throw newException('Division by zero.');
}
return1/$x;
}
try {
echoinverse(5) ."\n";
echoinverse(0) ."\n";
} catch (Exception $e) {
echo'Caught exception: ',$e->getMessage(),"\n";
}
// Continue execution
echo"Hello World\n";
?>The above example will output:
0.2Caught exception: Division by zero.Hello World
Example #3 Exception handling with afinally block
<?php
functioninverse($x) {
if (!$x) {
throw newException('Division by zero.');
}
return1/$x;
}
try {
echoinverse(5) ."\n";
} catch (Exception $e) {
echo'Caught exception: ',$e->getMessage(),"\n";
} finally {
echo"First finally.\n";
}
try {
echoinverse(0) ."\n";
} catch (Exception $e) {
echo'Caught exception: ',$e->getMessage(),"\n";
} finally {
echo"Second finally.\n";
}
// Continue execution
echo"Hello World\n";
?>The above example will output:
0.2First finally.Caught exception: Division by zero.Second finally.Hello World
Example #4 Interaction between thefinally block andreturn
<?php
functiontest() {
try {
throw newException('foo');
} catch (Exception $e) {
return'catch';
} finally {
return'finally';
}
}
echotest();
?>The above example will output:
finally
Example #5 Nested Exception
<?php
classMyExceptionextendsException{ }
classTest{
public functiontesting() {
try {
try {
throw newMyException('foo!');
} catch (MyException $e) {
// rethrow it
throw$e;
}
} catch (Exception $e) {
var_dump($e->getMessage());
}
}
}
$foo= newTest;
$foo->testing();
?>The above example will output:
string(4) "foo!"
Example #6 Multi catch exception handling
<?php
classMyExceptionextendsException{ }
classMyOtherExceptionextendsException{ }
classTest{
public functiontesting() {
try {
throw newMyException();
} catch (MyException|MyOtherException $e) {
var_dump(get_class($e));
}
}
}
$foo= newTest;
$foo->testing();
?>The above example will output:
string(11) "MyException"
Example #7 Omitting the caught variable
Only permitted in PHP 8.0.0 and later.
<?php
classSpecificExceptionextendsException{}
functiontest() {
throw newSpecificException('Oopsie');
}
try {
test();
} catch (SpecificException) {
print"A SpecificException was thrown, but we don't care about the details.";
}
?>The above example will output:
A SpecificException was thrown, but we don't care about the details.
Example #8 Throw as an expression
Only permitted in PHP 8.0.0 and later.
<?php
functiontest() {
do_something_risky() or throw newException('It did not work');
}
functiondo_something_risky() {
returnfalse;// Simulate failure
}
try {
test();
} catch (Exception $e) {
print$e->getMessage();
}
?>The above example will output:
It did not work
Example #9 Exception in try and in finally
<?php
try {
try {
throw newException(message:'Third',previous: newException('Fourth'));
} finally {
throw newException(message:'First',previous: newException('Second'));
}
} catch (Exception $e) {
var_dump(
$e->getMessage(),
$e->getPrevious()->getMessage(),
$e->getPrevious()->getPrevious()->getMessage(),
$e->getPrevious()->getPrevious()->getPrevious()->getMessage(),
);
}The above example will output:
string(5) "First"string(6) "Second"string(5) "Third"string(6) "Fourth"
If you intend on creating a lot of custom exceptions, you may find this code useful. I've created an interface and an abstract exception class that ensures that all parts of the built-in Exception class are preserved in child classes. It also properly pushes all information back to the parent constructor ensuring that nothing is lost. This allows you to quickly create new exceptions on the fly. It also overrides the default __toString method with a more thorough one.<?phpinterfaceIException{/* Protected methods inherited from Exception class */public functiongetMessage();// Exception messagepublic functiongetCode();// User-defined Exception codepublic functiongetFile();// Source filenamepublic functiongetLine();// Source linepublic functiongetTrace();// An array of the backtrace()public functiongetTraceAsString();// Formated string of trace /* Overrideable methods inherited from Exception class */public function__toString();// formated string for displaypublic function__construct($message=null,$code=0);}abstract classCustomExceptionextendsExceptionimplementsIException{ protected$message='Unknown exception';// Exception messageprivate$string;// Unknownprotected$code=0;// User-defined exception codeprotected$file;// Source filename of exceptionprotected$line;// Source line of exceptionprivate$trace;// Unknownpublic function__construct($message=null,$code=0) { if (!$message) { throw new$this('Unknown '.get_class($this)); }parent::__construct($message,$code); } public function__toString() { returnget_class($this) ." '{$this->message}' in{$this->file}({$this->line})\n"."{$this->getTraceAsString()}"; }}?>Now you can create new exceptions in one line:<?phpclassTestExceptionextendsCustomException{}?>Here's a test that shows that all information is properly preserved throughout the backtrace.<?phpfunctionexceptionTest(){ try { throw newTestException(); } catch (TestException $e) { echo"Caught TestException ('{$e->getMessage()}')\n{$e}\n"; } catch (Exception $e) { echo"Caught Exception ('{$e->getMessage()}')\n{$e}\n"; }}echo'<pre>'.exceptionTest() .'</pre>';?>Here's a sample output:Caught TestException ('Unknown TestException')TestException 'Unknown TestException' in C:\xampp\htdocs\CustomException\CustomException.php(31)#0 C:\xampp\htdocs\CustomException\ExceptionTest.php(19): CustomException->__construct()#1 C:\xampp\htdocs\CustomException\ExceptionTest.php(43): exceptionTest()#2 {main}Custom error handling on entire pages can avoid half rendered pages for the users:<?phpob_start();try {/*contains all page logic and throws error if needed*/...} catch (Exception $e) {ob_end_clean();displayErrorPage($e->getMessage());}?>Easy to understand `finally`.<?phptry { try { echo"before\n";1/0; echo"after\n"; } finally { echo"finally\n"; }} catch (\Throwable) { echo"exception\n";}?># Print:beforefinallyexceptionThe "finally" block can change the exception that has been throw by the catch block.<?phptry{ try { throw new\Exception("Hello"); } catch(\Exception $e) { echo$e->getMessage()." catch in\n"; throw$e; } finally { echo$e->getMessage()." finally \n"; throw new\Exception("Bye"); }} catch (\Exception $e) { echo$e->getMessage()." catch out\n";}?>The output is:Hello catch inHello finally Bye catch out‘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.’‘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().’These two sentences seem a bit contradicting about what happens ‘when a catch matching the thrown exception’s class is not present’ (and the second sentence is actually correct).As noted elsewhere, throwing an exception from the `finally` block will replace a previously thrown exception. But the original exception is magically available from the new exception's `getPrevious()`.<?phptry { try { throw newRuntimeException('Exception A'); } finally { throw newRuntimeException('Exception B'); }}catch (Throwable $exception) { echo$exception->getMessage(),"\n";// 'previous' is magically available!echo$exception->getPrevious()->getMessage(),"\n";}?>Will print:Exception BException AIn case your E_WARNING type of errors aren't catchable with try/catch you can change them to another type of error like this:<?php set_error_handler(function($errno,$errstr,$errfile,$errline){ if($errno===E_WARNING){// make it more serious than a warning so it can be caughttrigger_error($errstr,E_ERROR); returntrue; } else {// fallback to default php error handlerreturnfalse; } }); try {// code that might result in a E_WARNING} catch(Exception $e){// code to handle the E_WARNING (it's actually changed to E_ERROR at this point)} finally {restore_error_handler(); }?>#3 is not a good example. inverse("0a") would not be caught since (bool) "0a" returns true, yet 1/"0a" casts the string to integer zero and attempts to perform the calculation.Starting in PHP 7, the classes Exception and Error both implement the Throwable interface. This means, if you want to catch both Error instances and Exception instances, you should catch Throwable objects, like this:<?phptry { throw newError("foobar");// or: // throw new Exception( "foobar" );}catch (Throwable $e) {var_export($e);}?>When using finally keep in mind that when a exit/die statement is used in the catch block it will NOT go through the finally block.<?phptry { echo"try block<br />"; throw newException("test");} catch (Exception $ex) { echo"catch block<br />";} finally { echo"finally block<br />";}// try block// catch block// finally block?><?phptry { echo"try block<br />"; throw newException("test");} catch (Exception $ex) { echo"catch block<br />"; exit(1);} finally { echo"finally block<br />";}// try block// catch block?><?php/** * You can catch exceptions thrown in a deep level function */functionemployee(){ throw new\Exception("I am just an employee !");}functionmanager(){employee();}functionboss(){ try {manager(); } catch (\Exception $e) { echo$e->getMessage(); }}boss();// output: "I am just an employee !"Contrary to the documentation it is possible in PHP 5.5 and higher use only try-finally blocks without any catch block.the following is an example of a re-thrown exception and the using of getPrevious function:<?php$name="Name";//check if the name contains only letters, and does not contain the word nametry { try { if (preg_match('/[^a-z]/i',$name)) { throw newException("$name contains character other than a-z A-Z"); } if(strpos(strtolower($name),'name') !==FALSE) { throw newException("$name contains the word name"); } echo"The Name is valid"; } catch(Exception $e) { throw newException("insert name again",0,$e); } } catch (Exception $e) { if ($e->getPrevious()) { echo"The Previous Exception is: ".$e->getPrevious()->getMessage()."<br/>"; } echo"The Exception is: ".$e->getMessage()."<br/>"; }?>