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.
<?php
interface IException
{
/* Protected methods inherited from Exception class */
public function getMessage(); // Exception message
public function getCode(); // User-defined Exception code
public function getFile(); // Source filename
public function getLine(); // Source line
public function getTrace(); // An array of the backtrace()
public function getTraceAsString(); // Formated string of trace
/* Overrideable methods inherited from Exception class */
public function __toString(); // formated string for display
public function __construct($message = null, $code = 0);
}
abstract class CustomException extends Exception implements IException
{
protected $message = 'Unknown exception'; // Exception message
private $string; // Unknown
protected $code = 0; // User-defined exception code
protected $file; // Source filename of exception
protected $line; // Source line of exception
private $trace; // Unknown
public function __construct($message = null, $code = 0)
{
if (!$message) {
throw new $this('Unknown '. get_class($this));
}
parent::__construct($message, $code);
}
public function __toString()
{
return get_class($this) . " '{$this->message}' in {$this->file}({$this->line})\n"
. "{$this->getTraceAsString()}";
}
}
?>
Now you can create new exceptions in one line:
<?php
class TestException extends CustomException {}
?>
Here's a test that shows that all information is properly preserved throughout the backtrace.
<?php
function exceptionTest()
{
try {
throw new TestException();
}
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}Исклучоци
Почист и полокален преглед на PHP референцата, со задржана структура од PHP.net и подобра читливост за примери, секции и белешки.
Исклучоци
Референца за `language.exceptions.php` со подобрена типографија и навигација.
Исклучоци
Содржина
PHP 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 or finally block.
If an exception is thrown and its current function scope has no catch
block, the exception will "bubble up" the call stack to the calling function until it finds a matching catch block. All finally 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 matching catch block, the program will terminate with a fatal error unless a global exception handler has been set.
The thrown object must be an instanceof Проверува тврдење. Trying to throw an object that is not will result in a PHP Fatal Error.
Од PHP 8.0.0, throw 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
А catch block defines how to respond to a thrown exception. A catch
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 first catch block a thrown exception or error encounters that matches the type of the thrown object will handle the object.
Multiple catch blocks can be used to catch different classes of exceptions. Normal execution (when no exception is thrown within the try
block) will continue after that last catch блок дефиниран во секвенца. Може да се појават исклучоци throwн (или повторно фрлен) во рамките на catch блок. Ако не, извршувањето ќе продолжи по catch блок што беше активиран.
Кога ќе се фрли исклучок, кодот што следи по изјавата нема да се изврши, а PHP ќе се обиде да го најде првиот соодветен catch блок. Ако исклучокот не е фатен, ќе се издаде PHP Fatal Error со порака "Uncaught Exception ..." порака, освен ако не е дефиниран ракувач со set_exception_handler().
Од PHP 7.1.0, а catch блок може да специфицира повеќе исклучоци користејќи ја цевката (|) карактер. Ова е корисно кога различни исклучоци од различни хиерархии на класи се третираат на ист начин.
Од PHP 8.0.0, името на променливата за фатен исклучок е опционално. Ако не е специфицирано, catch блокот сепак ќе се изврши, но нема да има пристап до фрлениот објект.
finally
А finally блокот исто така може да биде специфициран по или наместо catch блокови. Кодот во рамките на finally блокот секогаш ќе се извршува по try and catch блокови, без оглед на тоа дали е фрлен исклучок, и пред да продолжи нормалното извршување.
Една забележлива интеракција е помеѓу finally блокот и return изјава. Ако return изјава се сретне внатре во било кој од try или catch блокови, а finally блокот сепак ќе се изврши. Покрај тоа, return изјавата се проценува кога ќе се наиде на неа, но резултатот ќе биде вратен по finally блокот се извршува. Дополнително, ако finally блокот исто така содржи return изјава, вредноста од finally блокот се враќа.
Друга забележлива интеракција е помеѓу исклучок фрлен од внатрешноста на try блок, и исклучок фрлен од внатрешноста на finally блок. Ако и двата блока фрлат исклучок, тогаш исклучокот фрлен од finally блокот ќе биде оној што се шири, а исклучокот фрлен од try блокот ќе се користи како негов претходен исклучок.
Глобален ракувач со исклучоци
Ако на исклучокот му е дозволено да се искачи до глобалниот опсег, тој може да биде фатен од глобален ракувач со исклучоци ако е поставен. На set_exception_handler()
функцијата може да постави функција што ќе се повика наместо catch блок ако не е повикан друг блок. Ефектот е суштински ист како ако целата програма беше завиткана во try-catch блок со таа функција како catch.
Белешки
Забелешка:
Внатрешните PHP функции главно користат Пријавување грешки, само модерно Object-oriented екстензиите користат исклучоци. Сепак, грешките лесно можат да се преведат во исклучоци со ErrorException. Оваа техника работи само со нефатални грешки, сепак.
Пример #1 Претворање на известувањето за грешки во исклучоци
<?php
function exceptions_error_handler($severity, $message, $filename, $lineno) {
throw new ErrorException($message, 0, $severity, $filename, $lineno);
}
set_error_handler('exceptions_error_handler');
?>
На Стандардна PHP библиотека (SPL) обезбедува добар број на вградени исклучоци.
Примери
Пример #2 Фрлање исклучок
<?php
function inverse($x) {
if (!$x) {
throw new Exception('Division by zero.');
}
return 1/$x;
}
try {
echo inverse(5) . "\n";
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
// Continue execution
echo "Hello World\n";
?>Пример #1 Пример што покажува затворачка ознака што го опфаќа последниот нов ред
0.2 Caught exception: Division by zero. Hello World
Пример #3 Ракување со исклучоци со finally block
<?php
function inverse($x) {
if (!$x) {
throw new Exception('Division by zero.');
}
return 1/$x;
}
try {
echo inverse(5) . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
} finally {
echo "First finally.\n";
}
try {
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
} finally {
echo "Second finally.\n";
}
// Continue execution
echo "Hello World\n";
?>Пример #1 Пример што покажува затворачка ознака што го опфаќа последниот нов ред
0.2 First finally. Caught exception: Division by zero. Second finally. Hello World
Пример #4 Интеракција помеѓу finally блок и return
<?php
function test() {
try {
throw new Exception('foo');
} catch (Exception $e) {
return 'catch';
} finally {
return 'finally';
}
}
echo test();
?>Пример #1 Пример што покажува затворачка ознака што го опфаќа последниот нов ред
finally
Пример #5 Вгнезден исклучок
<?php
class MyException extends Exception { }
class Test {
public function testing() {
try {
try {
throw new MyException('foo!');
} catch (MyException $e) {
// rethrow it
throw $e;
}
} catch (Exception $e) {
var_dump($e->getMessage());
}
}
}
$foo = new Test;
$foo->testing();
?>Пример #1 Пример што покажува затворачка ознака што го опфаќа последниот нов ред
string(4) "foo!"
Пример #6 Ракување со повеќекратни исклучоци
<?php
class MyException extends Exception { }
class MyOtherException extends Exception { }
class Test {
public function testing() {
try {
throw new MyException();
} catch (MyException | MyOtherException $e) {
var_dump(get_class($e));
}
}
}
$foo = new Test;
$foo->testing();
?>Пример #1 Пример што покажува затворачка ознака што го опфаќа последниот нов ред
string(11) "MyException"
Пример #7 Изоставување на фатената променлива
Дозволено само во PHP 8.0.0 и понови.
<?php
class SpecificException extends Exception {}
function test() {
throw new SpecificException('Oopsie');
}
try {
test();
} catch (SpecificException) {
print "A SpecificException was thrown, but we don't care about the details.";
}
?>Пример #1 Пример што покажува затворачка ознака што го опфаќа последниот нов ред
A SpecificException was thrown, but we don't care about the details.
Пример #8 Фрлање како израз
Дозволено само во PHP 8.0.0 и понови.
<?php
function test() {
do_something_risky() or throw new Exception('It did not work');
}
function do_something_risky() {
return false; // Simulate failure
}
try {
test();
} catch (Exception $e) {
print $e->getMessage();
}
?>Пример #1 Пример што покажува затворачка ознака што го опфаќа последниот нов ред
It did not work
Пример #9 Исклучок во try и во finally
<?php
try {
try {
throw new Exception(message: 'Third', previous: new Exception('Fourth'));
} finally {
throw new Exception(message: 'First', previous: new Exception('Second'));
}
} catch (Exception $e) {
var_dump(
$e->getMessage(),
$e->getPrevious()->getMessage(),
$e->getPrevious()->getPrevious()->getMessage(),
$e->getPrevious()->getPrevious()->getPrevious()->getMessage(),
);
}Пример #1 Пример што покажува затворачка ознака што го опфаќа последниот нов ред
string(5) "First" string(6) "Second" string(5) "Third" string(6) "Fourth"
Белешки од корисници 13 белешки
Custom error handling on entire pages can avoid half rendered pages for the users:
<?php
ob_start();
try {
/*contains all page logic
and throws error if needed*/
...
} catch (Exception $e) {
ob_end_clean();
displayErrorPage($e->getMessage());
}
?>Easy to understand `finally`.
<?php
try {
try {
echo "before\n";
1 / 0;
echo "after\n";
} finally {
echo "finally\n";
}
} catch (\Throwable) {
echo "exception\n";
}
?>
# Print:
before
finally
exceptionThe "finally" block can change the exception that has been throw by the catch block.
<?php
try{
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 in
Hello 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).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.
<?php
try {
echo "try block<br />";
throw new Exception("test");
} catch (Exception $ex) {
echo "catch block<br />";
} finally {
echo "finally block<br />";
}
// try block
// catch block
// finally block
?>
<?php
try {
echo "try block<br />";
throw new Exception("test");
} catch (Exception $ex) {
echo "catch block<br />";
exit(1);
} finally {
echo "finally block<br />";
}
// try block
// catch block
?>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:
<?php
try {
throw new Error( "foobar" );
// or:
// throw new Exception( "foobar" );
}
catch (Throwable $e) {
var_export( $e );
}
?>In 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 caught
trigger_error($errstr, E_ERROR);
return true;
} else {
// fallback to default php error handler
return false;
}
});
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.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()`.
<?php
try {
try {
throw new RuntimeException('Exception A');
} finally {
throw new RuntimeException('Exception B');
}
}
catch (Throwable $exception) {
echo $exception->getMessage(), "\n";
// 'previous' is magically available!
echo $exception->getPrevious()->getMessage(), "\n";
}
?>
Will print:
Exception B
Exception A<?php
/**
* You can catch exceptions thrown in a deep level function
*/
function employee()
{
throw new \Exception("I am just an employee !");
}
function manager()
{
employee();
}
function boss()
{
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 name
try
{
try
{
if (preg_match('/[^a-z]/i', $name))
{
throw new Exception("$name contains character other than a-z A-Z");
}
if(strpos(strtolower($name), 'name') !== FALSE)
{
throw new Exception("$name contains the word name");
}
echo "The Name is valid";
}
catch(Exception $e)
{
throw new Exception("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/>";
}
?>