Глава 20. Изключения

Съдържание
Наследяване на изключения

PHP 5 има модел за изключения подобен на този в други езици за програмиране. Изключенията могат да бъдат хвърляни, пробвани и хващани в PHP. Пробващият блок трябва да включва поне един хвашащ блок. Могат да бъдат използвани няколко хващащи блокове, за да се хванат различни типове класове; изпълнението ще продължи последователно след последния дефиниран хващащ блок. Изключенията могат да бъдат хвърляни в хващащи блокове.

Когато някое изключение бива хвърлено, кодът след инструкцията няма да бъде изпълнен и PHP ще опита да намери първия съвпадащ хващащ блок. Ако изключението не бъде хванато, ще бъде изведена PHP Фатална грешка със съобщение за Неприхванато изключение (Uncaught Exception), освен ако не е бил дефиниран обработчик посредством set_exception_handler().

Пример 20-1. Хвърляне на изключение

<?php
try {
    $error = 'Винаги хвърля тази грешка';
    throw new Exception($error);

    // Кодът след изключението никога не се изпълнява.
    echo 'Никога няма да се изпълни';

} catch (Exception $e) {
    echo 'Хванато изключение: ',  $e->getMessage(), "\n";
}

// Изпълнението продължава
echo 'Здравей Свят';
?>

Наследяване на изключения

Потребителски дефиниран клас за изключения може да бъде дефиниран, като се наследи базовия клас за изключения. Членовете и свойствата по-долу показват какво е достъпно в класа дете, който наследява базовия клас за изключения.

Пример 20-2. Базов клас за изключения

<?php
class Exception
{
    protected $message = 'Неизвестно изключение';   // съобщение на изключението
    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();          // форматиран низ на проследяването

    /* Предифинируем */
    function __toString();                       // форматиран низ за показване
}
?>

Ако даден клас наследява базовия клас за изключения и повторно дефинира конструктора, е силно препоръчително да се извика също и parent::__construct(), за да се осигури правилното присвояване на всички налични данни. Методът __toString() може да бъде предефиниран, за да се предостави поръчков изход, когато обектът е представен като низ.

Пример 20-3. Наследяване на класа за изключения

<?php
/**
 * Поръчково дефиниране на клас за изключения
 */
class MyException extends Exception
{
    // Повторно дефиниране на изключението, така че низът да бъде задължителен
    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;
        }
    }
}


// Пример 1
try {
    $o = new TestException(TestException::THROW_CUSTOM);
} catch (MyException $e) {      // Ще бъде хванато
    echo "Хванато е моето изключение\n", $e;
    $e->customFunction();
} catch (Exception $e) {        // Skipped
    echo "Хванато е изключението по подразбиране\n", $e;
}

// Продължаване на изпълнението
var_dump($o);
echo "\n\n";


// Пример 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";


// Пример 3
try {
    $o = new TestException(TestException::THROW_CUSTOM);
} catch (Exception $e) {        // Ще бъде хванато
    echo "Хванато е изключението по подразбиране\n", $e;
}

// Продължаване на изпълнението
var_dump($o);
echo "\n\n";


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

// Продължаване на изпълнението
var_dump($o);
echo "\n\n";
?>