PHP
downloads | documentation | faq | getting help | mailing lists | wiki | reporting bugs | php.net sites | links | conferences | my php.net

search for in the

Ссылки. Разъяснения> <Правила разбора имён
Last updated: Fri, 28 Nov 2008

view this page in

Исключения

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

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

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

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

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

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

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

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

Наследование исключений

Определенный пользователем класс исключения должен быть определен, как класс расширяющий (наследующий) встроенный класс Exception. Ниже приведены методы и свойства класса Exception, доступные дочерним классам.

Пример #2 Встроеный класс Exception

<?php
class Exception
{
    protected 
$message 'Unknown exception';   // сообшение
    
protected $code 0;                        // Код исключения,
    
определяемый пользователем
    
protected $file;                            // файл в котором было
    
выброшено исключение
    
protected $line;                            // строка в которой было
    
выброшено исключение

    
function __construct($message null$code 0);

    final function 
getMessage();                // Возвращает сообшение
    
исключения 
    
final function getCode();                   // Код исключения
    
final function getFile();                   // Файл, где выброшено
    
исключение
    
final function getLine();                   // Строка, выбросившая исключени
    
final function getTrace();                  // Массив backtrace()
    
final function getTraceAsString();          // Обратная трассировка как
    
строка

    
/* Overrideable - т.е. то, что можно переопределить */
    
function __toString();                       // должен вернуть
    
форматированную строкудля отображения
}
?>

Если класс, наследуемый от Exception переопределяет конструктор, необходимо вызвать в конструкторе parent::__construct(), чтобы быть уверенным, что все дынные будут доступны. Метод __toString() может быть переопределен, что бы обеспечить нужный вывод, когда объект преобразуется в строку.

Пример #3 Наследование класса Exception

<?php
/**
 * Определим свой класс исключения
 */
class MyException extends Exception
{
    
// Переопределим исключение так, что параметр message станет обязательным
    
public function __construct($message$code 0) {
        
// какой-то код 
    
        
parent::__construct($message$code);
    }

    
// Переопределим строковое представление объекта.
    
public function __toString() {
        return 
__CLASS__ ": [{$this->code}]: {$this->message}\n";
    }

    public function 
customFunction() {
        echo 
"Мы можем определять новые методы в наследуемом классе\n";
    }
}


/**
 * Создадим класс для тестирования исключения
 */
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 new MyException('1 - неправильный параметр'5);
                break;

            case 
self::THROW_DEFAULT:
                
// Бросаем встроеное исключение
                
throw new Exception('2 - недопустимый параметр'6);
                break;

            default: 
                
// Никаких исключений, объект будет создан.
                
$this->var $avalue;
                break;
        }
    }
}


// Example 1
try {
    
$o = new TestException(TestException::THROW_CUSTOM);
} catch (
MyException $e) {      // Will be caught
    
echo "Поймано собственное, переопределенное исключение\n"$e;
    
$e->customFunction();
} catch (
Exception $e) {        // Будет пропущено.
    
echo "Поймано встроенное исключение\n"$e;
}

// Отсюда будет продолжено выполнение программы
var_dump($o);
echo 
"\n\n";


// Example 2
try {
    
$o = new TestException(TestException::THROW_DEFAULT);
} catch (
MyException $e) {      // Тип исключения не совпадет
    
echo "Поймано переопределенное исключение\n"$e;
    
$e->customFunction();
} catch (
Exception $e) {        // Будет перехвачено
    
echo "Перехвачено встроенное исключение\n"$e;
}

// Отсюда будет продолжено выполнение программы
var_dump($o);
echo 
"\n\n";


// Example 3
try {
    
$o = new TestException(TestException::THROW_CUSTOM);
} catch (
Exception $e) {        // Будет перехвачено.
    
echo "Поймано встроенное исключение\n"$e;
}

// Продолжение исполнения программы
var_dump($o);
echo 
"\n\n";


// Example 4
try {
    
$o = new TestException();
} catch (
Exception $e) {        // Будет пропущено, т.к. исключение не выкидывается
    
echo "Поймано встроенное исключение\n"$e;
}

// Продолжение выполнения программы
var_dump($o);
echo 
"\n\n";
?>


add a note add a note User Contributed Notes
Исключения
Shot (Piotr Szotkowski)
21-Oct-2008 12:13
‘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).
i dot crash17 at gmail dot com
04-Apr-2008 09:50
As a response to Anonymous on 28-Dec-2007 07:25, please, remember that creating lots of MyXExtension classes is wrong if and only if your object oriented model is not well designed.

One should (I even think MUST) create as many custom Exception extentions as the design needs, because every different type of exception requires (or will require, even if we haven't think about it) a different response, from showing different messages to the user, to log error messages or even kill the script.

Forwarding can become confusing if you are using a big number of classes and packages, and is useless if you are using few (unless you are too lasy to write 3, 5 or 10 includes and their respective Exception classes).

Remember that object orientation was created to give more semantics to the programming code so the programmer understands better what is written without having to excecute the code, so don't do things that go against that, which is the cool thing about object orientation.
michael dot ochs at gmx dot net
21-Mar-2008 04:44
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();
?>
chugadie dot geo at yahoo dot com
05-Mar-2008 10:40
@webmaster at asylum-et dot com

What Mo is describing is bug 44053 (http://bugs.php.net/bug.php?id=44053) in which exceptions cannot be caught if you are using a custom error handler to catch warnings, notices, etc.
omnibus at omnibus dot edu dot pl
04-Dec-2007 03:11
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
frank at netventures dot com dot au
07-Nov-2007 03:43
If you are going to use multiple catches within a try-catch then do not forget the stacking order of those catches!

This is important as any classes that extend the Exception class, like MyException in example 20.3, will be caught in the Exception case. This is because your newly extended class also has a class type of Exception. This baffled me for awhile as the examples here worked but mine didn't because my first catch was trying to catch Exception.

Example:

<?php

/**
 * My1Exception extends Exception
 * My2Exception extends Exception
 */

/**
 * This will always fall in the first exception
 */
try {
    throw new
My1Exception("My fail english? That's unpossible", 69);
} catch (
Exception $e) {
    print
"Incorrect Exception";
} catch (
My1Exception $e) {
    print
"Correct Exception but I won't be here";
} catch (
My2Exception $e) {
    print
"Again, incorrect";
}

/**
 * Whereas here, the catch stacking order was changed so our throw will cascade into the correct catch
 */
try {
    throw new
My1Exception("My cat's breath smells like cat food", 69);
} catch (
My2Exception $e) {
    print
"Incorrect Exception";
} catch (
My1Exception $e) {
    print
"Correct Exception and I WILL be printed";
} catch (
Exception $e) {
    print
"Again, incorrect";
}

?>

So, ALWAYS keep the Exception catch block at the bottom, then any of the other extended exceptions that extend from Exception, then any of your other extended exceptions that extend from those extended exceptions, etc
hartym dot dont dot like dot spam at gmail dot com
18-Oct-2007 04:41
@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.
peter dot goodman at gmail dot com
14-Jun-2007 07:52
I've found that exception destructors are not called unless the exception is caught.

I've created a simple solution to this problem (calling __destruct() from __toString() ) and have written up a lengthy article detailing one good use case for this method at http://ioreader.com/2007/06/14/taking-advantage-of-exceptions-in-php5/

Also, one of the useful things about using a destructor as a clean up method is that it is called at the end of a catch statement.
jon at hackcraft dot net
24-Jan-2007 09: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.
dexen at google dot me dot up
18-Sep-2006 06: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);
    }
}
?>
jazfresh at hotmail.com
07-Aug-2006 10: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.
fjoggen at gmail dot com
26-Apr-2006 01: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.
jd at wuputah dot com
06-May-2005 07: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.

 
show source | credits | sitemap | contact | advertising | mirror sites