Try catch php обработка ошибок

Исключения

Содержание

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

В PHP реализована модель исключений, аналогичная тем, что используются в других языках программирования.
Исключение в PHP может быть выброшено (throw) и поймано (catch).
Код может быть заключён в блок try, чтобы облегчить обработку потенциальных исключений.
У каждого блока try должен быть как минимум один соответствующий блок catch или finally.

Если выброшено исключение, а в текущей области видимости функции нет блока catch,
исключение будет «подниматься» по стеку вызовов к вызывающей функции, пока не найдёт подходящий блок catch.
Все блоки finally, которые встретятся на этом пути, будут выполнены.
Если стек вызовов разворачивается до глобальной области видимости, не встречая подходящего блока catch,
программа завершается с неисправимой ошибкой, если не был установлен глобальный обработчик исключений.

Выброшенный объект должен наследовать (instanceof) интерфейс Throwable.
Попытка выбросить объект, который таковым не является, приведёт к неисправимой ошибке PHP.

Начиная с PHP 8.0.0, ключевое слово throw является выражением и может быть использовано
в любом контексте выражения. В предыдущих версиях оно было утверждением
и должно было располагаться в отдельной строке.

catch

Блок catch определяет, как реагировать на выброшенное исключение.
Блок catch определяет один или несколько типов исключений или ошибок, которые он может обработать,
и, по желанию, переменную, которой можно присвоить исключение
(указание переменной было обязательно до версии PHP 8.0.0).
Первый блок catch, с которым столкнётся выброшенное исключение или ошибка
и соответствует типу выброшенного объекта, обработает объект.

Несколько блоков catch могут быть использованы для перехвата различных классов исключений.
Нормальное выполнение (когда исключение не выброшено в блоке try)
будет продолжаться после последнего блока catch, определённого в последовательности.
Исключения могут быть выброшены (throw) (или повторно выброшены) внутри блока catch.
В противном случае выполнение будет продолжено после блока catch, который был вызван.

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

Начиная с версии PHP 7.1.0, в блоке catch можно указывать несколько исключений,
используя символ |. Это полезно, когда разные исключения
из разных иерархий классов обрабатываются одинаково.

Начиная с версии PHP 8.0.0, имя переменной для пойманного исключения является необязательным.
Если оно не указано, блок catch будет выполнен,
но не будет иметь доступа к выброшенному объекту.

finally

Блок finally также может быть указан после или вместо блоков catch.
Код в блоке finally всегда будет выполняться после блоков try и catch,
независимо от того, было ли выброшено исключение
и до возобновления нормального выполнения.

Одно из заметных взаимодействий происходит между блоком finally и оператором return.
Если оператор return встречается внутри блоков try или catch, блок finally
всё равно будет выполнен. Более того, оператор return выполнится, когда встретится,
но результат будет возвращён после выполнения блока finally.
Кроме того, если блок finally также содержит оператор return,
возвращается значение из блока finally.

Глобальный обработчик исключений

Если исключению разрешено распространяться на глобальную область видимости,
оно может быть перехвачено глобальным обработчиком исключений, если он установлен.
Функция set_exception_handler() может задать функцию,
которая будет вызвана вместо блока catch, если не будет вызван никакой другой блок.
Эффект по сути такой же, как если бы вся программа была обёрнута в блок trycatch
с этой функцией в качестве catch.

Примечания

Замечание:

Внутренние функции PHP в основном используют отчёт об ошибках,
только современные объектно-ориентированные модули используют исключения.
Однако ошибки можно легко перевести в исключения с помощью класса 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');
?>

Примеры

Пример #2 Выбрасывание исключения


<?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 "Привет, мирn";
?>

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

0.2
Выброшено исключение: Деление на ноль.
Привет, мир

Пример #3 Обработка исключений с помощью блока finally


<?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 "Привет, мирn";
?>

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

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

Пример #4 Взаимодействие между блоками finally и return


<?phpfunction test() {
try {
throw new
Exception('foo');
} catch (
Exception $e) {
return
'catch';
} finally {
return
'finally';
}
}

echo

test();
?>

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

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


<?phpclass 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();?>

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

Пример #6 Обработка нескольких исключений в одном блоке catch


<?phpclass 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();?>

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

Пример #7 Пример блока catch без указания переменной

Допустимо начиная с PHP 8.0.0


<?phpclass SpecificException extends Exception {}

function

test() {
throw new
SpecificException('Ой!');
}

try {

test();
} catch (
SpecificException) {
print
"Было поймано исключение SpecificException, но нам безразлично, что у него внутри.";
}
?>

Пример #8 Throw как выражение

Допустимо начиная с PHP 8.0.0


<?phpfunction test() {
do_something_risky() or throw new Exception('Всё сломалось');
}

try {

test();
} catch (
Exception $e) {
print
$e->getMessage();
}
?>

ask at nilpo dot com

14 years ago


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;                             // Unknownpublic 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:xampphtdocsCustomExceptionCustomException.php(31)
#0 C:xampphtdocsCustomExceptionExceptionTest.php(19): CustomException->__construct()
#1 C:xampphtdocsCustomExceptionExceptionTest.php(43): exceptionTest()
#2 {main}


Johan

12 years ago


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());
}
?>


christof+php[AT]insypro.com

5 years ago


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();
    }
?>


lscorionjs at gmail dot com

4 months ago


<?phptry {
 
$str = 'hi';
  throw new
Exception();
} catch (
Exception) {
 
var_dump($str);
} finally {
 
var_dump($str);
}
?>

Output:
string(2) "hi"
string(2) "hi"

Shot (Piotr Szotkowski)

14 years ago


‘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).


Simo

8 years ago


#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.

daviddlowe dot flimm at gmail dot com

5 years ago


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 new
Error( "foobar" );
   
// or:
    // throw new Exception( "foobar" );
}
catch (
Throwable $e) {
   
var_export( $e );
}
?>


Edu

9 years ago


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 inn";
                throw
$e;
        } finally {
                echo
$e->getMessage()." finally n";
                throw new
Exception("Bye");
        }
} catch (
Exception $e) {
        echo
$e->getMessage()." catch outn";
}
?>

The output is:

Hello catch in
Hello finally
Bye catch out


mlaopane at gmail dot com

5 years ago


<?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 !"

telefoontoestel at nospam dot org

8 years ago


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
?>


Tom Polomsk

8 years ago


Contrary to the documentation it is possible in PHP 5.5 and higher use only try-finally blocks without any catch block.

Sawsan

11 years ago


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 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/>";
   }
?>


ilia-yats at ukr dot net

5 months ago


Note some undocumented details about exceptions thrown from 'finally' blocks.

When exception is thrown from 'finally' block, it overrides the original not-caught (or re-thrown) exception. So the behavior is similar to 'return': value returned from 'finally' overrides the one returned earlier. And the original exception is automatically appended to the exceptions chain, i.e. becomes 'previous' for the new one. Example:
<?php
try {
    try {
        throw new
Exception('thrown from try');
    } finally {
        throw new
Exception('thrown from finally');
    }
} catch(
Exception $e) {
    echo
$e->getMessage();
    echo
PHP_EOL;
    echo
$e->getPrevious()->getMessage();
}
// will output:
// thrown from finally
// thrown from try
?>

Example with re-throwing:
<?php
try {
    try {
        throw new
Exception('thrown from try');
    } catch (
Exception $e) {
        throw new
Exception('thrown from catch');
    } finally {
        throw new
Exception('thrown from finally');
    }
} catch(
Exception $e) {
    echo
$e->getMessage();
    echo
PHP_EOL;
    echo
$e->getPrevious()->getMessage();
}
// will output:
// thrown from finally
// thrown from catch
?>

The same happens even if explicitly pass null as previous exception:
<?php
try {
    try {
        throw new
Exception('thrown from try');
    } finally {
        throw new
Exception('thrown from finally', null, null);
    }
} catch(
Exception $e) {
    echo
$e->getMessage();
    echo
PHP_EOL;
    echo
$e->getPrevious()->getMessage();
}
// will output:
// thrown from finally
// thrown from try
?>

Also it is possible to pass previous exception explicitly, the 'original' one will be still appended to the chain, e.g.:
<?php
try {
    try {
        throw new
Exception('thrown from try');
    } finally {
        throw new
Exception(
           
'thrown from finally',
           
null,
            new
Exception('Explicitly set previous!')
        );
    }
} catch(
Exception $e) {
    echo
$e->getMessage();
    echo
PHP_EOL;
    echo
$e->getPrevious()->getMessage();
    echo
PHP_EOL;
    echo
$e->getPrevious()->getPrevious()->getMessage();
}
// will output:
// thrown from finally
// Explicitly set previous!
// thrown from try
?>

This seems to be true for versions 5.6-8.2.


Daan

1 year ago


I would like to emphasise that you can not rethrow an Exception inside a catch-block and expect that the next catch-block will handle it.

<?php try {
    throw new
RuntimeException('error');           
} catch (
RuntimeException $e) {
    throw
$e;
} catch (
Exception $e) {
   
// this will not be executed[
}
?>


Russian (Pусский) translation by Anna Goorikova (you can also view the original English article)

В этом посте вы узнаете, как использовать обработку исключений в PHP. Начиная с PHP 5, мы можем использовать блоки try catch для обработки ошибок — это лучший способ обработки исключений и управления потоком вашего приложения. В этой статье мы рассмотрим основы обработки исключений вместе с несколькими примерами из реального мира.

Что такое исключение?

В PHP 5 появилась новая модель ошибок, которая позволяет вам кидать и ловить исключения в вашем приложении — это лучший способ обработки ошибок, чем то, что мы имели в более старых версиях PHP. Все исключения являются экземплярами базового класса Exception, который мы можем расширить, чтобы ввести наши собственные пользовательские исключения.

Здесь важно отметить, что обработка исключений отличается от обработки ошибок. При обработке ошибок мы можем использовать функцию set_error_handler для установки нашей настраиваемой функции обработки ошибок, чтобы всякий раз, когда срабатывает ошибка, она вызывала нашу функцию обработки ошибок. Таким образом, вы можете управлять ошибками. Однако, как правило, некоторые виды ошибок не восстанавливаются и прекращают выполнение программы.

С другой стороны, исключения — это что-то, что умышленно вызывает код, и ожидается, что он будет пойман в какой-то момент вашего приложения. Таким образом, мы можем сказать, что исключения восстанавливаются, а не определенные ошибки, которые не подлежат восстановлению. Если исключение, которое выбрасывается, попадает где-то в ваше приложение, выполнение программы продолжается с момента, когда исключение было поймано. А исключение, которое не попадает нигде в ваше приложение, приводит к ошибке, которое останавливает выполнение программы.

Поток управления обработкой исключений

Давайте рассмотрим следующую диаграмму, которая показывает общий поток управления обработкой исключений.

Exception handling in a try catch finally blockException handling in a try catch finally blockException handling in a try catch finally block

Исключения могут быть выброшены и пойманы с помощью блоков try и catch. Вы несете ответственность за выброс исключений, если что-то произойдет, чего не ожидается. Давайте быстро рассмотрим основной поток обработки исключений, как показано в следующем псевдокоде.

1
// code before the try-catch block

2

3
try {
4
  // code

5

6
  // if something is not as expected

7
      // throw exception using the "throw" keyword

8

9
  // code, it won't be executed if the above exception is thrown

10
} catch (Exception $e) {
11
  // exception is raised and it'll be handled here

12
  // $e->getMessage() contains the error message

13
}
14

15
// code after the try-catch block, will always be executed

В большинстве случаев, когда вы имеете дело с исключениями, вы в конечном итоге используете шаблон, как показано в приведенном выше фрагменте. Вы также можете использовать блок finally вместе с блоками try и catch, но мы вернемся к этому позже в этой статье.

Блок try — тот, который используется, когда вы подозреваете, что ваш код может генерировать исключение. Вы всегда должны обертывать такой код, используя try и catch.

Выброс исключения

Исключение может быть вызвано функцией, которую вы вызываете, или вы можете использовать ключевое слово throw для выбрасывания исключения вручную. Например, вы можете проверить некоторый ввод перед выполнением любой операции и выбросить исключение, если данные недействительны.

Здесь важно отметить, что если вы выбросите исключение, но вы не определили блок catch, который должен обрабатывать это исключение, это приведет к фатальной ошибке. Поэтому вам нужно убедиться, что вы всегда определяете блок catch, если вы бросаете исключения в своем приложении.

Когда исключение попадает в блок catch, объект Exception содержит сообщение об ошибке, которое было выбрано с использованием ключевого слова throw. Переменная $e в приведенном выше примере является экземпляром класса Exception, поэтому она имеет доступ ко всем методам этого класса. В этом блоке вы должны определить свою собственную логику обработки исключений — что именно вы хотите сделать с ошибкой, которую вы поймаете.

В следующем разделе мы рассмотрим пример из реального мира, чтобы понять, как работает обработка исключений.

Пример из реального мира

В этом разделе мы построим реальный пример для демонстрации обработки исключений в PHP.

Предположим, что вы создали приложение, которое загружает конфигурацию приложения из файла config.php. Теперь важно, чтобы файл config.php присутствовал, когда ваше приложение загружается. Таким образом, ваше приложение не может работать, если файл config.php отсутствует. Так что это идеальный случай, чтобы выбросить исключение и сообщить пользователю, что им необходимо исправить проблему.

1
<?php
2
try {
3
    // init bootstrapping phase

4

5
    $config_file_path = "config.php";
6

7
    if (!file_exists($config_file_path))
8
    {
9
      throw new Exception("Configuration file not found.");
10
    }
11
 
12
    // continue execution of the bootstrapping phase

13
} catch (Exception $e) {
14
    echo $e->getMessage();
15
    die();
16
}
17
?>

Как вы можете видеть в приведенном выше примере, мы проверяем наличие файла config.php в начале фазы начальной загрузки. Если файл config.php найден, выполнение продолжается в обычном режиме. С другой стороны, мы выбросим исключение, если файл config.php не существует. Кроме того, мы хотели бы прекратить выполнение, если есть исключение!

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

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

Как создавать пользовательские исключения

В этом разделе мы обсудим, как вы можете создавать пользовательские исключения в своих приложениях. Фактически, мы расширим пример, который мы только что обсуждали в предыдущем разделе, чтобы продемонстрировать пользовательские исключения.

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

Перейдем к предыдущему примеру, как показано в следующем фрагменте.

1
<?php
2
class ConfigFileNotFoundException extends Exception {}
3

4
try {
5
    // init bootstrapping phase

6

7
    $config_file_path = "config.php";
8

9
    if (!file_exists($config_file_path))
10
    {
11
      throw new ConfigFileNotFoundException("Configuration file not found.");
12
    }
13
 
14
    // continue execution of the bootstrapping phase

15
} catch (ConfigFileNotFoundException $e) {
16
    echo "ConfigFileNotFoundException: ".$e->getMessage();
17
    // other additional actions that you want to carry out for this exception

18
    die();
19
} catch (Exception $e) {
20
    echo $e->getMessage();
21
    die();
22
}
23
?>

Во-первых, мы определили класс ConfigFileNotFoundException, который расширяет основной класс Exception. Теперь он становится нашим настраиваемым классом исключений, и мы можем использовать его, когда хотим выбросить исключение ConfigFileNotFoundException в нашем приложении.

Затем мы использовали ключевое слово throw для исключения исключений ConfigFileNotFoundException в случае, если файл config.php не существует. Однако важное различие находится в блоке catch. Как вы можете видеть, мы определили два блока catch, и каждый блок используется для обнаружения различного типа исключения.

Первый получает исключения типа ConfigFileNotFoundException. Итак, если генерируемое исключение относится к типу ConfigFileNotFoundException, этот блок будет выполнен. Если тип исключения не соответствует какому-либо конкретному блоку catch, он будет соответствовать последнему, который должен поймать все генерические сообщения об исключениях.

Блок Finally

В этом разделе мы рассмотрим, как вы можете использовать ключевое слово finally вместе с блоками try и catch. Иногда вы хотите выполнить часть кода независимо от того, было ли исключено исключение. Вот где вы можете использовать блок finally, поскольку код, который вы размещаете в блоке finally, всегда будет выполняться после выполнения блоков try и catch независимо от того, было ли выбрано исключение.

Попробуем понять это, используя следующий пример.

1
try {
2
  // code

3

4
  // if something is not as expected

5
      // throw exception using the "throw" keyword

6

7
  // code, it won't be executed if the above exception is thrown

8
} catch (Exception $e) {
9
  // exception is raised and it'll be handled here

10
  // $e->getMessage() contains the error message

11
} finally {
12
  // code, it'll always be executed

13
}

Код в приведенном выше примере почти то же самое с единственным исключением, что мы добавили блок finally после блока catch. И, как мы обсуждали, код в этом блоке всегда будет выполняться.

Типичные прецеденты, которые мы могли бы придумать для использования блока finally, обычно связаны с очисткой ресурсов. Например, если вы открыли соединение с базой данных или файл на диске в блоке try, вы можете выполнять задачи очистки, такие как закрытие соединения в блоке finally, так как это гарантировано.

Обработка исключений — это ключевой навык кодирования, и вы должны подумать над тем, как будут обрабатываться исключения при разработке ваших приложений. Это поможет вам обнаружить и восстановить непредвиденные ошибки в вашем приложении. Надеюсь, что этот пост вдохновит вас написать лучший код обработки ошибок!

Заключение

Сегодня мы обсудили тему обработки исключений в PHP. В первой половине статьи мы обсудили основы исключений в PHP и создали реальный пример, чтобы продемонстрировать, как они работают. В конце мы рассмотрели, как вы можете создавать пользовательские исключения, расширяя основной класс Exception.

Обработка исключений

Конструкция try catch finally

Последнее обновление: 24.03.2021

В процессе работы программы могут возникать различные ошибки, которые могут прервать работу программы. Например, рассмотрим следующую ситуацию:

$a = 5;
$b = 0;
$result = $a / $b;
echo $result;
echo "Конец работы программы";

Программа выводит результат деления. Поскольку делитель равен 0, а на ноль делить нельзя, то при выполнении деления программа завершится, и в браузере мы увидим
что-то типа следующего:

Fatal error: Uncaught DivisionByZeroError: Division by zero in D:localhosthello.php:11 Stack trace: #0 {main} thrown in D:localhosthello.php on line 11

Браузер отобразит нам произошедшую ошибку, причем дальше после строки с делением программа даже не будет выполняться.

Кто-то может сказать, что ситуация искуственная, так как мы сами определили делитель равный нулю. Но данные могут передаваться извне. Кроме того, кроме деления на ноль есть различные
ситуации, при которых могут происходить ошибки. Но PHP предоставляет ряд возможностей для обработки подобных ситуаций.

Для обработки исключений в PHP применяется конструкция try-catch:

try
{
	// код, который может вызвать исключение
}
catch(Тип_исключения $ex)
{
	// обработка исключения
}

Эта конструкция в общем варианте состоит из двух блоков — try и catch. В блок try помещается код, который потенциально может вызвать исключение.
А в блоке catch помещается обработка возникшего исключения. Причем каждого типа исключения мы можем определить свою логику обработки. Конкретный тип исключения,
который мы хотим обработать, указывается в круглых скобках после оператора catch:

catch(Тип_исключения $ex)

После названия типа указывается переменная этого типа (в данном случае $ex), которая будет хранить информацию об исключении и которую мы можем использовать
при обработке исключения.

Если в блоке try при выполнении кода возникает ошибка, то блок try прекращает выполнение и передает управление блоку catch, который обрабатывает ошибку.
А после завершения выполнения кода в блоке catch программа продолжает выполнять инструкции, которые размещены после блока catch.

Если в блоке try при выполнении кода не возникает ошибок, то блок catch не выполняется, а после завершения блока try
программа продолжает выполнять инструкции, которые размещены после блока catch.

Например, обработаем ошибку с делением на ноль:

try
{
	// код, который может вызвать исключение
	$a = 5;
	$b = 0;
	$result = $a / $b;
	echo $result;
}
catch(DivisionByZeroError $ex)
{
	// обработка исключения
	echo "Произошло исключение:<br>";
	echo $ex . "<br>";
}
echo "Конец работы программы";

В данном случае код деления на ноль, поскольку он может потенциально вызвать ошибку, помещен в блок try.

В блоке catch обрабатывается ошибка типа DivisionByZeroError, которая генерируется при делении на ноль. Вся обработка сводится
к выводу информации на экран.

В итоге при выполнении программа выведет следующее:

Произошло исключение:
DivisionByZeroError: Division by zero in D:localhosthello.php:14 Stack trace: #0 {main}
Конец работы программы

Как видно из вывода программы, она не завершается аварийно при делении на ноль, а продолжает работу.

Типы ошибок и исключений

В PHP для разных ситуаций есть множество типов, которые описывают ошибки. Все эти встроенные типы применяют интерфейс Throwable:

Ошибки и исключения Error, Exception и Throwable в PHP

Все типы делятся на две группы: собственно ошибки (класс Error) и собственно исключения (класс Exception).
А от классов Error и Exception наследуются классы ошибок и исключений, которые описывают конкретные ситуации. Например, от класса
Error наследуется класс ArithmeticError, который описывает ошибки, возникающие при выполнении арифметических операций.
А от класса ArithmeticError наследуется класс DivisionByZeroError, который представляют ошибку при делении на ноль.

Блок catch

Конструкция try..catch позволяет определить несколько блоков catch — для обработки различных типов ошибок и исключений:

try
{
	$result = 5 / 0;
	echo $result;
}
catch(ParseError $p)
{
    echo "Произошла ошибка парсинга";
}
catch(DivisionByZeroError $d)
{
	echo "На ноль делить нельзя";
}

При возникновении ошибки будет для ее обработки будет выбираться тот блок catch, который соответствует вошникшей ошибки. Так, в данном случае
при делении на ноль будет выполняться второй блок catch.

Если бы в блоке try возникла бы ошибка, которая бы не соответствовала типам из блоков catch (в данном случае — типам DivisionByZeroError и ParseError),
то такая ошибка не была бы обработана, и соответственно программа бы аварийно завершила свое выполнение.

Блоки catch с более конкретными типами ошибок и исключений должны идти в начале, а более с более общими типа — в конце:

try
{
	$result = 5 / 0;
	echo $result;
}
catch(DivisionByZeroError $ex)
{
	echo "На ноль делить нельзя";
}
catch(ArithmeticError $ex)
{
	echo "Ошибка при выполнении арифметической операции";
}
catch(Error $ex)
{
	echo "Произошла ошибка";
}
catch(Throwable $ex)
{
	echo "Ошибка при выполнении программы";
}

Класс DivisionByZeroError унаследован от ArithmeticError, который, в свою очередь, унаследован от Error, реализующего интерфейс Throwable. Поэтому
класс DivisionByZeroError представляет более конкретный тип и представляемые им ошибки должны обрабатываться в первую очередь. А тип Throwable представляет
наиболее общий тип, так как ему соответствуют все возможные ошибки и исключения, поэтому блоки catch с таким типом должны идти в конце.

В данном случае опять же в блоке try происходит ошибка деления на ноль. Но этой ошибке соответствуют все четыре блока catch. Для обработки PHP будет
выбирать первый попавшийся, который соответствует типу ошибки. В данном случае это блок для обработки ошибки типа DivisionByZeroError.

Если нам надо обрабатывать в принципе все ошибки и исключения, то мы можем определить только обработку общего для всех них типа Throwable:

try
{
	$result = 5 / 0;
	echo $result;
}
catch(Throwable $ex)
{
	echo "Ошибка при выполнении программы";
}

Начиная с версии PHP 8.0 в блоке catch можно просто указать тип обрабатываемого исключения, не определяя переменную:

catch(DivisionByZeroError)
{
	echo "Произошло исключение: деление на ноль";
}

Получение информации об ошибках и исключениях

Интерфейс Throwable предоставляет ряд методов, которые позволяют получить некоторую информацию о возникшем исключении:

  • getMessage(): возвращает сообщение об ошибке

  • getCode(): возвращает код исключения

  • getFile(): возвращает название файла, в котором возникла ошибка

  • getLine(): возвращает номер строки, в которой возникла ошибка

  • getTrace(): возвращает трассировку стека

  • getTraceAsString(): возвращает трассировку стека в виде строки

Применим некоторые из этих методов:

try
{
	$result = 5 / 0;
	echo $result;
}
catch(DivisionByZeroError $ex)
{
	echo "Сообщение об ошибке: " . $ex->getMessage() . "<br>";
	echo "Файл: " . $ex->getFile() . "<br>";
	echo "Номер строки: " . $ex->getLine() . "<br>";
}

Результат работы:

Сообщение об ошибке: Division by zero
Файл: D:localhosthello.php
Номер строки: 11

Блок finally

Конструкция try..catch также может определять блок finally. Этот блок выполняется в конце — после блока try и catch
вне зависимости, возникла или нет ошибка. Нередко блок finally используется для закрытия ресурсов, которые применяются в блоке try.

try
{
	$result = 5 / 0;
	echo $result . "<br>";
}
catch(Throwable $ex)
{
	echo "Ошибка при выполнении программы<br>";
}
finally
{
	echo "Блок finally<br>";
}
echo "Конец работы программы";

Вывод программы:

Ошибка при выполнении программы
Блок finally
Конец работы программы

Конструкция try..catch..finally может содержать либо все три блока, либо только два блока try и либо блок catch,
либо блок finally.

Хотя PHP уже давно поддерживает обработку исключений, однако, по сравнению с Java эта поддержка была довольно слабой

Первоначальная поддержка обработки исключений была введена в язык с 5 версии PHP, с двумя простыми встроенными классами исключений — Exception и ErrorException, с поддержкой дополнительных классов через SPL. Идея этого поста состоит в том, чтобы представить читателям современные возможности обработки исключений PHP. 

Новый интерфейс

Хотя PHP 7 предоставляет классы Error и Exception, давайте сначала затронем интерфейс Throwable . И Error и Exception классы реализуют Throwable интерфейс — это основа для любого объекта , который может быть брошен с помощью оператора throw. Единственное, что он не может быть реализован непосредственно в классах пользовательского пространства, только через расширение класса Exception. Кроме того, он обеспечивает единую точку для отлова обоих типов ошибок в одном выражении:

<?php

try {
// ваш код
} catch (Throwable $e) {
echo 'Очень хороший способ отловить исключения и ошибки';
}

Список доступных встроенных классов исключений начиная с PHP 7.4:

  • Exception
  • ErrorException
  • Error
  • ArgumentCountError
  • ArithmeticError
  • AssertionError
  • DivisionByZeroError
  • CompileError
  • ParseError
  • TypeError

Дополнительные классы исключений можно найти в стандартной библиотеке PHP . И наиболее заметным из расширений JSON является класс JsonException.

THROWABLE

Интерфейс Throwable  PHP 7:

interface Throwable
{
public function getMessage(): string; // Error reason
public function getCode(): int; // Error code
public function getFile(): string; // Error begin file
public function getLine(): int; // Error begin line
public function getTrace(): array; // Return stack trace as array like debug_backtrace()
public function getTraceAsString(): string; // Return stack trace as string
public function getPrevious(): Throwable; // Return previous `Trowable`
public function __toString(): string; // Convert into string
}

Вот иерархия Throwable:

interface Throwable
|- Error implements Throwable
|- ArithmeticError extends Error
|- DivisionByZeroError extends ArithmeticError
|- AssertionError extends Error
|- ParseError extends Error
|- TypeError extends Error
|- ArgumentCountError extends TypeError
|- Exception implements Throwable
|- ClosedGeneratorException extends Exception
|- DOMException extends Exception
|- ErrorException extends Exception
|- IntlException extends Exception
|- LogicException extends Exception
|- BadFunctionCallException extends LogicException
|- BadMethodCallException extends BadFunctionCallException
|- DomainException extends LogicException
|- InvalidArgumentException extends LogicException
|- LengthException extends LogicException
|- OutOfRangeException extends LogicException
|- PharException extends Exception
|- ReflectionException extends Exception
|- RuntimeException extends Exception
|- OutOfBoundsException extends RuntimeException
|- OverflowException extends RuntimeException
|- PDOException extends RuntimeException
|- RangeException extends RuntimeException
|- UnderflowException extends RuntimeException
|- UnexpectedValueException extends RuntimeException

Ошибка, почему?

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

Проблема заключалась в том, что было несколько фатальных ошибок, которые не могли быть обработаны определяемым пользователем обработчиком ошибок. Это означало, что вы не могли корректно обрабатывать фатальные ошибки в PHP. Было несколько побочных эффектов, которые были проблематичными, такие как потеря контекста времени выполнения, деструкторы не вызывались, да и вообще иметь дело с ними было неудобно. В PHP 7 фатальные ошибки теперь являются исключениями, и мы можем легко их обработать. Фатальные ошибки приводят к возникновению исключений. Вам необходимо обрабатывать нефатальные ошибки с помощью функции обработки ошибок.

Вот пример ловли фатальной ошибки в PHP 7.1. Обратите внимание, что нефатальная ошибка не обнаружена.

<?php 

try {
// будет генерировать уведомление, которое не будет поймано
echo $someNotSetVariable;
// фатальная ошибка, которая сейчас на самом деле ловится
someNoneExistentFunction();
} catch (Error $e) {
echo "Error caught: " . $e->getMessage();
}

Этот скрипт выведет сообщение об ошибке при попытке доступа к недопустимой переменной. Попытка вызвать функцию, которая не существует, приведет к фатальной ошибке в более ранних версиях PHP, но в PHP 7.1 вы можете ее перехватить. Вот вывод для скрипта:

Notice: Undefined variable: someNotSetVariable on line 3
Error caught: Call to undefined function someNoneExistentFunction()

Константы ошибок

В PHP много констант, которые используются в отношении ошибок. Эти константы используются при настройке PHP для скрытия или отображения ошибок определенных классов.

Вот некоторые из наиболее часто встречающихся кодов ошибок:

  • E_DEPRECATED — интерпретатор сгенерирует этот тип предупреждений, если вы используете устаревшую языковую функцию. Сценарий обязательно продолжит работать без ошибок.
  • E_STRICT — аналогично E_DEPRECATED, — указывает на то, что вы используете языковую функцию, которая не является стандартной в настоящее время и может не работать в будущем. Сценарий будет продолжать работать без каких-либо ошибок.
  • E_PARSE — ваш синтаксис не может быть проанализирован, поэтому ваш скрипт не запустится. Выполнение скрипта даже не запустится.
  • E_NOTICE — движок просто выведет информационное сообщение. Выполнение скрипта не прервется, и ни одна из ошибок не будет выдана.
  • E_ERROR — скрипт не может продолжить работу, и завершится. Выдает ошибки, а как они будут обрабатываться, зависит от обработчика ошибок.
  • E_RECOVERABLE_ERROR — указывает на то, что, возможно, произошла опасная ошибка, и движок работает в нестабильном состоянии. Дальнейшее выполнение зависит от обработчика ошибок, и ошибка обязательно будет выдана.

Полный список констант можно найти в руководстве по PHP.

Функция обработчика ошибок

Функция set_error_handler() используется, чтобы сообщить PHP как обрабатывать стандартные ошибки, которые не являются экземплярами класса исключений Error. Вы не можете использовать функцию обработчика ошибок для фатальных ошибок. Исключения ошибок должны обрабатываться с помощью операторов try/catch. set_error_handler() принимает callback функцию в качестве своего параметра. Callback-функции в PHP могут быть заданы двумя способами: либо строкой, обозначающей имя функции, либо передачей массива, который содержит объект и имя метода (именно в этом порядке). Вы можете указать защищенные и приватные методы для callable в объекте. Вы также можете передать значение null, чтобы указать PHP вернуться к использованию стандартного механизма обработки ошибок. Если ваш обработчик ошибок не завершает программу и возвращает результат, ваш сценарий будет продолжать выполняться со строки, следующей за той, где произошла ошибка.

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

Вот пример:

<?php

function myCustomErrorHandler(int $errNo, string $errMsg, string $file, int $line) {
echo "Ух ты, мой обработчик ошибок получил #[$errNo] в [$file] на [$line]: [$errMsg]";
}

set_error_handler('myCustomErrorHandler');

try {
why;
} catch (Throwable $e) {
echo 'И моя ошибка: ' . $e->getMessage();
}

Если вы запустите этот код в PHP-консоли php -a, вы должны получить похожий вывод:

Error #[2] occurred in [php shell code] at line [3]: [Use of undefined constant why - assumed 'why' (this will throw an Error in a future version of PHP)]

Самые известные PHP библиотеки , которые делают обширное использование РНР set_error_handler() и могут сделать хорошие представления исключений и ошибок являются whoops,  и Symony Debug, ErrorHandler компоненты. Я смело рекомендую использовать один из них. Если не собираетесь их использовать в своем проекте, то вы всегда можете черпать вдохновение из их кода. В то время как компонент Debug широко используется в экосистеме Symfony, Whoops остается библиотекой выбора для фреймворка Laravel . 

Для подробного и расширенного использования, пожалуйста, обратитесь к руководству по PHP для обработчика ошибок.

Отображение или подавление нефатальной ошибки

Когда ваше приложение выходит в продакшн, логично, что вы хотите скрыть все системные сообщения об ошибках во время работы, и ваш код должен работать без генерации предупреждений или сообщений. Если вы собираетесь показать сообщение об ошибке, убедитесь, что оно сгенерировано и не содержит информации, которая может помочь злоумышленнику проникнуть в вашу систему.

В вашей среде разработки вы хотите, чтобы все ошибки отображались, чтобы вы могли исправить все проблемы, с которыми они связаны, но в процессе работы вы хотите подавить любые системные сообщения, отправляемые пользователю.

Для этого вам нужно настроить PHP, используя следующие параметры в вашем файле php.ini:

  • display_errors – может быть установлен в false для подавления сообщений
  • log_errors – может использоваться для хранения сообщений об ошибках в файлах журнала
  • error_reporting – можно настроить, какие ошибки вызывают отчет

Лучше всего корректно обрабатывать ошибки в вашем приложении. В производственном процессе вы должны скорее регистрировать необработанные ошибки, чем разрешать их отображение пользователю. Функция error_log() может использоваться для отправки сообщения одной из определенных процедур обработки ошибок. Вы также можете использовать функцию error_log() для отправки электронных писем, но лично вы бы предпочли использовать хорошее решение для регистрации ошибок и получения уведомлений при возникновении ошибок, например Sentry или Rollbar .

Существует вещь, называемая оператором контроля ошибок ( @ ), который по своей сути может игнорировать и подавлять ошибки. Использование очень простое — просто добавьте любое выражение PHP с символом «собаки», и сгенерированная ошибка будет проигнорирована. Хотя использование этого оператора может показаться интересным, я призываю вас не делать этого. Мне нравится называть это живым пережитком прошлого.

Больше информации для всех функций, связанных с ошибками PHP, можно найти в руководстве.

Исключения (Exceptions)

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

Исключение будет возникать в стеке вызовов, если вы его не перехватите. Давайте посмотрим на простой пример:

try {
print "это наш блок попыток n";
throw new Exception();
} catch (Exception $e) {
print "что-то пошло не так, есть улов!";
} finally {
print "эта часть всегда выполняется";
}

PHP включает в себя несколько стандартных типов исключений, а стандартная библиотека PHP (SPL) включает в себя еще несколько. Хотя вам не нужно использовать эти исключения, это означает, что вы можете использовать более детальное обнаружение ошибок и отчеты. Классы Exception и Error реализуют интерфейс Throwable и, как и любые другие классы, могут быть расширены. Это позволяет вам создавать гибкие иерархии ошибок и адаптировать обработку исключений. Только класс, который реализует класс Throwable, может использоваться с ключевым словом throw. Другими словами, вы не можете объявить свой собственный базовый класс и затем выбросить его как исключение.

Надежный код может встретить ошибку и справиться с ней. Разумная обработка исключений повышает безопасность вашего приложения и облегчает ведение журнала и отладку. Управление ошибками в вашем приложении также позволит вам предложить своим пользователям лучший опыт. В этом разделе мы рассмотрим, как отлавливать и обрабатывать ошибки, возникающие в вашем коде.

Ловля исключений

Вы должны использовать try/catch структуру:

<?php

class MyCustomException extends Exception { }

function throwMyCustomException() {
throw new MyCustomException('Здесь что-то не так.');
}

try {
throwMyCustomException();
} catch (MyCustomException $e) {
echo "Ваше пользовательское исключение поймано";
echo $e->getMessage();
} catch (Exception $e) {
echo "Стандартное исключение PHP";
}

Как видите, есть два предложения catch. Исключения будут сопоставляться с предложениями сверху вниз, пока тип исключения не будет соответствовать предложению catch. Эта очень простая функция throwMyCustomException() генерирует исключение MyCustomException, и мы ожидаем, что оно будет перехвачено в первом блоке. Любые другие исключения, которые произойдут, будут перехвачены вторым блоком. Здесь мы вызываем метод getMessage() из базового класса Exception. Вы можете найти больше информации о дополнительном методе в Exception PHP docs.

Кроме того, можно указать несколько исключений, разделяя их трубой ( | ).

Давайте посмотрим на другой пример:

<?php

class MyCustomException extends Exception { }
class MyAnotherCustomException extends Exception { }

try {
throw new MyAnotherCustomException;
} catch (MyCustomException | MyAnotherCustomException $e) {
echo "Caught : " . get_class($e);
}

Этот очень простой блок catch будет перехватывать исключения типа MyCustomException и MyAnotherCustomException.

Немного более продвинутый сценарий:

// exceptions.php
use SymfonyComponentHttpKernelExceptionNotFoundHttpException;

try {
throw new NotFoundHttpException();
} catch (Exception $e) {
echo 1;
} catch (NotFoundHttpException $e) {
echo 2;
} catch (Exception $e) {
echo 3;
} finally {
echo 4;
}

Это ваш окончательный ответ?

В PHP 5.5 и более поздних, блок finally также может быть указан после или вместо блоков catch. Код внутри блока finally всегда будет выполняться после блоков try и catch независимо от того, было ли выброшено исключение, и до возобновления нормального выполнения. Одним из распространенных применений блока finally является закрытие соединения с базой данных, но, наконец, его можно использовать везде, где вы хотите, чтобы код всегда выполнялся.

<?php

class MyCustomException extends Exception { }

function throwMyCustomException() {
throw new MyCustomException('Здесь что-то не так');
}

try {
throwMyCustomException();
} catch (MyCustomException $e) {
echo "Ваше пользовательское исключение поймано ";
echo $e->getMessage();
} catch (Exception $e) {
echo "Стандартное исключение PHP";
} finally {
echo "Я всегда тут";
}

Вот хороший пример того, как работают операторы PHP catch/finally:

<?php

try {
try {
echo 'a-';
throw new exception();
echo 'b-';
} catch (Exception $e) {
echo 'пойманный-';
throw $e;
} finally {
echo 'завершенный-';
}
} catch (Exception $e) {
echo 'конец-';
}

Функция обработчика исключений

Любое исключение, которое не было обнаружено, приводит к фатальной ошибке. Если вы хотите изящно реагировать на исключения, которые не перехватываются в блоках перехвата, вам нужно установить функцию в качестве обработчика исключений по умолчанию.

Для этого вы используете функцию set_exception_handler() , которая принимает вызываемый элемент в качестве параметра. Ваш сценарий завершится после того, как вызов будет выполнен.

Функция restore_exception_handler() вернет обработчик исключений к его предыдущему значению.

<?php

class MyCustomException extends Exception { }

function exception_handler($exception) {
echo "Uncaught exception: " , $exception->getMessage(), "n";
}

set_exception_handler('exception_handler');

try {
throw new Exception('Uncaught Exception');
} catch (MyCustomException $e) {
echo "Ваше пользовательское исключение поймано ";
echo $e->getMessage();
} finally {
echo "Я всегда тут";
}

print "Не выполнено";

Здесь простая функция exception_handler будет выполняться после блока finally, когда ни один из типов исключений не был сопоставлен. Последний вывод никогда не будет выполнен.

Для получения дополнительной информации обратитесь к документации PHP. 

Старый добрый «T_PAAMAYIM_NEKUDOTAYIM»

Вероятно, это было самое известное сообщение об ошибке PHP. В последние годы было много споров по этому поводу. Вы можете прочитать больше в отличном сообщении в блоге от Фила Осетра .  

Сегодня я с гордостью могу сказать, что если вы запустите этот код с PHP 7, то сообщение о T_PAAMAYIM_NEKUDOTAYIM больше не будет:

<?php

class foo
{
static $bar = 'baz';
}

var_dump('foo'::$bar);

// Output PHP < 7.0:
// PHP Parse error: syntax error, unexpected '::' (T_PAAMAYIM_NEKUDOTAYIM) in php shell code on line 1
// Output PHP > 7.0:
// string(3) "baz"
?>

В заключении

Со времени первого внедрения обработки исключений в PHP прошло много лет, пока мы не получили гораздо более надежную и зрелую обработку исключений, чем в Java. Обработка ошибок в PHP 7 получила много внимания, что делает его хорошим, открывая пространство для будущих улучшений, если мы действительно с сегодняшней точки зрения нуждаемся в них.

Обработка исключений

Курс PHP для начинающих

  • PHP и MySQL
  • Основы PHP
  • Обработка исключений

Внимание! Данный курс устарел!
Переходите к новому курсу «PHP для начинающих».

До недавнего времени программистам приходилось проявлять незаурядную изобретательность в поиске способов устранения ситуаций возникновения ошибок в сценариях на языке PHP. Для этого они либо задавали и выводили строки с описанием ошибок, либо пытались воспользоваться всеми возможными средствами ограниченной системы регистрации ошибок. Несмотря на наличие других многочисленных полезных средств, интерпретатор PHP не поддерживал качественную систему, позволяющую успешно справляться с ситуациями возникновения ошибок. К счастью, с выходом версии PHP 5 ситуация изменилась в лучшую сторону.

Курс PHP для начинающих

Программисты, знакомые с такими языками структурного программирования, как C# и Java, по-видимому, давно привыкли использовать различные встроенные объекты, позволяющие справляться с ошибками и исключительными ситуациями. Таким специалистам будет приятно узнать, что теперь в версии PHP 5 впервые появился объект, предназначенный для обработки исключительных ситуаций, и что синтаксические конструкции для работы с этим объектом весьма напоминают существующие языки, такие как Java. В действительности даже после краткого знакомства с этими синтаксическими конструкциями разработчик может приступить к обработке ошибок и исключительных ситуаций в языке PHP во многом по такому же принципу, который применяется в других объектно-ориентированных языках.

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

Ошибки и исключительные ситуации

Приступая к изучению данной темы, необходимо прежде всего понять, что нарушение в работе программы представляет собой исключительную ситуацию, а не просто ошибку. Как следует из самого этого термина, исключительная ситуация представляет собой любое условие, наблюдаемое в ходе выполнения программы, которое либо является непредвиденным, либо действия, связанные с этим условием, не предусмотрены в рамках нормального выполнения программы. Вообще говоря, исключительную ситуацию следует рассматривать не как неисправимую ошибку, из-за которой должно остановиться выполнение программы, а как условие, которое может быть обнаружено, и предприняты действия, позволяющие продолжить работу должным образом.

Исключительные ситуации при их надлежащем использовании позволяют в значительной степени повысить надежность приложения и в основном избавиться от затруднений, связанных с отладкой. Но неправильно обрабатываемые или неверно определенные исключительные ситуации могут создать больше проблем, чем можно было бы устранить с их помощью, поскольку такие исключительные ситуации затрудняют обнаружение источника ошибки. Эта означает, что необходимо уделять достаточное внимание решению задачи правильного определения и осуществления принятого метода обработки исключительных ситуаций, поскольку это принесет значительную пользу.

Ниже приведен пример кода, который содержит некоторые средства обнаружения ошибок в том виде, в каком они могло быть реализованы в версии PHP 4 или в одной из предыдущих версий. В этом коде осуществляется выборка переменной POST, содержащей идентификатор пользователя. Такой идентификатор должен иметь длину по меньшей мере девять символов и начинаться с префикса «usr»:

Пример метода обработки ошибок без использования исключений

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Основы PHP</title>
</head>

<body>

<?php

// Выполнить выборку идентификатора пользователя, подлежащего проверке
$user_id = isset($_POST['user_id']) ? $_POST['user_id'] : '';

// Подготовить для вывода на дисплей сообщение, содержимое которого
// зависит от того, является ли действительным идентификатор пользователя
if (!empty($user_id))
	echo !is_valid_user($user_id) ? '<b style="color:red;">Некорректный логин</b><br>' : "Все правильно<br>";


function is_valid_user($user_id)
{	
	// Возвратить значение false, если идентификатор пользователя 
	// не начинается с подстроки "usr"
	$pre_str = "usr";
	if ((strpos($user_id, $pre_str) === false) || (strpos ($user_id, $pre_str) != 0))
		return false;
		
	// Возвратить значение false, если идентификатор пользователя 
	// не соответствует требованиям по длине 
	if ((strlen($user_id) < 9)) return false;
	
	return true;
}

?>

<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">
    <input type="text" name="user_id" placeholder="Введите логин"><br>
    <input type="submit" value="Отправить">
</form>

</body>
</html>

Вполне очевидно, что функция is_valid_user() может вернуть значение false вследствие несоблюдения любого из указанных требований. Применение исключений позволяет проще организовать выявление ошибок различных типов, после чего принимать соответствующие меры, учитывая характер каждой отдельной ошибки.

Класс Exception

В версии PHP 5 предусмотрен новый класс Exception, который может непосредственно использоваться в любом коде, выполняемом под управлением сервера PHP 5 или более поздней версии. В примере, приведенном выше, применялись булевы функции, а класс Exception позволяет создавать или активизировать в коде исключительные ситуации — экземпляры этого класса.

В следующем примере показано, какой вид может принять предыдущий пример после его корректировки в целях использования средств обработки исключительных ситуаций:

Код PHP

<?php

// Выполнить выборку идентификатора пользователя, подлежащего проверке
$user_id = isset($_POST['user_id']) ? $_POST['user_id'] : '';

// Подготовить для вывода на дисплей сообщение, содержимое которого
// зависит от того, является ли действительным идентификатор пользователя
try
{
	if (!empty($user_id) && is_valid_user($user_id))
		echo "Все правильно<br>";
}
catch (Exception $ex)
{
	$msg = $ex->getMessage();
	echo $msg;
}


function is_valid_user($user_id)
{	
	// Активизировать исключение, если идентификатор пользователя 
	// не начинается с подстроки "usr"
	$pre_str = "usr";
	if ((strpos($user_id, $pre_str) === false) || (strpos ($user_id, $pre_str) != 0))
		throw new Exception('<b style="color:red;">Некорректный логин - должен начинаться со строки "usr"</b><br>');
		
	// Активизировать исключение, если идентификатор пользователя 
	// не соответствует требованиям по длине 
	if ((strlen($user_id) < 9)) 
		throw new Exception('<b style="color:red;">Некорректный логин - длина меньше 9 символов</b><br>');
	
	return true;
}

?>

Использование исключений

В этом коде для активизации исключения применяется оператор throw. А сами средства обработки исключительных ситуаций в данном случае использовались для создания нового объекта Exception. Благодаря применению этих средств появляется возможность обрабатывать ошибки, нарушающие нормальный ход выполнения программы, как отдельные исключительные ситуации, поэтому ошибки больше не могут влиять на остальную часть приложения.

Перехват и обработка исключительных ситуаций осуществляется с помощью управляющей конструкции try/catch. Прежде всего необходимо включить любой код, выполнение которого может привести к возникновению ошибки или исключительной ситуации, в конструкцию try(). При активизации в этом коде любой исключительной ситуации выполнение блока try() прекращается; это означает, что оставшийся код в конструкции try() не выполняется. Затем блок catch() просматривается для поиска исключительной ситуации соответствующего типа, а обработка исключительной ситуации происходит с помощью кода, содержащегося в данном конкретном блоке catch. Благодаря такой организации работы может быть предусмотрен анализ различных условий с учетом типа активизированной исключительной ситуации, но не предпринимается безнадежная попытка справиться с какой-то общей, неконкретизированной ошибкой.

Активизация исключительной ситуации

Благодаря использованию исключительных ситуациях появляется еще одна удобная возможность — предоставлять пользователю настолько много (или настолько мало) информации об ошибке, насколько это целесообразно. В сочетании с объектом Exception допускается применение нескольких методов обработки исключительных ситуаций, которые могут использоваться для создания собственных сообщений об ошибках или организации правильных действий в сложившейся ситуации.

Ниже приведен код, который показывает, как можно активизировать и сразу же перехватить универсальную исключительную ситуацию, а затем применить методы объекта Exception для извлечения переданного сообщения, кода ошибки, имени файла и номера строки, ставшей источником ошибки:

Код PHP

try
{
	if (!empty($user_id) && is_valid_user($user_id))
		echo "Все правильно<br>";
}
catch (Exception $ex)
{
	// Входная строка, переданная объекту 
	$msg = $ex->getMessage(); 
	
	// Определяемый пользователем код ошибки 
	$code = $ex->getCode();
	
	// Имя файла, при обработке которого была активизирована 
	// исключительная ситуация 
	$file = $ex->getFile();
	
	// Номер строки, при обработке которой возникла исключительная ситуация 
	$line = $ex->getLine();
	
	echo "Ошибка в коде $code: $msg Файл: $file, номер строки: $line<br><br>";
}

Получение дополнительной информации об исключении

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

Определение собственных подклассов Exception

Язык PHP позволяет также определять собственные классы, которые наследуют методы класса Exception. Это означает, что можно больше не ограничиваться использованием лишь функции getMessage() для получения информации о том, какой именно тип имеет возникшая ошибка. Подклассы могут быть определены так, как показано в следующем примере:

Код PHP

class CustomException extends Exception {
	public function __construct($message) {
		parent::__construct($message);
	}
}

Рассмотрим пример, приведенный ниже, который демонстрирует способы использования специализированных исключительных ситуаций. Этот код предназначен для обеспечения взаимодействия с пользователями, подписывающимися на определенные услуги. Пользователи могут забыть включить обязательный префикс «usr» в свой идентификатор пользователя поэтому при отсутствии этого префикса можно предусмотреть возможность добавить его и снова проверить подлинность пользователя, а не прекращать выполнение программы:

Код PHP

<?php

// Определить специализированные классы исключений
class PrefixException extends Exception {
	public function __construct($message) {
		parent::__construct($message);
	}
}

class MaxLengthException extends Exception {
	public function __construct($message) {
		parent::__construct($message);
	}
}

// Выполнить выборку идентификатора пользователя, подлежащего проверке
$user_id = isset($_POST['user_id']) ? $_POST['user_id'] : '';

// Подготовить для вывода на дисплей сообщение, содержимое которого
// зависит от того, является ли действительным идентификатор пользователя
try
{
	if (!empty($user_id) && is_valid_user($user_id))
		echo "Все правильно<br>";
}
catch (PrefixException $ex)
{
	// Если префикс "usr" отсутствует, добавить его
	$user_id = "usr".$user_id;
	
	// Повторный вызов проверки
	try {
		if (is_valid_user($user_id))
			echo "Все правильно<br>";
	} catch (Exception $ex) { 
		echo $ex->getMessage();
	}
}
catch (MaxLengthException $ex)
{
	$msg = $ex->getMessage(); 
	echo $msg;
}


function is_valid_user($user_id)
{	
	// Активизировать исключение, если идентификатор пользователя 
	// не начинается с подстроки "usr"
	$pre_str = "usr";
	if ((strpos($user_id, $pre_str) === false) || (strpos ($user_id, $pre_str) != 0))
		throw new PrefixException('<b style="color:red;">Некорректный логин - должен начинаться со строки "usr"</b><br>');
		
	// Активизировать исключение, если идентификатор пользователя 
	// не соответствует требованиям по длине 
	if ((strlen($user_id) < 9)) 
		throw new MaxLengthException('<b style="color:red;">Некорректный логин - длина меньше 9 символов</b><br>');
	
	return true;
}

?>

Обратите внимание на то, что в этом примере предпринимается попытка возобновить работу после обнаружения условия ошибки «отсутствующий префикс». Вполне очевидно, что теперь существует возможность легко справляться с отдельными типами ошибок после того, как они будут определены отдельно в программе.

Ограничения средств обработки исключений языка PHP

Объект Exception — новое средство, появившееся в версии PHP 5, поэтому этот объект, как таковой, все еще находится на самых ранних этапах разработки. Ко времени написания этих строк язык PHP не поддерживал использование таких конструкций, как finally() и throws(), предусмотренных в языке Java и других языках. Кроме того, в отличие от других языков на исключительные ситуации еще не отображаются ошибки, распознаваемые самим интерпретатором PHP (в том числе ошибки, сообщения о которых обычно появляются в клиентском браузере). Из-за этого, например, возникновение ошибки в операторе SQL, выполняемом в блоке try/catch, не приводит автоматически к активизации исключительной ситуации, которую можно было бы перехватить и заняться ее обработкой. Такие удобные функциональные средства, по всей вероятности, должны быть включены в одну из будущих версий PHP, поэтому для авторов имеет смысл упомянуть о них, а для читателей — следить за их появлением.

Cookie-файлы и сеансы

Нарушения в работе системы PHP

Оценить статью:

Время на прочтение
4 мин

Количество просмотров 65K

В предыдущей статье я предложил свести все «механизмы ошибок» к исключениям, поэтому логично будет объяснить, как правильно работать с исключениями в PHP.
Сначала поясню, почему я выбрал именно исключения, как механизм работы с ошибками:

  1. Исключения — это гибкий, расширяемый метод обработки ошибок;
  2. Это стандартизованный механизм – человеку, не работавшему с вашим кодом, не нужно будет читать мануал, чтобы понять, как обрабатывать ошибки. Ему достаточно знать, как работают исключения;
  3. С исключениями гораздо проще находить источник ошибок, так как всегда есть стек вызовов (trace).

Сразу скажу, что в этой статье я не открываю Америку. Описаны стандартные принципы работы с исключениями плюс некоторые особенности, налагаемые PHP. Полезно будет почитать новичкам, хотя может быть и опытные разработчики найдут что-нибудь новое для себя.

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

class baseException extends Exception{}

и замените все строки в своем коде

throw new Exception();

на

throw new baseException();

Таким образом, все исключения вашего кода можно будет отличить от исключений не вашего кода.

2. Исключения должны быть иерархичны. У вас должен быть базовый класс исключений, от которого наследуются все исключения, бросаемые в вашем коде. Например, у вас в коде есть модуль для работы с файлами fileModule, объявите исключение, которое будет бросаться только этим модулем

class fileModuleException extends baseException{}

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

class fileNotFoundException extends fileModuleException{}
 

Соблюдая иерархичность, вы сможете различать исключения от разных модулей в вашем приложении. Я не призываю наплодить кучу исключений, для каждого модуля. Исключения должны проектироваться не от кода, а от ситуаций, которые вы хотите по-особенному обработать.
И обратная ситуация, не скупитесь сделать разные исключения, если того требует обстоятельства

try{
    //…
}catch(fileModuleException $e){
    switch($e->getCode()){//так делать не надо
        case 1: echo ‘file not found’;
        case 2: echo ‘file not readable’;
        //…
    }
}
 

Чтобы такие ситуации в принципе не были возможны, можно «заглушить» code в базовом классе

function __construct($message = », $code = 0) {
    parent::__construct($message, 0);
}

3. Не обрабатывайте исключения, если в данном контексте не понятно, как его обработать. Например, если вы следуете паттерну MVC, то в методе модели может быть не понятно, как обработать ошибку — как ее вывести, потому как за логику отвечает control, а за вывод view. Если не понятно, что делать с исключением, то «пробросьте» его дальше.

try{
    $db->begin();
    //…
    $db->commit();
}catch(Exception $e){
    $db->rollback();
    throw $e;
}

От метода, который пробрасывает исключения, можно ожидать любых исключений. Можно сузить количество исключений, бросаемых методом, преобразовав исключение:

try{
    //…
}catch(Exception $e){
    throw new baseException($message, 0, $e);//не разрывайте цепь
}

Тут очень важный момент — не разрывать цепь исключений. Третьим параметром передается изначальное исключение. Этот код нативно работает в 5.3 и с доработкой в 5.2. При таком подходе стек вызовов будет «цельным» от самого первого броска исключения.

4. У вас должен быть глобальный обработчик исключений. Это может быть или try…catch на самом верхнем уровне или ExceptionHandler. Все исключения, которые добрались до глобального обработчика, считаются критическими, так как не были правильно обработаны ранее. Их надо залогировать.

5. Исключение это объект, соответственно его можно расширять под свои потребности. Допустим у вас многоязычное приложение и текст ошибки в бросаемом исключении нужно выводить пользователю. Соответственно это сообщение нужно переводить. Это не сложно, если сообщение без переменных частей, например, «Ошибка при выполнении операции». Но что делать, если в сообщение входят переменные части, например, «У вас недостаточно денег на балансе (1000). Нужно 2000». Тогда можно отдельно передать шаблон текста ошибки и отдельно сами переменные. Пример кода

Старый пример кода

.

6. Преобразуйте все ошибки утверждений (assertion fail) и не фатальные ошибки в исключения (см. мою предыдущую статью)

7. Никогда не глушите исключения без какой либо обработки

try {
    //…
} catch (Exception $e) {
    //ничего делаем
}
 

потому, что в противном случае ошибку из-за таких действий будет очень сложно найти. Нужно хотя бы логировать:

try {
    //…
} catch (Exception $e) {
    exceptionHandlerClass::exceptionLog($e);
}

8. Документируйте исключения. Указывайте в докблоке, какие исключения выбрасывает метод (таг @throws, можно указывать больше одного). Это упростит всем жизнь.

Вот в принципе и все, что нужно знать про исключения. Еще один интересный факт напоследок — исключения можно ловить по интерфейсу:

interface iException{}
class customException extends baseException implements iException{}
try{
    //…
}catch(iException $e){
    //…
}

UPD исправлены замечания в комментариях:1, 2 и 3 (спасибо всем, кто поучаствовал в обсуждении).
Отдельное спасибо, хабраюзеру ckopobapkuh за активное участие

Представляем вашему вниманию руководство для новичков о том, как использовать блоки  Try Catch PHP. В этой статье я покажу, как перехватывать исключения.

Давайте сразу взглянем на пример сгенерированного исключения (и впоследствии перехваченного):

<?php

//Пробуем (try) что-либо сделать.
try{
    //Очевидно, 1 никогда не будет равняться 2...
    if(1 !== 2){
        //Генерируем исключение.
        throw new Exception('1 не равняется 2!');
    }
} 
//Перехватываем (catch) исключение, если что-то идет не так.
catch (Exception $ex) {
    //Выводим сообщение об исключении.
    echo $ex->getMessage();
}

В приведенном выше примере я продемонстрировал использование TRY и CATCH, в котором исключение всегда сгенерировано (только ради примера):

  1. Внутри блока TRY мы проверяем, равняется ли цифра 1 цифре 2. Так как она не равняется (и никогда не будет равняться), мы генерируем исключение с сообщением “1 не равняется 2!”;
  2. Внутри блока CATCH мы перехватываем исключение и выводим соответствующее сообщение.

Выводы:

  • TRY: внутри блока PHP try мы задаем логику приложения. Этот блок содержит код, который может или не может сгенерировать исключение;
  • CATCH: блок CATCH будет перехватывать любые исключения, проявившиеся в предыдущем блоке TRY. Код внутри блока CATCH будет исполнен только в случае обнаружения исключения;
  • FINALLY: если вы используете PHP 5.5 и выше, то вы можете использовать блок FINALLY. Расположенный в нем код исполняется всегда, вне зависимости от того, было ли обнаружено исключение.

Когда используются исключения?

Исключения используются, когда результат операции отличается от того, что ожидало ваше приложение. К примеру, если ваше приложение пытается прочитать CSV-файл на сервере, а этого файла не существует, то можно сгенерировать исключение. Использование PHP try catch в примере:

<?php

//Пробуем (try) что-либо сделать.
try{
    //Попытка открыть CSV-файл.
    $fileHandle = fopen("my_file.csv", "r");
    //Если fopen возвращает логическое значение FALSE, то возникает ошибка.
    if($fileHandle === false){
        throw new Exception('Невозможно открыть CSV-файл!');
    }
} 
//Перехватываем (catch) исключение, если что-то идет не так.
catch (Exception $ex) {
    //Выводим сообщение об исключении.
    echo $ex->getMessage();
}

В приведенном выше примере использования в PHP try exception мы генерируем исключение тогда, когда не можем открыть запрашиваемый файл. И генерируем мы его, так как файл должен был существовать. Примеры ситуаций, когда вы можете генерировать исключения:

  1. Ваше PHP-приложение не может подключиться к MySQL;
  2. Ошибка при запросе к базе данных;
  3. Ошибка при запросе к API;
  4. Получен некорректный тип запроса;
  5. Отсутствуют необходимые переменные $_POST или $_GET.

Нужно ли перехватывать все исключения?

Лично я так не считаю. К примеру: вам не удается подключиться к базе данных, и генерируется исключение, нужно ли тогда исполнять весь оставшийся код? Если весь последующий код привязан к базе данных, которой просто не существует, то зачем же его исполнять?

По моему мнению, исключения нужно перехватывать с помощью PHP try catch finally только, если это не оказывает негативного влияния на остальные функции приложения.

Например: если API-запрос к внешнему сервису выдает ошибку, то вы можете перехватить исключение и вывести дружественное пользователю сообщение «Невозможно подключиться к базе данных» или «Информация о погоде недоступна».

Не перехваченные исключения следует обрабатывать с помощью пользовательского обработчика. Так вы сможете обрабатывать не перехваченные исключения, и выводить понятные человеку сообщения или заглушки.

An error is unexpected data generated by a computer program that the computer program cannot handle. PHP Error Handling is a single-step technique for catching all errors generated by your primary computer program and taking suitable action.

Error handling in PHP with try-catch blocks is remarkably similar to error handling in other programming languages. The PHP runtime looks for a catch statement that can handle a PHP exception when it is thrown. It will keep inspecting the calling methods all the way up the stack trace until it finds a catch statement.

As you read, you will learn more about PHP Exception Handling:

  1. What is Exception Handling?
  2. PHP Exception Handling
  3. PHP Exception Handling Keywords
  4. How to Implement Exception Handling?
  5. When Should You Use try-catch-finally?
  6. Creating Custom PHP Exception Types
  7. Global Exception Handler
  8. Handling Multiple Exception
  9. Re-throwing Exceptions
  10. How to Log Exceptions in Your PHP try-catch Blocks?
  11. How to Use try-catch with MySQL?
  12. View All PHP Exceptions in One Place

#1 What is Exception Handling?

Exception handling is the process of making accommodations for errors and responding to them programmatically, which results in the execution of an alternate, but previously planned, sequence of code. It can be found in a variety of places, including software, hardware, and electrical systems, such as the motherboard of your computer.

Invalid form inputs, erroneous programming logic, network errors, software compatibility issues, unauthorized access, and a lack of memory, among other things, can cause problems on the software side.

Exception handling helps developers debug and users better grasp an application’s needs by resolving conflicts via error messages. Good Exception practices protect users from a bad user experience while also assisting developers in determining what went wrong with the application.

PHP, like many other programming languages, contains some built-in exceptions such as ArgumentCountError, ArithmeticError, DivisionByZeroError, CompileError, ParseError, and TypeError. Exception handling systems, on the other hand, can be utilized to generate problem-specific constraints. If the requirements aren’t followed, they will result in exceptions that can be handled by programming.

#2 PHP Exception Handling

When an error occurs, PHP displays an error message in the web browser with information about the error that happened, depending on your configuration settings.

Errors can be handled in a variety of ways in PHP. We’ll look at three ways that are often used:

  1. Die Statements
    The echo and exit functions are combined in the die function. When we want to produce a notice and stop the script execution when an error occurs, it’s quite handy.
  2. Custom Error Handlers
    When an error occurs, these are user-defined functions that are invoked.
  3. PHP Error Reporting
    Depending on your PHP error reporting settings, the error message will be different. When you don’t know what caused the error, this solution comes in handy in the development environment. The data given can assist you in debugging your application.

#3 PHP Exception Handling Keywords

For PHP exception handling, the following keywords are utilized.

Try – The code that could potentially throw an exception is contained in the try block. Until an exception is thrown, the code in the try block is performed in its entirety.

Throw – The throw keyword is used to indicate that a PHP exception has occurred. After that, the PHP runtime will look for a catch statement to handle the exception.

Catch – Only if an exception occurs within the try code block will this block of code be called. The exception thrown must be handled by the code in your catch statement.

Finally – The «finally» statement was added in PHP 5.5. Finally, instead of or in addition to «catch» blocks, the «finally» block can be specified. Regardless of whether an exception has been thrown, code in the «finally» block will always be run after the try and catch blocks and before normal execution resumes. This is handy in situations where you want to close a database connection regardless of whether or not an exception occurred.

#4 How to Implement Exception Handling?

Exception handling mechanisms such as ‘try,’ ‘throw,’ and ‘catch’ is widely used.

Try

The try block is used to include code that is likely to result in an error. Any erroneous condition that happens in the code within a try block can be addressed programmatically.

<?php
try {
    // code likely to give error comes in this block
}
?>

Throw

Exceptions can be raised either automatically by the programming language as a result of syntactical errors and warnings, or manually by code when a condition is or isn’t met. Throwing is the term used in programming to describe the act of raising errors. Only from within the try block can errors be thrown manually, as shown below:

<?php
try {
    throw new Exception("Something breaks. Please fix it!");
}
?>

The throw keyword is used to create an instance of the exception class with a string that describes the exception that was thrown. Any code in the try block that comes after the throw call is not performed because program control is transferred to the catch block, which is where the exception is handled.

Catch

Let’s look at how exceptions are dealt with or managed now that we know where and how they can be thrown. To catch an exception, use the proper terminology. After the try scope, catch procedures are declared that take an instance of the exception class as a parameter.

<?php
try {
    // if some condition is met or not met
    throw new Exception("Error Message!!!");

    // below lines are not executed
    echo "I am not executed!";
} catch(Exception $e) {
    echo "n Exception caught - ", $e->getMessage();
}
?>

#5 When Should You Use try-catch-finally?

You may wish to utilize a «finally» section in your PHP error handling code. Finally, it’s used for more than simply managing exceptions; it’s also used to run cleaning code like closing a file, closing a database connection, and so on.

When the try-catch block finishes, the «finally» block is always executed.

Example:

try {
    print "this is our try block n";
    throw new Exception();

} catch (Exception $e) {
    print "something went wrong, caught yah! n";

} finally {
   print "this part is always executed n";
}

The program’s operation is illustrated in the diagram below.

#6 Creating Custom PHP Exception Types

Custom exception types are also possible in PHP. This can be useful for defining custom exceptions in your application, which you can then handle differently.

We must construct a new class with functions that can be called when an exception occurs in order to develop a custom exception handler. The class must be an exception class extension.

You can add custom functions to the custom exception class, which inherits properties from PHP’s Exception class. You may not want to show the user all of the details of an exception; instead, show a user-friendly message and log the error message internally for monitoring.

A custom PHP exception is used in the following example:

<?php
// custom exception class
class DivideByZeroException extends Exception {

    // define constructor
    function __construct() {
        // we can even take arguments if we want
    }

    // we can define class methods if required
}
?>

The DivideByZeroException() and DivideByNegativeException() classes are extensions of the current Exception class, inheriting all of the Exception class’s methods and properties.

#7 Global Exception Handler

There’s a good chance you didn’t account for the potential of an exception. To be on the safe side, it’s a good idea to create a global, top-level function that handles any unexpected exceptions. This method will help you in dealing with exceptions not covered by try blocks.

The set_exception_handler function in PHP allows us to define such an exception handler, which takes as an argument the name of the function that is responsible for catching an unexpected exception:

function our_global_exception_handler($exception) {
    // This code should log the exception to console and an error tracking system
    echo "Exception:" . $exception->getMessage();
}

set_exception_handler(‘our_global_exception_handler’);

#8 Handling Multiple Exception

To handle thrown exceptions, multiple exceptions utilize numerous try-catch blocks. Multiple exceptions is a good idea when

  • You’d like to show a customized message based on the exception that was thrown
  • You wish to run a unique operation based on the exception thrown

In simple terms, if a piece of code can throw several types of exceptions and we need to do different actions depending on the type of exception, we can have multiple catch blocks.

<?php
try {
    // calling the function
    triggerException();

} catch (StudytonightException $e) {
    // do something here...

} catch (Exception $e) {
    echo "Oops! Some unexpected error occured...";
}
?>

When handling multiple exceptions with several catch blocks, there are a few things to keep in mind:

  • The catch block for the Exception class’s child class must be positioned above the catch block for the Exception class. Exception class handling catch block, in other words, should be placed last.
  • Since all exception classes are child classes of the Exception class, the catch block handling the Exception class can also handle other exceptions.

The flowchart below shows how multiple exceptions are handled.

#9 Re-throwing Exceptions

When an exception is thrown, you might want to handle it in a different way than usual. Within a «catch» block, it is possible to throw an exception a second time.

Users should not be aware of system errors if a script is used. System errors may be significant to programmers, but they are uninteresting to users. You can re-throw the exception with a user-friendly message to make things easy for the user.

<?php
try {
    try {
        // throw exception
        throw new Exception($e);

    } catch(Exception $e) {
        // re-throw exception
       throw new customException($e);
    }

} catch (customException $e) {
    // display custom message
    echo $e->errorMessage();
}
?>

#10 How to Log Exceptions in Your PHP try-catch Blocks?

During development, error logs are essential because they allow developers to observe warnings, errors, alerts, and other events that occurred while the application was operating. That you can use the PHP exception handling try-catch method to appropriately handle them.

Depending on the PHP framework you’re using, such as Laravel, Codeigniter, Symfony, or others, built-in logging frameworks may be available. Monolog, a standard PHP logging package, is another option. You should always log essential exceptions thrown in your code, regardless of the logging system you’re using.

Here’s a sample of a Monolog try/catch that reports errors:

require_once(DIR.'/vendor/autoload.php');
use MonologLogger;
use MonologHandlerStreamHandler;

$logger = new Logger('channel-name');
$logger->pushHandler(new StreamHandler(DIR.'/app.log', Logger::DEBUG));

try {
    // Code does some stuff
    // debug logging statement
    $logger->info('This is a log!');

} catch (Exception $e) {
    $logger->error('Oh no an exception happened! ');
}

#11 How to Use try-catch with MySQL?

Error handling is handled differently in the PHP libraries for MySQL, PDO, and mysqli. You can’t use try-catch blocks if you don’t have exceptions enabled for those libraries. This makes error handling unique and possibly more difficult.

PDO

When making a connection in PDO, you must activate ERRMODE_EXCEPTION.

ERRMODE_EXCEPTION is the default mode as of PHP 8.0.0. PDO will throw a PDOException and adjust its attributes to reflect the error code and details about the error in order to update the error code. This feature is especially handy while debugging because it literally blows the script at the point of the error, pointing a finger at potential trouble spots in your code very fast. If the script crashes due to an exception, the transactions are automatically rolled back.

// connect to MySQL
$conn = new PDO('mysql:host=localhost;dbname=atatusdb;charset=utf8mb4', 'username', 'password');

//PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

MySQL

You must do something similar for mysqli too:

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

#12 View All PHP Exceptions in One Place

In PHP, proper exception handling is critical. You don’t want to just log your exceptions to a log file and never know they happened as part of that.

Use an error tracking tool like Atatus as a solution. All errors are logged and captured together with key information about them, such as class, message, URL, request agent, version, and so on.

In Atatus, all errors are automatically logged and organized so they may be easily accessed. Atatus will not only show you what errors have occurred, but it will also investigate where and why they happened. The timing and number of incidents are also displayed in the logs, making it much easier to pinpoint which issue needs to be addressed first.

Error tracking and monitoring tools are included in Atatus. Among the features is the ability to see all errors in one location, the ability to identify unique errors, the ability to locate new errors faster after a deployment, email notifications about errors, and more.

The error and log management solution from Atatus can help you in easily monitoring and troubleshooting your application.

Wrapping It

We realized that it’s necessary to be prepared for unexpected scenarios that require exceptions at all times. To do this, we can wrap our code in try blocks, have proper catch methods to deal with thrown errors, and a global exception handler to deal with anything else. Now that you’ve gathered all of the necessary information, go ahead and make your own exceptions!

Also, do share what you think of this article with us in the below comment section.


В этом уроке вы узнаете, как генерировать и перехватывать исключения (exceptions) в PHP.


Что такое исключение?

Как и в других языках, PHP имеет механизм исключений для обработки пользовательских ошибок. Исключение — это сигнал, указывающий на то, что что-то в процессе отличается от предполагаемого хода событий. Исключения могут быть вызваны различными причинами, обычно в случае ошибки, которые проявляются или могут быть специально созданы программой. В большинстве случаев подобные ситуации требуют выполнения специальных инструкций, чтобы предотвратить неконтролируемое завершение процесса.

Многие функции и классы PHP создают исключения.

Пользовательские функции и классы также могут вызывать исключения.

Выброс исключения

Исключения могут быть брошены throw и пойманы catch. Если исключение брошено, значит произошло что-то выходящее за рамки нормального течения процесса работы сценария, и необходимо выполнение какой-то другой функции. Подхват исключения происходит в специальной функции, которая сообщает остальной программе о том, что она готова обработать исключение.

Функция try-catch может самостоятельно выполнить необходимые действия или перебросить исключение другой функции.

Исключение может быть вызвано функцией, которую мы вызываем, или мы можем использовать ключевое слово throw для выбрасывания исключения вручную. Например, мы можем проверить некоторый ввод перед выполнением любой операции и выбросить исключение, если данные недействительны.

Здесь важно отметить, что если мы выбрасываем исключение, но не определили блок catch, который должен обрабатывать это исключение, это приведет к фатальной ошибке. Поэтому важно убедиться, что мы определили блок catch, если бросаем исключения в своем приложении.

Если исключение не обнаружено, произойдет фатальная ошибка с сообщением «Uncaught Exception» (Неперехваченное исключение).

Попробуем сгенерировать исключение, не улавливая его:

<?php
function divide($dividend, $divisor) {
  // Выбросить исключение, если делитель равен нулю
  if($divisor == 0) {
    throw new Exception("Деление на ноль");
  }
  return $dividend / $divisor;
}

echo divide(5, 0);
?>

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

Fatal error: Uncaught Exception: Деление на ноль in C:webfoldertest.php:4 Stack trace: #0 C:webfoldertest.php(14): divide(5, 0) #1 {main} thrown in C:webfoldertest.php on line 9

Конструкция try … catch

Исключение генерируется оператором throw new Exception(), а ловится операторами try и catch.

Чтобы избежать ошибки из приведенного выше примера, мы можем использовать оператор try...catch для перехвата исключений и продолжения процесса.

Синтаксис

try {
  код, который может бросать (throw) исключения
} catch(Исключение $e) {
  код, который запускается при обнаружении исключения
}

При подходе, основанном на исключениях, программный код записывается в блоке try, исключение может быть создано с помощью оператора throw, когда во время выполнения кода в блоке try происходит исключительное событие. Затем он вылавливается и разрешается одним или несколькими блоками catch.

В следующем примере покажем сообщение при возникновении исключения:

<?php
function divide($dividend, $divisor) {
  if($divisor == 0) {
    throw new Exception("Деление на ноль");
  }
  return $dividend / $divisor;
}

try {
  echo divide(5, 0);
} catch(Exception $e) {
  echo "Невозможно разделить!";
}
?>

Когда исключение попадает в блок catch, объект Exception содержит сообщение об ошибке, которое было выбрано с использованием ключевого слова throw. Переменная $e, в приведенном выше примере, является экземпляром класса Exception, поэтому она имеет доступ ко всем методам этого класса. В этом блоке мы должны определить свою собственную логику обработки исключений — что именно мы хотим сделать с ошибкой, которую вы поймаете.

Конструкция try…catch…finally

Иногда возникает необходимость выполнить часть кода независимо от того, было ли вызвано исключение в блоке try. Вот где мы можем использовать блок finally, поскольку код, который мы размещаем в блоке finally, всегда будет выполняться после выполнения блоков try и catch независимо от того, было ли выбрано исключение.

Важно понять, что catch выполнится в любом случае перед выходом из конструкции try...catch...finally, то есть эта особенность пригодится для обработки ситуаций, когда блок catch не отработал, а исключение возникло. Например, если блока catch нет вообще или в подобной ситуации, когда ожидается исключение определенного вида, а вызывается другое:

try { 
    echo "В try вызвано исключениеn";
    throw new StrangeException();
} catch (SomeException $e) {
    echo "Вызвано исключение SomeException n";
} catch (AnotherException $e) {
    echo "Вызвано исключение AnotherException n";
} finally {
    echo "Этот блок кода выполнится всегдаn";
}

В следующем примере покажем сообщение при возникновении исключения, а затем укажем, что процесс завершен:

<?php
function divide($dividend, $divisor) {
  if($divisor == 0) {
    throw new Exception("Деление на ноль");
  }
  return $dividend / $divisor;
}

try {
  echo divide(5, 0);
} catch(Exception $e) {
  echo "Невозможно разделить! ";
} finally {
  echo "Процесс завершен.";
}
?>

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

Невозможно разделить! Процесс завершен.

В следующем примере смоделируем ситуацию, когда блока catch нет вообще, т.е. выведем строку «Процесс завершен», даже если исключение не было обнаружено:

<?php
function divide($dividend, $divisor) {
  if($divisor == 0) {
    throw new Exception("Деление на ноль");
  }
  return $dividend / $divisor;
}

try {
  echo divide(5, 0);
} finally {
  echo "Процесс завершен.";
}
?>

Объект исключения (Exception)

Объект Exception содержит информацию об ошибке или непредвиденном поведении, с которым столкнулась функция.

Синтаксис

new Exception(message, code, previous)

Параметры

Параметр Описание
message Необязательный. Строка, описывающая, почему было сгенерировано исключение.
code Необязательный. Целое число, которое можно использовать, чтобы легко отличить это исключение от других того же типа.
previous Необязательный. Если это исключение было сгенерировано в блоке catch другого исключения, рекомендуется передать это исключение в этот параметр.

Методы класса Exception РНР

Класс Exception РНР также предоставляет методы, которые могут быть использованы для создания подробной информации отладки.

В следующей таблице показаны некоторые методы, которые можно использовать для получения информации при перехвате исключения:

Метод Описание
getMessage() Возвращает строку, описывающую, почему было создано исключение.
getPrevious() Если это исключение было вызвано другим, этот метод возвращает предыдущее исключение. Если нет, то возвращается null
getCode() Возвращает код исключения
getFile() Возвращает полный путь к файлу, в котором возникло исключение.
getLine() Возвращает номер строки кода, вызвавшего исключение.

Используем приведенные методы для для создания подробной информации о сгенерированном исключении:

<?php
function divide($dividend, $divisor) {
  if($divisor == 0) {
    throw new Exception("Деление на ноль", 1);
  }
  return $dividend / $divisor;
}

try {
  echo divide(5, 0);
} catch(Exception $ex) {
  $code = $ex->getCode();
  $message = $ex->getMessage();
  $file = $ex->getFile();
  $line = $ex->getLine();
  echo "Исключение добавлено в $file в строке $line: [Код $code]
  $message";
}
?>

Примечание: Исключения следует использовать только для обозначения исключительных условий. Не рекомендуем использовать их для управления обычным потоком программы, например, для перехода в другое место в скрипте в определенной точке, т.к. это отрицательно скажется на производительности вашей прграммы.


Определение пользовательских исключений

Мы можем определить свои собственные обработчики исключений, чтобы по-своему обрабатывать различные типы исключений. Это позволяет нам использовать для каждого типа исключения отдельный блок catch.

Учитывая, что класс Exception является базовым классом для всех исключений, мы можем, расширив класс Exception, определить свое настраиваемое исключение. Пользовательский класс исключений наследует все свойства и методы класса Exception PHP. Давайте посмотрим на следующий пример, в котором добавим свои собственные методы в собственный класс исключений:

<?php
// Расширение класса Exception
class EmptyEmailException extends Exception {}
class InvalidEmailException extends Exception {}
 
$email = "someuser@example..com";
 
try{
    // Выбрасывать исключение, если электронная почта пуста
    if($email == ""){
        throw new EmptyEmailException("

Пожалуйста, введите ваш E-mail адрес!

"); } // Выбрасывать исключение, если адрес электронной почты недействителен if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) { throw new InvalidEmailException("

$email - это невалидный E-mail адрес!

"); } // Отобразить сообщение, если адрес электронной почты действителен echo "

SUCCESS: Email проверку прошел успешно

"; } catch(EmptyEmailException $e){ echo $e->getMessage(); } catch(InvalidEmailException $e){ echo $e->getMessage(); } ?>

В приведенном выше примере мы создали два новых класса исключений: EmptyEmailException и InvalidEmailException из базового класса Exception. Блоки catch используются для отображения различных сообщений об ошибках в зависимости от типа созданного исключения.

Созданные пользовательские классы исключений, как отмечалось выше, наследуют свойства и методы класса Exception, поэтому мы использовали методы класса Exception для получения информации об ошибке из объекта исключения.


Установка глобального обработчика исключений

Как уже отмечалось ранее, если исключение не перехвачено, PHP генерирует фатальную ошибку с сообщением «Uncaught Exception» (Неперехваченное исключение). Это сообщение об ошибке может содержать конфиденциальную информацию, такую ​​как имя файла и номер строки, в которой возникает проблема. Если, по какой-либо причине, вам необходимо срыть эту ​​информацию от пользователя, вы можете создать настраиваемую функцию и зарегистрировать ее в функции set_exception_handler() для обработки всех неперехваченных исключений.

<?php
function handleUncaughtException($e){
    // Отображение общего сообщения об ошибке для пользователя
    echo "Оппс! Что-то пошло не так. Повторите попытку или свяжитесь с нами, если проблема не исчезнет.";
    
    // Создадим строку ошибки
    $error = "Неперехваченное исключение: " . $message = date("Y-m-d H:i:s - ");
    $error .= $e->getMessage() . " в файле " . $e->getFile() . " на строке " . $e->getLine() . "n";
    
    // Записать сведения об ошибке в файл
    error_log($error, 3, "var/log/exceptionLog.log");
}
 
// Зарегистрировать пользовательский обработчик исключений
set_exception_handler("handleUncaughtException");
 
// Бросить исключение
throw new Exception("Тестовое исключение!");
?>

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

Оппс! Что-то пошло не так. Повторите попытку или свяжитесь с нами, если проблема не исчезнет.

Примечание: Неперехваченное исключение всегда приводит к завершению работы скрипта. Поэтому, если вам нужно, чтобы сценарий продолжал выполняться после точки остановки, в которой произошло исключение, у вас должен быть хотя бы один соответствующий блок catch для каждого блока try.

Понравилась статья? Поделить с друзьями:
  • Tur hinten re offen перевод ошибка фольксваген
  • Tuple object is not callable python ошибка
  • Tuple object has no attribute append ошибка
  • Tunngle ошибка install incomplete please download and run
  • Tunnelbear ошибка подключения к серверу