Исключения

Содержание

Модель исключений (exceptions) в PHP 5 проще, чем в других языках программирования. Исключение можно сгенерировать (как говорят, "выбросить") при помощи оператора throw, и можно перехватить (или, как говорят, "поймать") оператором catch. Код генерирующий исключение, должен быть окружен блоком try, для того чтобы можно было перехватить исключение. Каждый блок try должен иметь как минимум один соответствующий ему блок catch. Так же можно использовать несколько блоков catch, перехватывающих различные классы исключений. Нормальное выполнение (когда не генерируются исключения в блоках try или когда класс сгенерированного исключения не совпадает с классами, объявленными в соответствующих блоках catch) будет продолжено за последним блоком catch. Исключения так же могут быть сгенерированы (или вызваны еще раз) оператором throw внутри блока catch.

При генерации исключения код следующий за оператором throw исполнен не будет, а PHP предпримет попытку найти первый блок catch, перехватывающий исключение данного класса. Если исключение не будет перехвачено, PHP выдаст сообщение об ошибке: "Uncaught Exception ..." (Неперехваченное исключение), если не был определен обработчик ошибок при помощи функции set_exception_handler().

В PHP 5.5 и более поздних версиях также можно использовать блок finally после блока catch. Код в блоке finally всегда будет выполняться после кода в блоках try и catch, вне зависимости было ли брошено исключение или нет, перед тем как продолжится нормальное выполнение кода. whether an exception has been thrown, and before normal execution resumes.

Генерируемый объект должен принадлежать классу Exception или наследоваться от Exception. Попытка сгенерировать исключение другого класса приведет к неисправимой ошибке.

Замечание:

Внутренние функции PHP в основном используют сообщения об ошибках, и только новые объектно-ориентированные расширения используют исключения. Однако, ошибки можно легко преобразовать в исключения с помощью класса ErrorException.

Подсказка

Стандартная библиотека PHP (SPL) предоставляет хороший набор встроенных классов исключений.

Пример #1 Выброс исключений

<?php
function inverse($x) {
    if (!
$x) {
        throw new 
Exception('Деление на ноль.');
    }
    return 
1/$x;
}

try {
    echo 
inverse(5) . "\n";
    echo 
inverse(0) . "\n";
} catch (
Exception $e) {
    echo 
'Выброшено исключение: ',  $e->getMessage(), "\n";
}

// Продолжение выполнения
echo "Hello World\n";
?>

Результат выполнения данного примера:

0.2
Выброшено исключение: Деление на ноль.
Hello World

Пример #2 Вложенные исключения

<?php
function inverse($x) {
    if (!
$x) {
        throw new 
Exception('Деление на ноль.');
    }
    return 
1/$x;
}

try {
    echo 
inverse(5) . "\n";
} catch (
Exception $e) {
    echo 
'Поймано исключение: ',  $e->getMessage(), "\n";
} finally {
    echo 
"Первое finally.\n";
}

try {
    echo 
inverse(0) . "\n";
} catch (
Exception $e) {
    echo 
'Поймано исключение: ',  $e->getMessage(), "\n";
} finally {
    echo 
"Второе finally.\n";
}

// Продолжение нормального выполнения
echo "Hello World\n";
?>

Результат выполнения данного примера:

0.2
Первое finally.
Поймано исключение: Деление на ноль.
Второе finally.
Hello World

Пример #3 Вложенные исключения

<?php

class MyException extends Exception { }

class 
Test {
    public function 
testing() {
        try {
            try {
                throw new 
MyException('foo!');
            } catch (
MyException $e) {
                
// повторный выброс исключения
                
throw $e;
            }
        } catch (
Exception $e) {
            
var_dump($e->getMessage());
        }
    }
}

$foo = new Test;
$foo->testing();

?>

Результат выполнения данного примера:

string(4) "foo!"

Коментарии

This code will turn php errors into exceptions:

<?php
function exceptions_error_handler($severity$message$filename$lineno) { 
    throw new 
ErrorException($message0$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.
2006-04-26 16:58:22
http://php5.kiev.ua/manual/ru/language.exceptions.html
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.
2006-08-08 01:18:44
http://php5.kiev.ua/manual/ru/language.exceptions.html
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.
2007-01-24 11:52:17
http://php5.kiev.ua/manual/ru/language.exceptions.html
@serenity: of course you need to throw exception within the try block, catch will not watch fatal errors, nor less important errors but only exceptions that are instanceof the exception type you're giving. Of course by within the try block, i mean within every functions call happening in try block.

For example, to nicely handle old mysql errors, you can do something like this:

<?php
try
{
 
$connection mysql_connect(...);
  if (
$connection === false)
  {
    throw new 
Exception('Cannot connect do mysql');
  }

   
/* ... do whatever you need with database, that may mail and throw exceptions too ... */

   
mysql_close($connection);
}
catch (
Exception $e)
{
   
/* ... add logging stuff there if you need ... */

 
echo "This page cannot be displayed";
}

?>

By doing so, you're aiming at the don't repeat yourself (D.R.Y) concept, by managing error handling at only one place for the whole.
2007-10-18 07:41:30
http://php5.kiev.ua/manual/ru/language.exceptions.html
Just to be more precise in what Frank found:
Catch the exceptions always in order from the bottom to the top of the Exception and subclasses class hierarchy. If you have class MyException extending Exception and class My2Exception extending MyException always catch My2Exception before MyException.

Hope this helps
2007-12-04 05:11:29
http://php5.kiev.ua/manual/ru/language.exceptions.html
Actually it isn't possible to do:
<?php
someFunction
() OR throw new Exception();
?>

This leads to a T_THROW Syntax Error. If you want to use this kind of exceptions, you can do the following:

<?php
function throwException($message null,$code null) {
    throw new 
Exception($message,$code);
}

someFunction() OR throwException();
?>
2008-03-21 05:44:17
http://php5.kiev.ua/manual/ru/language.exceptions.html
‘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).
2008-10-21 15:13:17
http://php5.kiev.ua/manual/ru/language.exceptions.html
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}
2009-05-27 15:19:13
http://php5.kiev.ua/manual/ru/language.exceptions.html
When catching an exception inside a namespace it is important that you escape to the global space:

<?php
 namespace SomeNamespace
;

 class 
SomeClass {

  function 
SomeFunction() {
   try {
    throw new 
Exception('Some Error Message');
   } catch (
Exception $e) {
   
var_dump($e->getMessage());
   }
  }

 }
?>
2010-05-18 16:05:58
http://php5.kiev.ua/manual/ru/language.exceptions.html
Автор:
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());
}
?>
2011-05-05 04:18:51
http://php5.kiev.ua/manual/ru/language.exceptions.html
If you use the set_error_handler() to throw exceptions of errors, you may encounter issues with __autoload() functionality saying that your class doesn't exist and that's it.

If you do this:

<?php

class MyException extends Exception
{
}

class 
Tester
{
    public function 
foobar()
    {
        try
        {
           
$this->helloWorld();
        } catch (
MyException $e) {
            throw new 
Exception('Problem in foobar',0,$e);
        }
    }
   
    protected function 
helloWorld()
    {
        throw new 
MyException('Problem in helloWorld()');
    }
}

$tester = new Tester;
try
{
   
$tester->foobar();
} catch (
Exception $e) {
    echo 
$e->getTraceAsString();
}
?>

The trace will only show $tester->foobar() and not the call made to $tester->helloWorld().

In other words, if you pass a previous exception to a new one, the previous exception's stack trace is taken into account in the new exception.
2011-07-27 15:20:23
http://php5.kiev.ua/manual/ru/language.exceptions.html
Автор:
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/>";
   }

 
?>
2011-11-28 23:36:00
http://php5.kiev.ua/manual/ru/language.exceptions.html
Автор:
The "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
2013-06-24 21:13:34
http://php5.kiev.ua/manual/ru/language.exceptions.html
Just an example why finally blocks are usefull (5.5)

<?php

//without catch
function example() {
  try {
   
//do something that throws an exeption
 
}
 
finally {
   
//this code will be executed even when the exception is executed
 
}
}

function 
example2() {
  try {
     
//open sql connection check user as example
     
if(condition) { 
        return 
false;
     }
  }
 
finally {
   
//close the sql connection, this will be executed even if the return is called.
 
}
}

?>
2013-06-26 17:05:33
http://php5.kiev.ua/manual/ru/language.exceptions.html
Just an example why finally blocks are usefull (5.5)

<?php

//without catch
function example() {
  try {
   
//do something that throws an exeption
 
}
 
finally {
   
//this code will be executed even when the exception is executed
 
}
}

function 
example2() {
  try {
     
//open sql connection check user as example
     
if(condition) { 
        return 
false;
     }
  }
 
finally {
   
//close the sql connection, this will be executed even if the return is called.
 
}
}
2013-06-26 17:08:23
http://php5.kiev.ua/manual/ru/language.exceptions.html
If you are using a namespace, you must indicate the global namespace when using Exceptions.
<?php
namespace alpha
;
function 
foo(){
    throw new 
Exception("Something is wrong!");
   
// throw new Exception(""); will fail
}

try {
   
foo();
} catch( 
Exception $e ) {
   
// catch( Exception $e ) will give no warning, but will not catch Exception
   
echo "ERROR: $e";
}

?>
2013-09-27 20:22:50
http://php5.kiev.ua/manual/ru/language.exceptions.html
There's some inconsistent behaviour associated with PHP 5.5.3's finally and return statements. If a method returns a variable in a try block (e.g. return $foo;), and finally modifies that variable, the /modified/ value is returned. However, if the try block has a return that has to be evaluated in-line (e.g. return $foo+0;), finally's changes to $foo will /not/ affect the return value.

[code]
function returnVariable(){
    $foo = 1;
    try{
        return $foo;
    } finally {
        $foo++;
    }
}

function returnVariablePlusZero(){
    $foo = 1;
    try{
        return $foo + 0;
    } finally {
        $foo++;
    }
}

$test1 = returnVariable(); // returns 2, not the correct value of 1.
$test2 = returnVariablePlusZero(); // returns correct value of 1, but inconsistent with $test1.
[/code]

It looks like it's trying to be efficient by not allocating additional memory for the return value when it thinks it doesn't have to, but the spec is that finally is run after try is completed execution, and that includes the evaluation of the return expression.

One could argue (weakly) that the first method should be the correct result, but at least the two methods should be consistent.
2013-10-02 17:50:51
http://php5.kiev.ua/manual/ru/language.exceptions.html
Using a return statement inside a finally block will override any other return statement or thrown exception from the try block and all defined catch blocks.   Code execution in the parent stack will continue as if the exception was never thrown. 

Frankly this is a good design decision because it means I can optionally dismiss all thrown exceptions from 1 or more catch blocks in one place, without having to nest my whole try block inside an additional (and otherwise needless) try/catch block.

This is the same behavior as Java, whereas C# throws a compile time error when a return statement exists inside a finally block.  So I figured it was worth pointing out to PHP devs who may not have any exposure to finally blocks or how other languages do it.

<?php

function asdf()
{
    try {
        throw new 
Exception('error');
    }
    catch(
Exception $e) {
        echo 
"An error occurred";
        throw 
$e;
    }
   
finally {
               
//This overrides the exception as if it were never thrown
       
return "\nException erased";
    }
}

try {
    echo 
asdf();
}
catch(
Exception $e) {
    echo 
"\nResult: " $e->getMessage();
}
?>

The output from above will look like this:

    An error occurred
    Exception erased

Without the return statement in the finally block it would look like this:

    An error occurred
    Result: error
2014-01-24 19:42:57
http://php5.kiev.ua/manual/ru/language.exceptions.html
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
?>
2014-06-18 15:34:04
http://php5.kiev.ua/manual/ru/language.exceptions.html
Автор:
Remember that Exceptions are also objects and can be handled as such; they can be constructed in and returned as values from functions, passed as arguments to other functions, and examined before being thrown. You don't have to throw it as soon as you have constructed it (the stack trace will of course reflect the moment the Exception was constructed, not the moment it was thrown).

You might, for example, want to collect additional information to include in YourException but you don't want to clutter up the YourException class or the code containing the "throw" statement by collecting the information there. Or you might want to do something (such as logging) with each Exception that is thrown from a certain region (catch it, pass it to the logging function, then rethrow it).
2014-06-23 09:58:59
http://php5.kiev.ua/manual/ru/language.exceptions.html
Автор:
Contrary to the documentation it is possible in PHP 5.5 and higher use only try-finally blocks without any catch block.
2014-12-04 15:45:20
http://php5.kiev.ua/manual/ru/language.exceptions.html
Автор:
#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.
2015-04-04 05:47:31
http://php5.kiev.ua/manual/ru/language.exceptions.html
catch doesn't check for the existence of the Exception class, so avoid typo.

<?php
   
class MyException extends Exception
   
{
      ...
   }

   try
   {
      throw new 
MyException(...);
   }
   catch (
MuException $e// <--- typo
   
{
      ...
   }
?> 

You WON'T get
   Fatal error: Class MuException could not be loaded ...

You WILL get
   Fatal error: Uncaught exception 'MyException' ...
2015-09-26 23:20:49
http://php5.kiev.ua/manual/ru/language.exceptions.html
Type declarations will trigger Uncaught TypeError when a different type is passed. And it cannot be caught with the Exception class.
<?php
   
function xc(array $a){       
    }   
    try{
       
xc(4);
    }catch (
Exception $e){ // failed to catch it
       
echo $e->getMessage();
    }
?>

You should use TypeError instead for PHP 7+,

<?php
   
function xc(array $a){   
    }   
    try{
       
xc(4);
    }catch (
TypeError $e){
        echo 
$e->getMessage();
    }
?>

In php version prior to 7.0, you should translate Catchable fatal errors to an exception and then catch it.

<?php
   
function exceptionErrorHandler($errNumber$errStr$errFile$errLine ) {
        throw new 
ErrorException($errStr0$errNumber$errFile$errLine);
    }
   
set_error_handler('exceptionErrorHandler');
    function 
s(array $a){       
    }
    try{
       
s(4);
    }catch (
Exception $e){
        echo 
$e->getMessage();
    }
?>
2015-11-07 19:40:30
http://php5.kiev.ua/manual/ru/language.exceptions.html
<?php
function Test_Extention($var1)
{
    if(
$var1 == false)
    {
        throw new 
Exception('Invalid !');
    }
}
//end function

try
{
   
Test_Extention(false);
   
printf("%s","Valid");
}

catch(
Exception $e)
{
   
printf("%s","Message : " $e->getMessage());
}
?>
2016-04-11 10:05:23
http://php5.kiev.ua/manual/ru/language.exceptions.html
If a TRY has a FINALLY, a RETURN either in the TRY or a CATCH won't terminate the script. Code in the same block after the RETURN will not be executed, and the RETURN itself will be "copied" to the bottom of the FINALLY block to be executed.

a RETURN in the FINALLY block will override value(s) returned from the TRY or a CATCH block.

An EXIT or a DIE always terminate the script after themselves.

code 1

<?php
   
function foo(){
       
$bar 1;
        try{
            throw new 
Exception('I am Wu Xiancheng.');
        }catch(
Exception $e){
            return 
$bar;
           
$bar--; // this line will be ignored
       
}finally{
           
$bar++;
        }
    }
    echo 
foo(); // 2
?>

code 2

<?php
   
function foo(){
       
$bar 1;
        try{
            throw new 
Exception('I am Wu Xiancheng.');
        }catch(
Exception $e){
            return 
$bar;
           
$bar--; // this line will be ignored
       
}finally{
           
$bar++;
            return 
$bar;
        }
    }
    echo 
foo(); //2 
?>

code 3

<?php
   
function foo(){
       
$bar 1;
        try{
            throw new 
Exception('I am Wu Xiancheng.');
        }catch(
Exception $e){
            return 
$bar;
           
$bar--; // this line will be ignored
       
}finally{
            return 
100;
        }
    }
    echo 
foo(); //100
?>
2016-08-11 12:25:50
http://php5.kiev.ua/manual/ru/language.exceptions.html
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($errstrE_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();
    }
?>
2017-06-15 13:12:33
http://php5.kiev.ua/manual/ru/language.exceptions.html
Please don't do something like this:

<?php
try {
   
// experimental stuff to check whether something works
} catch(Throwable $e) {
    return 
false;
finally {
    return 
true;
}
?>

Well, it is allowed syntax, it is bad practice, because once it hits the exception block, finally won't be called(because it exits the scope). Finally was invented to prevent duplicated code after one operation, regardless whether it was successful(for example closing the connection to a server). This way, you don't have any duplicated code. So you don't need to use this structure.
2017-07-09 00:10:18
http://php5.kiev.ua/manual/ru/language.exceptions.html

    Поддержать сайт на родительском проекте КГБ