Меню

Php вернуть статус ошибки

(PHP 5 >= 5.4.0, PHP 7, PHP 8)

http_response_codeПолучает или устанавливает код ответа HTTP

Описание

http_response_code(int $response_code = 0): int|bool

Список параметров

response_code

Код ответа устанавливается с помощью опционального параметра response_code.

Возвращаемые значения

Если response_code задан, то будет возвращён предыдущий код
статуса. Если response_code не задан, то будет возвращён
текущий код статуса. Оба этих значения будут по умолчанию иметь код состояния 200,
если они используются в окружении веб-сервера.

Если response_code не задан и используется не в окружении
веб-сервера (например, в CLI), то будет возвращено false. Если
response_code задан и используется не в окружении
веб-сервера, то будет возвращено true (но только если не был установлен предыдущий
код статуса).

Примеры

Пример #1 Использование http_response_code() в окружении веб-сервера


<?php// Берём текущий код и устанавливаем новый
var_dump(http_response_code(404));// Берём новый код
var_dump(http_response_code());
?>

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

Пример #2 Использование http_response_code() в CLI


<?php// Берём текущий код по умолчанию
var_dump(http_response_code());// Устанавливаем код
var_dump(http_response_code(201));// Берём новый код
var_dump(http_response_code());
?>

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

bool(false)
bool(true)
int(201)

Смотрите также

  • header() — Отправка HTTP-заголовка
  • headers_list() — Возвращает список переданных заголовков (или готовых к отправке)

craig at craigfrancis dot co dot uk

11 years ago


If your version of PHP does not include this function:

<?phpif (!function_exists('http_response_code')) {
        function
http_response_code($code = NULL) {

            if (

$code !== NULL) {

                switch (

$code) {
                    case
100: $text = 'Continue'; break;
                    case
101: $text = 'Switching Protocols'; break;
                    case
200: $text = 'OK'; break;
                    case
201: $text = 'Created'; break;
                    case
202: $text = 'Accepted'; break;
                    case
203: $text = 'Non-Authoritative Information'; break;
                    case
204: $text = 'No Content'; break;
                    case
205: $text = 'Reset Content'; break;
                    case
206: $text = 'Partial Content'; break;
                    case
300: $text = 'Multiple Choices'; break;
                    case
301: $text = 'Moved Permanently'; break;
                    case
302: $text = 'Moved Temporarily'; break;
                    case
303: $text = 'See Other'; break;
                    case
304: $text = 'Not Modified'; break;
                    case
305: $text = 'Use Proxy'; break;
                    case
400: $text = 'Bad Request'; break;
                    case
401: $text = 'Unauthorized'; break;
                    case
402: $text = 'Payment Required'; break;
                    case
403: $text = 'Forbidden'; break;
                    case
404: $text = 'Not Found'; break;
                    case
405: $text = 'Method Not Allowed'; break;
                    case
406: $text = 'Not Acceptable'; break;
                    case
407: $text = 'Proxy Authentication Required'; break;
                    case
408: $text = 'Request Time-out'; break;
                    case
409: $text = 'Conflict'; break;
                    case
410: $text = 'Gone'; break;
                    case
411: $text = 'Length Required'; break;
                    case
412: $text = 'Precondition Failed'; break;
                    case
413: $text = 'Request Entity Too Large'; break;
                    case
414: $text = 'Request-URI Too Large'; break;
                    case
415: $text = 'Unsupported Media Type'; break;
                    case
500: $text = 'Internal Server Error'; break;
                    case
501: $text = 'Not Implemented'; break;
                    case
502: $text = 'Bad Gateway'; break;
                    case
503: $text = 'Service Unavailable'; break;
                    case
504: $text = 'Gateway Time-out'; break;
                    case
505: $text = 'HTTP Version not supported'; break;
                    default:
                        exit(
'Unknown http status code "' . htmlentities($code) . '"');
                    break;
                }
$protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');header($protocol . ' ' . $code . ' ' . $text);$GLOBALS['http_response_code'] = $code;

            } else {

$code = (isset($GLOBALS['http_response_code']) ? $GLOBALS['http_response_code'] : 200);

            }

            return

$code;

        }
    }

?>

In this example I am using $GLOBALS, but you can use whatever storage mechanism you like... I don't think there is a way to return the current status code:

https://bugs.php.net/bug.php?id=52555

For reference the error codes I got from PHP's source code:

http://lxr.php.net/opengrok/xref/PHP_5_4/sapi/cgi/cgi_main.c#354

And how the current http header is sent, with the variables it uses:

http://lxr.php.net/opengrok/xref/PHP_5_4/main/SAPI.c#856


Stefan W

8 years ago


Note that you can NOT set arbitrary response codes with this function, only those that are known to PHP (or the SAPI PHP is running on).

The following codes currently work as expected (with PHP running as Apache module):
200 – 208, 226
300 – 305, 307, 308
400 – 417, 422 – 424, 426, 428 – 429, 431
500 – 508, 510 – 511

Codes 0, 100, 101, and 102 will be sent as "200 OK".

Everything else will result in "500 Internal Server Error".

If you want to send responses with a freestyle status line, you need to use the `header()` function:

<?php header("HTTP/1.0 418 I'm A Teapot"); ?>


Thomas A. P.

7 years ago


When setting the response code to non-standard ones like 420, Apache outputs 500 Internal Server Error.

This happens when using header(0,0,420) and http_response_code(420).
Use header('HTTP/1.1 420 Enhance Your Calm') instead.

Note that the response code in the string IS interpreted and used in the access log and output via http_response_code().


Anonymous

9 years ago


Status codes as an array:

<?php
$http_status_codes
= array(100 => "Continue", 101 => "Switching Protocols", 102 => "Processing", 200 => "OK", 201 => "Created", 202 => "Accepted", 203 => "Non-Authoritative Information", 204 => "No Content", 205 => "Reset Content", 206 => "Partial Content", 207 => "Multi-Status", 300 => "Multiple Choices", 301 => "Moved Permanently", 302 => "Found", 303 => "See Other", 304 => "Not Modified", 305 => "Use Proxy", 306 => "(Unused)", 307 => "Temporary Redirect", 308 => "Permanent Redirect", 400 => "Bad Request", 401 => "Unauthorized", 402 => "Payment Required", 403 => "Forbidden", 404 => "Not Found", 405 => "Method Not Allowed", 406 => "Not Acceptable", 407 => "Proxy Authentication Required", 408 => "Request Timeout", 409 => "Conflict", 410 => "Gone", 411 => "Length Required", 412 => "Precondition Failed", 413 => "Request Entity Too Large", 414 => "Request-URI Too Long", 415 => "Unsupported Media Type", 416 => "Requested Range Not Satisfiable", 417 => "Expectation Failed", 418 => "I'm a teapot", 419 => "Authentication Timeout", 420 => "Enhance Your Calm", 422 => "Unprocessable Entity", 423 => "Locked", 424 => "Failed Dependency", 424 => "Method Failure", 425 => "Unordered Collection", 426 => "Upgrade Required", 428 => "Precondition Required", 429 => "Too Many Requests", 431 => "Request Header Fields Too Large", 444 => "No Response", 449 => "Retry With", 450 => "Blocked by Windows Parental Controls", 451 => "Unavailable For Legal Reasons", 494 => "Request Header Too Large", 495 => "Cert Error", 496 => "No Cert", 497 => "HTTP to HTTPS", 499 => "Client Closed Request", 500 => "Internal Server Error", 501 => "Not Implemented", 502 => "Bad Gateway", 503 => "Service Unavailable", 504 => "Gateway Timeout", 505 => "HTTP Version Not Supported", 506 => "Variant Also Negotiates", 507 => "Insufficient Storage", 508 => "Loop Detected", 509 => "Bandwidth Limit Exceeded", 510 => "Not Extended", 511 => "Network Authentication Required", 598 => "Network read timeout error", 599 => "Network connect timeout error");
?>

Source: Wikipedia "List_of_HTTP_status_codes"


viaujoc at videotron dot ca

2 years ago


Do not mix the use of http_response_code() and manually setting  the response code header because the actual HTTP status code being returned by the web server may not end up as expected. http_response_code() does not work if the response code has previously been set using the header() function. Example:

<?php
header
('HTTP/1.1 401 Unauthorized');
http_response_code(403);
print(
http_response_code());
?>

The raw HTTP response will be (notice the actual status code on the first line does not match the printed http_response_code in the body):

HTTP/1.1 401 Unauthorized
Date: Tue, 24 Nov 2020 13:49:08 GMT
Server: Apache
Connection: Upgrade, Keep-Alive
Keep-Alive: timeout=5, max=100
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8

403

I only tested it on Apache. I am not sure if this behavior is specific to Apache or common to all PHP distributions.


Anonymous

8 years ago


You can also create a enum by extending the SplEnum class.
<?php/** HTTP status codes */
class HttpStatusCode extends SplEnum {
    const
__default = self::OK;

        const

SWITCHING_PROTOCOLS = 101;
    const
OK = 200;
    const
CREATED = 201;
    const
ACCEPTED = 202;
    const
NONAUTHORITATIVE_INFORMATION = 203;
    const
NO_CONTENT = 204;
    const
RESET_CONTENT = 205;
    const
PARTIAL_CONTENT = 206;
    const
MULTIPLE_CHOICES = 300;
    const
MOVED_PERMANENTLY = 301;
    const
MOVED_TEMPORARILY = 302;
    const
SEE_OTHER = 303;
    const
NOT_MODIFIED = 304;
    const
USE_PROXY = 305;
    const
BAD_REQUEST = 400;
    const
UNAUTHORIZED = 401;
    const
PAYMENT_REQUIRED = 402;
    const
FORBIDDEN = 403;
    const
NOT_FOUND = 404;
    const
METHOD_NOT_ALLOWED = 405;
    const
NOT_ACCEPTABLE = 406;
    const
PROXY_AUTHENTICATION_REQUIRED = 407;
    const
REQUEST_TIMEOUT = 408;
    const
CONFLICT = 408;
    const
GONE = 410;
    const
LENGTH_REQUIRED = 411;
    const
PRECONDITION_FAILED = 412;
    const
REQUEST_ENTITY_TOO_LARGE = 413;
    const
REQUESTURI_TOO_LARGE = 414;
    const
UNSUPPORTED_MEDIA_TYPE = 415;
    const
REQUESTED_RANGE_NOT_SATISFIABLE = 416;
    const
EXPECTATION_FAILED = 417;
    const
IM_A_TEAPOT = 418;
    const
INTERNAL_SERVER_ERROR = 500;
    const
NOT_IMPLEMENTED = 501;
    const
BAD_GATEWAY = 502;
    const
SERVICE_UNAVAILABLE = 503;
    const
GATEWAY_TIMEOUT = 504;
    const
HTTP_VERSION_NOT_SUPPORTED = 505;
}


divinity76 at gmail dot com

2 years ago


if you need a response code not supported by http_response_code(), such as WebDAV / RFC4918's "HTTP 507 Insufficient Storage", try:

<?php
header
($_SERVER['SERVER_PROTOCOL'] . ' 507 Insufficient Storage');
?>
result: something like

HTTP/1.1 507 Insufficient Storage


Rob Zazueta

9 years ago


The note above from "Anonymous" is wrong. I'm running this behind the AWS Elastic Loadbalancer and trying the header(':'.$error_code...) method mentioned above is treated as invalid HTTP.

The documentation for the header() function has the right way to implement this if you're still on < php 5.4:

<?php
header
("HTTP/1.0 404 Not Found");
?>


Anonymous

10 years ago


If you don't have PHP 5.4 and want to change the returned status code, you can simply write:
<?php
header
(':', true, $statusCode);
?>

The ':' are mandatory, or it won't work

Richard F.

9 years ago


At least on my side with php-fpm and nginx this method does not change the text in the response, only the code.

<?php// HTTP/1.1 404 Not Found
http_response_code(404);?>

The resulting response is HTTP/1.1 404 OK


Steven

7 years ago


http_response_code is basically a shorthand way of writing a http status header, with the added bonus that PHP will work out a suitable Reason Phrase to provide by matching your response code to one of the values in an enumeration it maintains within php-src/main/http_status_codes.h. Note that this means your response code must match a response code that PHP knows about. You can't create your own response codes using this method, however you can using the header method.

In summary - The differences between "http_response_code" and "header" for setting response codes:

1. Using http_response_code will cause PHP to match and apply a Reason Phrase from a list of Reason Phrases that are hard-coded into the PHP source code.

2. Because of point 1 above, if you use http_response_code you must set a code that PHP knows about. You can't set your own custom code, however you can set a custom code (and Reason Phrase) if you use the header method.


stephen at bobs-bits dot com

8 years ago


It's not mentioned explicitly, but the return value when SETTING, is the OLD status code.
e.g.
<?php

$a

= http_response_code();
$b = http_response_code(202);
$c = http_response_code();var_dump($a, $b, $c);// Result:
// int(200)
// int(200)
// int(202)
?>

Anonymous

4 years ago


http_response_code() does not actually send HTTP headers, it only prepares the header list to be sent later on.
So you can call http_reponse_code() to set, get and reset the HTTP response code before it gets sent.

Test code:
<php
http_response_code(500);  // set the code
var_dump(headers_sent());  // check if headers are sent
http_response_code(200);  // avoid a default browser page


Chandra Nakka

5 years ago


On PHP 5.3 version, If you want to set HTTP response code. You can try this type of below trick :)

<?php

header

('Temporary-Header: True', true, 404);
header_remove('Temporary-Header');?>


yefremov {dot} sasha () gmail {dot} com

8 years ago


@craig at craigfrancis dot co dot uk@ wrote the function that replaces the original. It is very usefull, but has a bug. The original http_response_code always returns the previous or current code, not the code you are setting now. Here is my fixed version. I also use $GLOBALS to store the current code, but trigger_error() instead of exit. So now, how the function will behave in the case of error lies on the error handler. Or you can change it back to exit().

if (!function_exists('http_response_code')) {
    function http_response_code($code = NULL) {    
        $prev_code = (isset($GLOBALS['http_response_code']) ? $GLOBALS['http_response_code'] : 200);

        if ($code === NULL) {
            return $prev_code;
        }

        switch ($code) {
            case 100: $text = 'Continue'; break;
            case 101: $text = 'Switching Protocols'; break;
            case 200: $text = 'OK'; break;
            case 201: $text = 'Created'; break;
            case 202: $text = 'Accepted'; break;
            case 203: $text = 'Non-Authoritative Information'; break;
            case 204: $text = 'No Content'; break;
            case 205: $text = 'Reset Content'; break;
            case 206: $text = 'Partial Content'; break;
            case 300: $text = 'Multiple Choices'; break;
            case 301: $text = 'Moved Permanently'; break;
            case 302: $text = 'Moved Temporarily'; break;
            case 303: $text = 'See Other'; break;
            case 304: $text = 'Not Modified'; break;
            case 305: $text = 'Use Proxy'; break;
            case 400: $text = 'Bad Request'; break;
            case 401: $text = 'Unauthorized'; break;
            case 402: $text = 'Payment Required'; break;
            case 403: $text = 'Forbidden'; break;
            case 404: $text = 'Not Found'; break;
            case 405: $text = 'Method Not Allowed'; break;
            case 406: $text = 'Not Acceptable'; break;
            case 407: $text = 'Proxy Authentication Required'; break;
            case 408: $text = 'Request Time-out'; break;
            case 409: $text = 'Conflict'; break;
            case 410: $text = 'Gone'; break;
            case 411: $text = 'Length Required'; break;
            case 412: $text = 'Precondition Failed'; break;
            case 413: $text = 'Request Entity Too Large'; break;
            case 414: $text = 'Request-URI Too Large'; break;
            case 415: $text = 'Unsupported Media Type'; break;
            case 500: $text = 'Internal Server Error'; break;
            case 501: $text = 'Not Implemented'; break;
            case 502: $text = 'Bad Gateway'; break;
            case 503: $text = 'Service Unavailable'; break;
            case 504: $text = 'Gateway Time-out'; break;
            case 505: $text = 'HTTP Version not supported'; break;
            default:
                trigger_error('Unknown http status code ' . $code, E_USER_ERROR); // exit('Unknown http status code "' . htmlentities($code) . '"');
                return $prev_code;
        }

        $protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
        header($protocol . ' ' . $code . ' ' . $text);
        $GLOBALS['http_response_code'] = $code;

        // original function always returns the previous or current code
        return $prev_code;
    }
}


Kubo2

6 years ago


If you want to set a HTTP response code without the need of specifying a protocol version, you can actually do it without http_response_code():

<?php

header

('Status: 404', TRUE, 404);?>


zweibieren at yahoo dot com

7 years ago


The limited list given by Stefan W is out of date. I have just tested 301 and 302 and both work.

divinity76 at gmail dot com

6 years ago


warning, it does not check if headers are already sent (if it is, it won't *actually* change the code, but a subsequent call will imply that it did!!),

you might wanna do something like
function ehttp_response_code(int $response_code = NULL): int {
    if ($response_code === NULL) {
        return http_response_code();
    }
    if (headers_sent()) {
        throw new Exception('tried to change http response code after sending headers!');
    }
    return http_response_code($response_code);
}


I just found this question and thought it needs a more comprehensive answer:

As of PHP 5.4 there are three methods to accomplish this:

Assembling the response code on your own (PHP >= 4.0)

The header() function has a special use-case that detects a HTTP response line and lets you replace that with a custom one

header("HTTP/1.1 200 OK");

However, this requires special treatment for (Fast)CGI PHP:

$sapi_type = php_sapi_name();
if (substr($sapi_type, 0, 3) == 'cgi')
    header("Status: 404 Not Found");
else
    header("HTTP/1.1 404 Not Found");

Note: According to the HTTP RFC, the reason phrase can be any custom string (that conforms to the standard), but for the sake of client compatibility I do not recommend putting a random string there.

Note: php_sapi_name() requires PHP 4.0.1

3rd argument to header function (PHP >= 4.3)

There are obviously a few problems when using that first variant. The biggest of which I think is that it is partly parsed by PHP or the web server and poorly documented.

Since 4.3, the header function has a 3rd argument that lets you set the response code somewhat comfortably, but using it requires the first argument to be a non-empty string. Here are two options:

header(':', true, 404);
header('X-PHP-Response-Code: 404', true, 404);

I recommend the 2nd one. The first does work on all browsers I have tested, but some minor browsers or web crawlers may have a problem with a header line that only contains a colon. The header field name in the 2nd. variant is of course not standardized in any way and could be modified, I just chose a hopefully descriptive name.

http_response_code function (PHP >= 5.4)

The http_response_code() function was introduced in PHP 5.4, and it made things a lot easier.

http_response_code(404);

That’s all.

Compatibility

Here is a function that I have cooked up when I needed compatibility below 5.4 but wanted the functionality of the «new» http_response_code function. I believe PHP 4.3 is more than enough backwards compatibility, but you never know…

// For 4.3.0 <= PHP <= 5.4.0
if (!function_exists('http_response_code'))
{
    function http_response_code($newcode = NULL)
    {
        static $code = 200;
        if($newcode !== NULL)
        {
            header('X-PHP-Response-Code: '.$newcode, true, $newcode);
            if(!headers_sent())
                $code = $newcode;
        }       
        return $code;
    }
}

http_response_code

(PHP 5 >= 5.4.0, PHP 7)

http_response_codeПолучает или устанавливает код ответа HTTP

Описание

int http_response_code
([ int $response_code
] )

Список параметров

response_code

Код ответа устанавливается с помощью опционального параметра response_code.


<?php
http_response_code
(404);
?>

Возвращаемые значения

Текущий код ответа. По умолчанию возвращается значение int(200).

Примеры

Пример #1 Пример использования http_response_code()


<?php// Получает текущий код ответа по умолчанию
var_dump(http_response_code()); // int(200)

// Устанавливает заданный код ответа

http_response_code(404);// Получает новый код ответа
var_dump(http_response_code()); // int(404)
?>

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

Смотрите также

  • header() — Отправка HTTP заголовка
  • headers_list() — Возвращает список переданных (или готовых к передаче) заголовков

Вернуться к: Сетевые Функции

Антон Шевчук // Web-разработчик

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

О да, в этой статье я поведу свой рассказа об ошибках в PHP, и том как их обуздать.

Ошибки

Разновидности в семействе ошибок

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

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

<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);

Фатальные ошибки

Самый грозный вид ошибок – фатальные, они могут возникнуть как при компиляции, так и при работе парсера или PHP-скрипта, выполнение скрипта при этом прерывается.

E_PARSE
Это ошибка появляется, когда вы допускаете грубую ошибку синтаксиса и интерпретатор PHP не понимает, что вы от него хотите, например если не закрыли фигурную или круглую скобочку:

<?php
/**
 Parse error: syntax error, unexpected end of file
 */
{

Или написали на непонятном языке:

<?php
/**
 Parse error: syntax error, unexpected '...' (T_STRING)
 */
Тут будет ошибка парсера

Лишние скобочки тоже встречаются, и не важно круглые либо фигурные:

<?php
/**
 Parse error: syntax error, unexpected '}'
 */
}

Отмечу один важный момент – код файла, в котором вы допустили parse error не будет выполнен, следовательно, если вы попытаетесь включить отображение ошибок в том же файле, где возникла ошибка парсера то это не сработает:

<?php
// этот код не сработает
error_reporting(E_ALL);
ini_set('display_errors', 1);

// т.к. вот тут
ошибка парсера

E_ERROR
Это ошибка появляется, когда PHP понял что вы хотите, но сделать сие не получилось ввиду ряда причин, так же прерывает выполнение скрипта, при этом код до появления ошибки сработает:

Не был найден подключаемый файл:

/**
 Fatal error: require_once(): Failed opening required 'not-exists.php' (include_path='.:/usr/share/php:/usr/share/pear')
 */
require_once 'not-exists.php';

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

/**
 Fatal error: Uncaught exception 'Exception'
 */
throw new Exception();

При попытке вызвать несуществующий метод класса:

/**
 Fatal error: Call to undefined method stdClass::notExists()
 */
$stdClass = new stdClass();
$stdClass->notExists();

Отсутствия свободной памяти (больше, чем прописано в директиве memory_limit) или ещё чего-нить подобного:

/**
 Fatal Error: Allowed Memory Size
 */
$arr = array();

while (true) {
    $arr[] = str_pad(' ', 1024);
}

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

Рекурсивный вызов функции. В данном примере он закончился на 256-ой итерации, ибо так прописано в настройках xdebug:

/**
 Fatal error: Maximum function nesting level of '256' reached, aborting!
 */
function deep() {
    deep();
}
deep();

Не фатальные

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

E_WARNING
Частенько встречается, когда подключаешь файл с использованием include, а его не оказывается на сервере или ошиблись указывая путь к файлу:

/**
 Warning: include_once(): Failed opening 'not-exists.php' for inclusion
 */
include_once 'not-exists.php';

Бывает, если используешь неправильный тип аргументов при вызове функций:

/**
 Warning: join(): Invalid arguments passed
 */
join('string', 'string');

Их очень много, и перечислять все не имеет смысла…

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

Когда обращаются к неопределенной переменной:

/**
 Notice: Undefined variable: a
 */
echo $a;

Когда обращаются к несуществующему элементу массива:

<?php
/**
 Notice: Undefined index: a
 */
$b = array();
$b['a'];

Когда обращаются к несуществующей константе:

/**
 Notice: Use of undefined constant UNKNOWN_CONSTANT - assumed 'UNKNOWN_CONSTANT'
 */
echo UNKNOWN_CONSTANT;

Когда не конвертируют типы данных:

/**
 Notice: Array to string conversion
 */
echo array();

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

PHP E_NOTICE in PHPStorm

E_STRICT
Это ошибки, которые научат вас писать код правильно, чтобы не было стыдно, тем более IDE вам эти ошибки сразу показывают. Вот например, если вызвали не статический метод как статику, то код будет работать, но это как-то неправильно, и возможно появление серьёзных ошибок, если в дальнейшем метод класса будет изменён, и появится обращение к $this:

/**
 Strict standards: Non-static method Strict::test() should not be called statically
 */
class Strict { 
    public function test() { 
        echo 'Test'; 
    } 
}

Strict::test();

E_DEPRECATED
Так PHP будет ругаться, если вы используете устаревшие функции (т.е. те, что помечены как deprecated, и в следующем мажорном релизе их не будет):

/**
 Deprecated: Function split() is deprecated
 */

// популярная функция, всё никак не удалят из PHP
// deprecated since 5.3
split(',', 'a,b');

В моём редакторе подобные функции будут зачёркнуты:

PHP E_DEPRECATED in PHPStorm

Обрабатываемые

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

  • E_USER_ERROR – критическая ошибка
  • E_USER_WARNING – не критическая ошибка
  • E_USER_NOTICE – сообщения которые не являются ошибками

Отдельно стоит отметить E_USER_DEPRECATED – этот вид всё ещё используется очень часто для того, чтобы напомнить программисту, что метод или функция устарели и пора переписать код без использования оной. Для создания этой и подобных ошибок используется функция trigger_error():

/**
 * @deprecated Deprecated since version 1.2, to be removed in 2.0
 */
function generateToken() {
    trigger_error('Function `generateToken` is deprecated, use class `Token` instead', E_USER_DEPRECATED);
    // ...
    // code ...
    // ...
}

Теперь, когда вы познакомились с большинством видов и типов ошибок, пора озвучить небольшое пояснение по работе директивы display_errors:

  • если display_errors = on, то в случае ошибки браузер получит html c текстом ошибки и кодом 200
  • если же display_errors = off, то для фатальных ошибок код ответа будет 500 и результат не будет возвращён пользователю, для остальных ошибок – код будет работать неправильно, но никому об этом не расскажет

Приручение

Для работы с ошибками в PHP существует 3 функции:

  • set_error_handler() — устанавливает обработчик для ошибок, которые не обрывают работу скрипта (т.е. для не фатальных ошибок)
  • error_get_last() — получает информацию о последней ошибке
  • register_shutdown_function() — регистрирует обработчик который будет запущен при завершении работы скрипта. Данная функция не относится непосредственно к обработчикам ошибок, но зачастую используется именно для этого

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

  • $errno – первый аргумент содержит тип ошибки в виде целого числа
  • $errstr – второй аргумент содержит сообщение об ошибке
  • $errfile – необязательный третий аргумент содержит имя файла, в котором произошла ошибка
  • $errline – необязательный четвертый аргумент содержит номер строки, в которой произошла ошибка
  • $errcontext – необязательный пятый аргумент содержит массив всех переменных, существующих в области видимости, где произошла ошибка

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

<?php
    // включаем отображение всех ошибок, кроме E_NOTICE
    error_reporting(E_ALL & ~E_NOTICE);
    ini_set('display_errors', 1);
    
    // наш обработчик ошибок
    function myHandler($level, $message, $file, $line, $context) {
        // в зависимости от типа ошибки формируем заголовок сообщения
        switch ($level) {
            case E_WARNING:
                $type = 'Warning';
                break;
            case E_NOTICE:
                $type = 'Notice';
                break;
            default;
                // это не E_WARNING и не E_NOTICE
                // значит мы прекращаем обработку ошибки
                // далее обработка ложится на сам PHP
                return false;
        }
        // выводим текст ошибки
        echo "<h2>$type: $message</h2>";
        echo "<p><strong>File</strong>: $file:$line</p>";
        echo "<p><strong>Context</strong>: $". join(', $', array_keys($context))."</p>";
        // сообщаем, что мы обработали ошибку, и дальнейшая обработка не требуется
        return true;
    }
    
    // регистрируем наш обработчик, он будет срабатывать на для всех типов ошибок
    set_error_handler('myHandler', E_ALL);

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

С обработчиком, который написан выше есть одна существенная проблема – он не ловит фатальные ошибки, и вместо сайта пользователи увидят лишь пустую страницу, либо, что ещё хуже, сообщение об ошибке. Дабы не допустить подобного сценария следует воспользоваться функцией register_shutdown_function() и с её помощью зарегистрировать функцию, которая всегда будет выполняться по окончанию работы скрипта:

function shutdown() {
    echo 'Этот текст будет всегда отображаться';
}
register_shutdown_function('shutdown');

Данная функция будет срабатывать всегда!

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

function shutdown() {
    $error = error_get_last();
    if (
        // если в коде была допущена ошибка
        is_array($error) &&
        // и это одна из фатальных ошибок
        in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])
    ) {
        // очищаем буфер вывода (о нём мы ещё поговорим в последующих статьях)
        while (ob_get_level()) {
            ob_end_clean();
        }
        // выводим описание проблемы
        echo 'Сервер находится на техническом обслуживании, зайдите позже';
    }
}
register_shutdown_function('shutdown');

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

О прожорливости

Проведём простой тест, и выясним – сколько драгоценных ресурсов кушает самая тривиальная ошибка:

/**
 * Этот код не вызывает ошибок
 */

// сохраняем параметры памяти и времени выполнения скрипта
$memory = memory_get_usage();
$time= microtime(true);

$a = '';
$arr = [];
for ($i = 0; $i < 10000; $i++) {
    $arr[$a] = $i;
}

printf('%f seconds <br/>', microtime(true) - $time);
echo number_format(memory_get_usage() - $memory, 0, '.', ' '), ' bytes<br/>';

В результате запуска данного скрипта у меня получился вот такой результат:

0.002867 seconds 
984 bytes

Теперь добавим ошибку в цикле:

/**
 * Этот код содержит ошибку
 */

// сохраняем параметры памяти и времени выполнения скрипта
$memory = memory_get_usage();
$time= microtime(true);

$a = '';
$arr = [];
for ($i = 0; $i < 10000; $i++) {
    $arr[$b] = $i; // тут ошиблись с именем переменной
}

printf('%f seconds <br/>', microtime(true) - $time);
echo number_format(memory_get_usage() - $memory, 0, '.', ' '), ' bytes<br/>';

Результат ожидаемо хуже, и на порядок (даже на два порядка!):

0.263645 seconds 
992 bytes

Вывод однозначен – ошибки в коде приводят к лишней прожорливости скриптов – так что во время разработки и тестирования приложения включайте отображение всех ошибок!

Тестирование проводил на PHP версии 5.6, в седьмой версии результат лучше – 0.0004 секунды против 0.0050 – разница только на один порядок, но в любом случае результат стоит прикладываемых усилий по исправлению ошибок

Где собака зарыта

В PHP есть спец символ «@» – оператор подавления ошибок, его используют дабы не писать обработку ошибок, а положится на корректное поведение PHP в случае чего:

<?php
    echo @UNKNOWN_CONSTANT;

При этом обработчик ошибок указанный в set_error_handler() всё равно будет вызван, а факт того, что к ошибке было применено подавление можно отследить вызвав функцию error_reporting() внутри обработчика, в этом случае она вернёт 0.

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

Исключения

В эру PHP4 не было исключений (exceptions), всё было намного сложнее, и разработчики боролись с ошибками как могли, это было сражение не на жизнь, а на смерть… Окунуться в эту увлекательную историю противостояния можете в статье Исключительный код. Часть 1. Стоит ли её читать сейчас? Думаю да, ведь это поможет вам понять эволюцию языка, и раскроет всю прелесть исключений

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

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

Исключение – это объект который наследуется от класса Exception, содержит текст ошибки, статус, а также может содержать ссылку на другое исключение которое стало первопричиной данного. Модель исключений в PHP схожа с используемыми в других языках программирования. Исключение можно инициировать (как говорят, “бросить”) при помощи оператора throw, и можно перехватить (“поймать”) оператором catch. Код генерирующий исключение, должен быть окружен блоком try, для того чтобы можно было перехватить исключение. Каждый блок try должен иметь как минимум один соответствующий ему блок catch или finally:

try {
    // код который может выбросить исключение
    if (rand(0, 1)) {
        throw new Exception('One')
    } else {
        echo 'Zero';
    }
} catch (Exception $e) {
    // код который может обработать исключение
    echo $e->getMessage();
}

В каких случаях стоит применять исключения:

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

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

$directory = __DIR__ . DIRECTORY_SEPARATOR . 'logs';

// директории может не быть
if (!is_dir($directory)) {
    throw new Exception('Directory `logs` is not exists');
}

// может не быть прав на запись в директорию
if (!is_writable($directory)) {
    throw new Exception('Directory `logs` is not writable');
}

// возможно кто-то уже создал файл, и закрыл к нему доступ
if (!$file = @fopen($directory . DIRECTORY_SEPARATOR . date('Y-m-d') . '.log', 'a+')) {
    throw new Exception('System can't create log file');
}

fputs($file, date('[H:i:s]') . " donen");
fclose($file);

Соответственно ловить данные исключения будем примерно так:

try {
    // код который пишет в файл
    // ...
} catch (Exception $e) {
    // выводим текст ошибки
    echo 'Не получилось: '. $e->getMessage();
}

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

// исключения файловой системы
class FileSystemException extends Exception {}

// исключения связанные с директориями
class DirectoryException extends FileSystemException {
    // коды исключений
    const DIRECTORY_NOT_EXISTS =  1;
    const DIRECTORY_NOT_WRITABLE = 2;
}

// исключения связанные с файлами
class FileException extends FileSystemException {}

Теперь, если использовать эти исключения то можно получить следующий код:

try {
    // код который пишет в файл
    if (!is_dir($directory)) {
        throw new DirectoryException('Directory `logs` is not exists', DirectoryException::DIRECTORY_NOT_EXISTS);
    }

    if (!is_writable($directory)) {
        throw new DirectoryException('Directory `logs` is not writable', DirectoryException::DIRECTORY_NOT_WRITABLE);
    }

    if (!$file = @fopen($directory . DIRECTORY_SEPARATOR . date('Y-m-d') . '.log', 'a+')) {
        throw new FileException('System can't open log file');
    }

    fputs($file, date('[H:i:s]'') . " donen");
    fclose($file);
} catch (DirectoryException $e) {
    echo 'С директорией возникла проблема: '. $e->getMessage();
} catch (FileException $e) {
    echo 'С файлом возникла проблема: '. $e->getMessage();
} catch (FileSystemException $e) {
    echo 'Ошибка файловой системы: '. $e->getMessage();
} catch (Exception $e) {
    echo 'Ошибка сервера: '. $e->getMessage();
}

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

Так, а что будет если не поймать исключение? Вы получите “Fatal Error: Uncaught exception …”. Неприятно.
Чтобы избежать подобной ситуации следует использовать функцию set_exception_handler() и установить обработчик для исключений, которые брошены вне блока try-catch и не были обработаны. После вызова такого обработчика выполнение скрипта будет остановлено:

// в качестве обработчика событий 
// будем использовать анонимную функцию
set_exception_handler(function($exception) {
    /** @var Exception $exception */
    echo $exception->getMessage(), "<br/>n";
    echo $exception->getFile(), ':', $exception->getLine(), "<br/>n";
    echo $exception->getTraceAsString(), "<br/>n";
});

Ещё расскажу про конструкцию с использованием блока finally – этот блок будет выполнен вне зависимости от того, было выброшено исключение или нет:

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

Для понимания того, что это нам даёт приведу следующий пример использования блока finally:

try {
    // где-то глубоко внутри кода
    // соединение с базой данных
    $handler = mysqli_connect('localhost', 'root', '', 'test');

    try {
        // при работе с БД возникла исключительная ситуация
        // ...
        throw new Exception('DB error');
    } catch (Exception $e) {
        // исключение поймали, обработали на своём уровне
        // и должны его пробросить вверх, для дальнейшей обработки
        throw new Exception('Catch exception', 0, $e);
    } finally {
        // но, соединение с БД необходимо закрыть
        // будем делать это в блоке finally
        mysqli_close($handler);
    }

    // этот код не будет выполнен, если произойдёт исключение в коде выше
    echo "Ok";
} catch (Exception $e) {
    // ловим исключение, и выводим текст
    echo $e->getMessage();
    echo "<br/>";
    // выводим информацию о первоначальном исключении
    echo $e->getPrevious()->getMessage();
}

Т.е. запомните – блок finally будет выполнен даже в том случае, если вы в блоке catch пробрасываете исключение выше (собственно именно так он и задумывался).

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

Задание
Написать свой обработчик исключений, с выводом текста файла где произошла ошибка, и всё это с подсветкой синтаксиса, так же не забудьте вывести trace в читаемом виде. Для ориентира – посмотрите как это круто выглядит у whoops.

PHP7 – всё не так, как было раньше

Так, вот вы сейчас всю информацию выше усвоили и теперь я буду грузить вас нововведениями в PHP7, т.е. я буду рассказывать о том, с чем вы столкнётесь через год работы PHP разработчиком. Ранее я вам рассказывал и показывал на примерах какой костыль нужно соорудить, чтобы отлавливать критические ошибки, так вот – в PHP7 это решили исправить, но как обычно завязались на обратную совместимость кода, и получили хоть и универсальное решение, но оно далеко от идеала. А теперь по пунктам об изменениях:

  1. при возникновении фатальных ошибок типа E_ERROR или фатальных ошибок с возможностью обработки E_RECOVERABLE_ERROR PHP выбрасывает исключение
  2. эти исключения не наследуют класс Exception (помните я говорил об обратной совместимости, это всё ради неё)
  3. эти исключения наследуют класс Error
  4. оба класса Exception и Error реализуют интерфейс Throwable
  5. вы не можете реализовать интерфейс Throwable в своём коде

Интерфейс Throwable практически полностью повторяет нам Exception:

interface Throwable
{
    public function getMessage(): string;
    public function getCode(): int;
    public function getFile(): string;
    public function getLine(): int;
    public function getTrace(): array;
    public function getTraceAsString(): string;
    public function getPrevious(): Throwable;
    public function __toString(): string;
}

Сложно? Теперь на примерах, возьмём те, что были выше и слегка модернизируем:

try {
    // файл, который вызывает ошибку парсера
    include 'e_parse_include.php';
} catch (Error $e) {
    var_dump($e);
}

В результате ошибку поймаем и выведем:

object(ParseError)#1 (7) {
  ["message":protected] => string(48) "syntax error, unexpected 'будет' (T_STRING)"
  ["string":"Error":private] => string(0) ""
  ["code":protected] => int(0)
  ["file":protected] => string(49) "/www/education/error/e_parse_include.php"
  ["line":protected] => int(4)
  ["trace":"Error":private] => array(0) { }
  ["previous":"Error":private] => NULL
}

Как видите – поймали исключение ParseError, которое является наследником исключения Error, который реализует интерфейс Throwable, в доме который построил Джек. Ещё есть другие, но не буду мучать – для наглядности приведу иерархию исключений:

interface Throwable
  |- Exception implements Throwable
  |    |- ErrorException extends Exception
  |    |- ... extends Exception
  |    `- ... extends Exception
  `- Error implements Throwable
      |- TypeError extends Error
      |- ParseError extends Error
      |- ArithmeticError extends Error
      |  `- DivisionByZeroError extends ArithmeticError
      `- AssertionError extends Error 

TypeError – для ошибок, когда тип аргументов функции не совпадает с передаваемым типом:

try {
    (function(int $one, int $two) {
        return;
    })('one', 'two');
} catch (TypeError $e) {
    echo $e->getMessage();
}

ArithmeticError – могут возникнуть при математических операциях, к примеру когда результат вычисления превышает лимит выделенный для целого числа:

try {
    1 << -1;
} catch (ArithmeticError $e) {
    echo $e->getMessage();
}

DivisionByZeroError – ошибка деления на ноль:

try {
    1 / 0;
} catch (ArithmeticError $e) {
    echo $e->getMessage();
}

AssertionError – редкий зверь, появляется когда условие заданное в assert() не выполняется:

ini_set('zend.assertions', 1);
ini_set('assert.exception', 1);

try {
    assert(1 === 0);
} catch (AssertionError $e) {
    echo $e->getMessage();
}

При настройках production-серверов, директивы zend.assertions и assert.exception отключают, и это правильно

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

При написании данного раздела были использованы материалы из статьи Throwable Exceptions and Errors in PHP 7

Отладка

Иногда для отладки кода нужно отследить что происходило с переменной или объектом на определённом этапе, для этих целей есть функция debug_backtrace() и debug_print_backtrace() которые вернут историю вызовов функций/методов в обратном порядке:

<?php
function example() {
    echo '<pre>';
    debug_print_backtrace();
    echo '</pre>';
}

class ExampleClass {
    public static function method () {
        example();
    }
}

ExampleClass::method();

В результате выполнения функции debug_print_backtrace() будет выведен список вызовов приведших нас к данной точке:

#0  example() called at [/www/education/error/backtrace.php:10]
#1  ExampleClass::method() called at [/www/education/error/backtrace.php:14]

Проверить код на наличие синтаксических ошибок можно с помощью функции php_check_syntax() или же команды php -l [путь к файлу], но я не встречал использования оных.

Assert

Отдельно хочу рассказать о таком экзотическом звере как assert() в PHP, собственно это кусочек контрактной методологии программирования, и дальше я расскажу вам как я никогда его не использовал 🙂

Первый случай – это когда вам надо написать TODO прямо в коде, да так, чтобы точно не забыть реализовать заданный функционал:

// включаем вывод ошибок
error_reporting(E_ALL);
ini_set('display_errors', 1);

// включаем asserts
ini_set('zend.assertions', 1);
ini_set('assert.active', 1);

assert(false, "Remove it!");

В результате выполнения данного кода получим E_WARNING:

Warning: assert(): Remove it! failed

PHP7 можно переключить в режим exception, и вместо ошибки будет всегда появляться исключение AssertionError:

// включаем asserts
ini_set('zend.assertions', 1);
ini_set('assert.active', 1);
// переключаем на исключения
ini_set('assert.exception', 1);

assert(false, "Remove it!");

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

assert(false, new Exception("Remove it!"));

Но я бы рекомендовал использовать метки @TODO, современные IDE отлично с ними работают, и вам не нужно будет прикладывать дополнительные усилия и ресурсы для работы с ними

Второй вариант использования – это создание некоего подобия TDD, но помните – это лишь подобие. Хотя, если сильно постараться, то можно получить забавный результат, который поможет в тестировании вашего кода:

// callback-функция для вывода информации в браузер
function backlog($script, $line, $code, $message) {
    echo "<h3>$message</h3>";
    highlight_string ($code);
}

// устанавливаем callback-функцию
assert_options(ASSERT_CALLBACK, 'backlog');
// отключаем вывод предупреждений
assert_options(ASSERT_WARNING,  false);

// пишем проверку и её описание
assert("sqr(4) == 16", "When I send integer, function should return square of it");

// функция, которую проверяем
function sqr($a) {
    return; // она не работает
}

Третий теоретический вариант – это непосредственно контрактное программирование – когда вы описали правила использования своей библиотеки, но хотите точно убедится, что вас поняли правильно, и в случае чего сразу указать разработчику на ошибку (я вот даже не уверен, что правильно его понимаю, но пример кода вполне рабочий):

/**
 * Настройки соединения должны передаваться в следующем виде
 *
 *     [
 *         'host' => 'localhost',
 *         'port' => 3306,
 *         'name' => 'dbname',
 *         'user' => 'root',
 *         'pass' => ''
 *     ]
 *
 * @param $settings
 */
function setupDb ($settings) {
    // проверяем настройки
    assert(isset($settings['host']), 'Db `host` is required');
    assert(isset($settings['port']) && is_int($settings['port']), 'Db `port` is required, should be integer');
    assert(isset($settings['name']), 'Db `name` is required, should be integer');

    // соединяем с БД
    // ...
}

setupDb(['host' => 'localhost']);

Никогда не используйте assert() для проверки входных параметров, ведь фактически assert() интерпретирует строковую переменную (ведёт себя как eval()), а это чревато PHP-инъекцией. И да, это правильное поведение, т.к. просто отключив assert’ы всё что передаётся внутрь будет проигнорировано, а если делать как в примере выше, то код будет выполняться, а внутрь отключенного assert’a будет передан булевый результат выполнения

Если у вас есть живой опыт использования assert() – поделитесь со мной, буду благодарен. И да, вот вам ещё занимательно чтива по этой теме – PHP Assertions, с таким же вопросом в конце 🙂

В заключение

Я за вас напишу выводы из данной статьи:

  • Ошибкам бой – их не должно быть в вашем коде
  • Используйте исключения – работу с ними нужно правильно организовать и будет счастье
  • Assert – узнали о них, и хорошо

P.S. Спасибо Максиму Слесаренко за помощь в написании статьи

The header() function is what you need.

void header ( string $string [, bool $replace = true [, int $http_response_code ]] )

Place it right after the first <?php tag for the script rendering the response output.

You should refer http://php.net/manual/en/function.header.php

answered Oct 16, 2012 at 11:26

egghese's user avatar

eggheseegghese

2,19316 silver badges26 bronze badges

Look at the header() function:

header('HTTP/1.0 404 Not Found');

Put that before any output of your script.

answered Oct 16, 2012 at 11:25

Matt Humphrey's user avatar

Matt HumphreyMatt Humphrey

1,5541 gold badge12 silver badges30 bronze badges

Send a header() before you send anything else to the client. Some possible status headers follow:

header('HTTP/1.1 200 OK');

// Page was not found:
header('HTTP/1.1 404 Not Found');

// Access forbidden:
header('HTTP/1.1 403 Forbidden');

// The page moved permanently should be used for
// all redrictions, because search engines know
// what's going on and can easily update their urls.
header('HTTP/1.1 301 Moved Permanently');

// Server error
header('HTTP/1.1 500 Internal Server Error');

// header for telling the browser that the content
// did not get changed
header('HTTP/1.1 304 Not Modified');

answered Oct 16, 2012 at 11:26

Richard's user avatar

RichardRichard

53.4k31 gold badges175 silver badges245 bronze badges

The header() function is what you need.

void header ( string $string [, bool $replace = true [, int $http_response_code ]] )

Place it right after the first <?php tag for the script rendering the response output.

You should refer http://php.net/manual/en/function.header.php

answered Oct 16, 2012 at 11:26

egghese's user avatar

eggheseegghese

2,19316 silver badges26 bronze badges

Look at the header() function:

header('HTTP/1.0 404 Not Found');

Put that before any output of your script.

answered Oct 16, 2012 at 11:25

Matt Humphrey's user avatar

Matt HumphreyMatt Humphrey

1,5541 gold badge12 silver badges30 bronze badges

Send a header() before you send anything else to the client. Some possible status headers follow:

header('HTTP/1.1 200 OK');

// Page was not found:
header('HTTP/1.1 404 Not Found');

// Access forbidden:
header('HTTP/1.1 403 Forbidden');

// The page moved permanently should be used for
// all redrictions, because search engines know
// what's going on and can easily update their urls.
header('HTTP/1.1 301 Moved Permanently');

// Server error
header('HTTP/1.1 500 Internal Server Error');

// header for telling the browser that the content
// did not get changed
header('HTTP/1.1 304 Not Modified');

answered Oct 16, 2012 at 11:26

Richard's user avatar

RichardRichard

53.4k31 gold badges175 silver badges245 bronze badges

PHP фатальные ошибки возвращаются в качестве кода состояния 200 для HTTP-клиента. Как я могу заставить его вернуть код состояния 500 (Внутренняя ошибка сервера)?

 header("HTTP/1.1 500 Internal Server Error"); 

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

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

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

 function my_error_handler() { $last_error = error_get_last(); if ($last_error && $last_error['type']==E_ERROR) { header("HTTP/1.1 500 Internal Server Error"); echo '...';//html for 500 page } } register_shutdown_function('my_error_handler'); 

Примечание. Если вы хотите поймать собственный тип ошибки, который начинается с E_USER *, вы можете использовать функцию set_error_handler () для регистрации обработчика ошибок и запускать ошибку с помощью функции trigger_error, однако этот обработчик ошибок не может обрабатывать тип ошибки E_ERROR. см. объяснение на php.net о обработчике ошибок

Я использовал «set_exception_handler» для обработки исключений uncaught.

 function handleException($ex) { error_log("Uncaught exception class=" . get_class($ex) . " message=" . $ex->getMessage() . " line=" . $ex->getLine()); ob_end_clean(); # try to purge content sent so far header('HTTP/1.1 500 Internal Server Error'); echo 'Internal error'; } set_exception_handler('handleException'); 

Невозможно обрабатывать PHP E_ERROR каким-либо образом в соответствии с документацией PHP: http://www.php.net/manual/en/function.set-error-handler.php

Также нельзя обрабатывать «E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING и большую часть E_STRICT» в соответствии с этой ссылкой.

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

И, конечно же, вы можете поймать любое исключение (даже те, которые бросают собственные PHP-функции).

Я согласен, что это проблема. Серверы НЕ должны возвращать 200 OK, когда код приложения сбой и ожоги.

Вам придется поймать заброшенную ошибку с помощью try / catch, а затем использовать этот блок catch для отправки заголовка () с ошибкой 500.

 try { ...badcode... throw new Exception('error'); } catch (Exception $e) { header("Status: 500 Server Error"); var_dump($e->getMessage()); } 

Если фатальное исключение не окружено блоками try {} catch, вы должны зарегистрировать глобальный обработчик и использовать register_shutdown_function() чтобы проверить наличие ошибки на конце скрипта.

Никогда не забудьте установить header("HTTP/1.1 200 OK", true, 200); как последняя строка любого пути выполнения:

 //first things first: header("HTTP/1.1 500 Internal Server Error", true, 500); //Application code, includes, requires, etc. [...] //somewhere something happens //die(); throw new Exception("Uncaught exception!"); //last things last, only reached if code execution was not stopped by uncaught exception or some fatal error header("HTTP/1.1 200 OK", true, 200); 

В PHP 5.4 вы можете заменить вышеописанную функцию header гораздо лучше http_response_code(200) или http_response_code(500) .

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

рок-твердое тело-script.php:

 // minimize changes to this script to keep it rock-solid http_response_code(500); // PHP >= 5.4 require_once("script-i-want-to-guard-for-errors.php"); 

скрипт-я-хочу-к-охранником-для-errors.php:

 // do all the processsing // don't produce any output // you might want to use output buffering http_response_code(200); // PHP >= 5.4 // here you can produce the output 

Направьте свой звонок на rock-solid-script.php, и вы готовы к работе.

Мне бы лучше было установить код состояния по умолчанию на 500 в .htaccess. Это кажется мне более элегантным, но я не могу найти способ его снять. Я попробовал флаг RewriteRule R, но это предотвращает выполнение php вообще, так что это бесполезно.

Стандартная конфигурация PHP возвращает 500 при возникновении ошибки! Просто убедитесь, что ваш display_errors = выключен. Вы можете имитировать его с помощью:

 ini_set('display_errors', 0); noFunction(); 

По умолчанию директива display_errors отключена по умолчанию.

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

Статья разбита на четыре раздела:

  1. Классификация ошибок.
  2. Пример, демонстрирующий различные виды ошибок и его поведение при различных настройках.
  3. Написание собственного обработчика ошибок.
  4. Полезные ссылки.

Классификация ошибок

Все ошибки, условно, можно разбить на категории по нескольким критериям.
Фатальность:

  • Фатальные
    Неустранимые ошибки. Работа скрипта прекращается.
    E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR.
  • Не фатальные
    Устранимые ошибки. Работа скрипта не прекращается.
    E_WARNING, E_NOTICE, E_CORE_WARNING, E_COMPILE_WARNING, E_USER_WARNING, E_USER_NOTICE, E_STRICT, E_DEPRECATED, E_USER_DEPRECATED.
  • Смешанные
    Фатальные, но только, если не обработаны функцией, определенной пользователем в set_error_handler().
    E_USER_ERROR, E_RECOVERABLE_ERROR.

Возможность перехвата ошибки функцией, определенной в set_error_handler():

  • Перехватываемые (не фатальные и смешанные)
    E_USER_ERROR, E_RECOVERABLE_ERROR, E_WARNING, E_NOTICE, E_USER_WARNING, E_USER_NOTICE, E_STRICT, E_DEPRECATED, E_USER_DEPRECATED.
  • Не перехватываемые (фатальные)
    E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING.

Инициатор:

  • Инициированы пользователем
    E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE.
  • Инициированы PHP
    E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_WARNING, E_NOTICE, E_CORE_WARNING, E_COMPILE_WARNING, E_STRICT, E_DEPRECATED, E_USER_DEPRECATED, E_USER_ERROR, E_RECOVERABLE_ERROR.

Для нас, в рамках данной статьи, наиболее интересны классификации по первым двум критериям, о чем будет рассказано далее.

Примеры возникновения ошибок

Листинг index.php

<?php
// определеяем уровень протоколирования ошибок
error_reporting(E_ALL | E_STRICT);
// определяем режим вывода ошибок
ini_set('display_errors', 'On');
// подключаем файл с ошибками
require 'errors.php';

Листинг errors.php

<?php
echo "Файл с ошибками. Начало<br>";
/*
 * перехватываемые ошибки (ловятся функцией set_error_handler())
 */
// NONFATAL - E_NOTICE
// echo $undefined_var;
// NONFATAL - E_WARNING
// array_key_exists('key', NULL);
// NONFATAL - E_DEPRECATED
split('[/.-]', "12/21/2012"); // split() deprecated начиная с php 5.3.0
// NONFATAL - E_STRICT
// class c {function f(){}} c::f();
// NONFATAL - E_USER_DEPRECATED
// trigger_error("E_USER_DEPRECATED", E_USER_DEPRECATED);
// NONFATAL - E_USER_WARNING
// trigger_error("E_USER_WARNING", E_USER_WARNING);
// NONFATAL - E_USER_NOTICE
// trigger_error("E_USER_NOTICE", E_USER_NOTICE);

// FATAL, если не обработана функцией set_error_handler - E_RECOVERABLE_ERROR
// class b {function f(int $a){}} $b = new b; $b->f(NULL);
// FATAL, если не обработана функцией set_error_handler - E_USER_ERROR
// trigger_error("E_USER_ERROR", E_USER_ERROR);

/*
 * неперехватываемые (не ловятся функцией set_error_handler())
 */
// FATAL - E_ERROR
// undefined_function();
// FATAL - E_PARSE
// parse_error
// FATAL - E_COMPILE_ERROR
// $var[];

echo "Файл с ошибками. Конец<br>";

Примечание: для полной работоспособности скрипта необходим PHP версии не ниже 5.3.0.

В файле errors.php представлены выражения, инициирующие практически все возможные ошибки. Исключение составили: E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_WARNING, генерируемые ядром Zend. В теории, встретить их в реальной работе вы не должны.
В следующей таблице приведены варианты поведения этого скрипта в различных условиях (в зависимости от значений директив display_errors и error_reporting):

Группа ошибок Значения директив* Статус ответа сервера Ответ клиенту**
E_PARSE, E_COMPILE_ERROR*** display_errors = off
error_reporting = ANY
500 Пустое значение
display_errors = on
error_reporting = ANY
200 Сообщение об ошибке
E_USER_ERROR, E_ERROR, E_RECOVERABLE_ERROR display_errors = off
error_reporting = ANY
500 Вывод скрипта до ошибки
display_errors = on
error_reporting = ANY
200 Сообщение об ошибке и вывод скрипта до ошибки
Не фатальные ошибки display_errors = off
error_reporting = ANY
и
display_errors = on
error_reporting = 0
200 Весь вывод скрипта
display_errors = on
error_reporting = E_ALL | E_STRICT
200 Сообщение об ошибке и весь вывод скрипта

* Значение ANY означает E_ALL | E_STRICT или 0.
** Ответ клиенту может отличаться от ответов на реальных скриптах. Например, вывод какой-либо информации до включения файла errors.php, будет фигурировать во всех рассмотренных случаях.
*** Если в файле errors.php заменить пример для ошибки E_COMPILE_ERROR на require "missing_file.php";, то ошибка попадет во вторую группу.

Значение, приведенной выше, таблицы можно описать следующим образом:

  1. Наличие в файле скрипта ошибки, приводящей его в «негодное» состояние (невозможность корректно обработать), на выходе даст пустое значение или же только само сообщение об ошибке, в зависимости от значения директивы display_errors.
  2. Скрипт в файле с фатальной ошибкой, не относящейся к первому пункту, будет выполняться в штатном режиме до самой ошибки.
  3. Наличие в файле фатальной ошибки при display_errors = Off обозначит 500 статус ответа.
  4. Не фатальные ошибки, как и следовало ожидать, в контексте возможности исполнения скрипта в целом, на работоспособность не повлияют.

Собственный обработчик ошибок

Для написания собственного обработчика ошибок необходимо знать, что:

  • для получения информации о последней произошедшей ошибке существует функция error_get_last();
  • для определения собственного обработчика ошибок существует функция set_error_handler(), но фатальные ошибки нельзя «перехватить» этой функцией;
  • используя register_shutdown_function(), можно зарегистрировать свою функцию, выполняемую по завершении работы скрипта, и в ней, используя знания из первого пункта, если фатальная ошибка имела место быть, предпринять необходимые действия;
  • сообщение о фатальной ошибке в любом случае попадет в буфер вывода;
  • воспользовавшись функциями контроля вывода можно предотвратить отображение нежелательной информации;
  • при использовании оператора управления ошибками (знак @) функция, определенная в set_error_handler() все равно будет вызвана, но функция error_reporting() в этом случае вернет 0, чем и можно пользоваться для прекращения работы или определения другого поведения своего обработчика ошибок.

Третий пункт поясню: зарегистрированная нами функция при помощи register_shutdown_function() выполнится в любом случае — корректно ли завершился скрипт, либо же был прерван в связи с критичной (фатальной) ошибкой. Второй вариант мы можем однозначно определить, воспользовавшись информацией предоставленной функцией error_get_last(), и, если ошибка все же была, выполнить наш собственный обработчик ошибок.
Продемонстрируем вышесказанное на модифицированном скрипте index.php:

<?php
/**
 * Обработчик ошибок
 * @param int $errno уровень ошибки
 * @param string $errstr сообщение об ошибке
 * @param string $errfile имя файла, в котором произошла ошибка
 * @param int $errline номер строки, в которой произошла ошибка
 * @return boolean
 */
function error_handler($errno, $errstr, $errfile, $errline)
{
    // если ошибка попадает в отчет (при использовании оператора "@" error_reporting() вернет 0)
    if (error_reporting() & $errno)
    {
        $errors = array(
            E_ERROR => 'E_ERROR',
            E_WARNING => 'E_WARNING',
            E_PARSE => 'E_PARSE',
            E_NOTICE => 'E_NOTICE',
            E_CORE_ERROR => 'E_CORE_ERROR',
            E_CORE_WARNING => 'E_CORE_WARNING',
            E_COMPILE_ERROR => 'E_COMPILE_ERROR',
            E_COMPILE_WARNING => 'E_COMPILE_WARNING',
            E_USER_ERROR => 'E_USER_ERROR',
            E_USER_WARNING => 'E_USER_WARNING',
            E_USER_NOTICE => 'E_USER_NOTICE',
            E_STRICT => 'E_STRICT',
            E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
            E_DEPRECATED => 'E_DEPRECATED',
            E_USER_DEPRECATED => 'E_USER_DEPRECATED',
        );

        // выводим свое сообщение об ошибке
        echo "<b>{$errors[$errno]}</b>[$errno] $errstr ($errfile на $errline строке)<br />n";
    }

    // не запускаем внутренний обработчик ошибок PHP
    return TRUE;
}

/**
 * Функция перехвата фатальных ошибок
 */
function fatal_error_handler()
{
    // если была ошибка и она фатальна
    if ($error = error_get_last() AND $error['type'] & ( E_ERROR | E_PARSE | E_COMPILE_ERROR | E_CORE_ERROR))
    {
        // очищаем буффер (не выводим стандартное сообщение об ошибке)
        ob_end_clean();
        // запускаем обработчик ошибок
        error_handler($error['type'], $error['message'], $error['file'], $error['line']);
    }
    else
    {
        // отправка (вывод) буфера и его отключение
        ob_end_flush();
    }
}

// определеяем уровень протоколирования ошибок
error_reporting(E_ALL | E_STRICT);
// определяем режим вывода ошибок
ini_set('display_errors', 'On');
// включаем буфферизацию вывода (вывод скрипта сохраняется во внутреннем буфере)
ob_start();
// устанавливаем пользовательский обработчик ошибок
set_error_handler("error_handler");
// регистрируем функцию, которая выполняется после завершения работы скрипта (например, после фатальной ошибки)
register_shutdown_function('fatal_error_handler');

require 'errors.php';

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

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

Полезные ссылки

  • Первоисточник: php.net/manual/ru/book.errorfunc.php
  • Описание ошибок: php.net/manual/ru/errorfunc.constants.php
  • Функции контроля вывода: php.net/manual/ru/ref.outcontrol.php
  • Побитовые операторы: php.net/manual/ru/language.operators.bitwise.php и habrahabr.ru/post/134557
  • Тематически близкая статья: habrahabr.ru/post/134499

Неустранимые ошибки PHP возвращаются HTTP-клиенту как код состояния 200. Как я могу заставить его возвращать код состояния 500 (внутренняя ошибка сервера)?

10 ответы

header("HTTP/1.1 500 Internal Server Error");

ответ дан 12 окт ’09, 18:10

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

1) в первую очередь необходимо отловить фатальные ошибки PHP, а именно ошибку типа E_ERROR. при возникновении этой ошибки сценарий сохранит ошибку и прекратит выполнение. вы можете получить сохраненную ошибку, вызвав функцию error_get_last ().

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

function my_error_handler()
{
  $last_error = error_get_last();
  if ($last_error && $last_error['type']==E_ERROR)
      {
        header("HTTP/1.1 500 Internal Server Error");
        echo '...';//html for 500 page
      }
}
register_shutdown_function('my_error_handler');

Примечание: если вы хотите поймать пользовательский тип ошибки, который начинается с E_USER *, вы можете использовать функцию set_error_handler () для регистрации обработчика ошибок и запуска ошибки с помощью функции trigger_error, однако этот обработчик ошибок не может обрабатывать тип ошибки E_ERROR. см. объяснение на php.net о обработчик ошибок

Создан 15 ноя.

Я использовал set_exception_handler для обработки неперехваченных исключений.

function handleException($ex) {
      error_log("Uncaught exception class=" . get_class($ex) . " message=" . $ex->getMessage() . " line=" . $ex->getLine());
      ob_end_clean(); # try to purge content sent so far
      header('HTTP/1.1 500 Internal Server Error');
      echo 'Internal error';
    }

set_exception_handler('handleException');

Создан 23 сен.

Стандартная конфигурация PHP делает верните 500 при возникновении ошибки! Просто убедитесь, что ваш display_errors = off. Вы можете смоделировать это с помощью:

ini_set('display_errors', 0); 
noFunction();

На производстве директива display_errors по умолчанию отключена.

ответ дан 05 апр.

В соответствии с документацией PHP невозможно обработать PHP E_ERROR каким-либо образом:
http://www.php.net/manual/en/function.set-error-handler.php

Также невозможно обработать E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING и большую часть E_STRICT по этой ссылке.

Вы МОЖЕТЕ предоставить обработчик для других ошибок, предупреждений и уведомлений, включая E_USER_ERROR, но на самом деле это не так полезно, как кажется, поскольку эта ошибка возникает только намеренно программистом с помощью trigger_error ().

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

Я согласен, что это проблема. Серверы НЕ должны возвращать 200 OK при сбое и сгорании кода приложения.

Создан 24 янв.

Поскольку PHP> = 5.4

http_response_code(500);
echo json_encode( [ 'success' => false , 'message' => 'Crazy thing just happened!' ]);
exit();

Пожалуйста, установите httpCode перед echo.

Создан 16 июн.

ответ дан 12 окт ’09, 18:10

Вам нужно будет поймать брошенную ошибку, используя попробуй поймать а затем используйте этот блок catch для отправки заголовок () с ошибкой 500.

try {
    ...badcode...
    throw new Exception('error');

} catch (Exception $e) {

    header("Status: 500 Server Error");
    var_dump($e->getMessage());
}

Если фатальное исключение не окружено блоками try {} catch, вы должны зарегистрировать глобальный обработчик и использовать register_shutdown_function() чтобы проверить наличие ошибки в конце скрипта.

ответ дан 18 авг.

Никогда не забывайте устанавливать header("HTTP/1.1 200 OK", true, 200); как последняя строка любого пути выполнения:

//first things first:
header("HTTP/1.1 500 Internal Server Error", true, 500);


//Application code, includes, requires, etc. [...]


//somewhere something happens
//die();
throw new Exception("Uncaught exception!");


//last things last, only reached if code execution was not stopped by uncaught exception or some fatal error
header("HTTP/1.1 200 OK", true, 200);

In PHP 5.4 вы можете заменить header функция выше с гораздо лучшими http_response_code(200) or http_response_code(500).

ответ дан 01 авг.

Сложность при работе с фатальными ошибками (ошибками компиляции, например, отсутствием точки с запятой) заключается в том, что сценарий не будет выполняться, поэтому установка кода состояния в этом сценарии не поможет. Однако, когда вы включаете или требуете сценарий, вызывающий сценарий будет выполняться независимо от ошибок во включенном сценарии. Итак, я пришел к такому решению:

рок-твердый-скрипт.php:

// minimize changes to this script to keep it rock-solid
http_response_code(500); // PHP >= 5.4
require_once("script-i-want-to-guard-for-errors.php");

скрипт-я-хочу-охранять-для-ошибок.php:

// do all the processsing
// don't produce any output
// you might want to use output buffering

http_response_code(200); // PHP >= 5.4

// here you can produce the output

Направьте ваш звонок на rock-solid-script.php, и вы готовы к работе.

Я бы предпочел установить код состояния по умолчанию 500 в .htaccess. Мне это кажется более элегантным, но я не могу найти способ справиться с этим. Я пробовал использовать R-флаг RewriteRule, но он полностью предотвращает выполнение php, так что бесполезно.

Создан 23 фев.

Не тот ответ, который вы ищете? Просмотрите другие вопросы с метками

php
error-handling
error-code

or задайте свой вопрос.

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

  • Веб-серверу может не хватить места на диске;
  • Пользователь мог ввести недопустимое значение в поле формы;
  • Файл или запись базы данных, к которой вы пытались получить доступ, возможно, не существует;
  • Приложение может не иметь разрешения на запись в файл на диске;
  • Служба, к которой приложение должно получить доступ, может быть временно недоступна.

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

Профессиональное приложение должно иметь возможность изящно обрабатывать такие ошибки времени выполнения. Обычно это означает более четкое и точное информирование пользователя о проблеме.

Понимание уровней ошибок

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

Название Значение Описание
E_ERROR 1 Неустранимая ошибка времени выполнения от которой невозможно избавиться. Выполнение скрипта немедленно прекращается.
E_WARNING 2 Предупреждение во время выполнения. Она несущественна, и большинство ошибок попадают в эту категорию. Выполнение скрипта не останавливается.
E_NOTICE 8 Уведомление во время выполнения. Указывает, что скрипт обнаружил что-то, что могло быть ошибкой, хотя такая ситуация также может возникнуть при обычном запуске скрипта.
E_USER_ERROR 256 Сообщение о фатальной пользовательской ошибке. Она похожа на E_ERROR, за исключением того, что она генерируется PHP-скриптом с использованием функции trigger_error().
E_USER_WARNING 512 Предупреждающее сообщение, созданное пользователем без фатального исхода. Она похожа на E_WARNING, за исключением того, что она генерируется PHP-скриптом с использованием функции trigger_error().
E_USER_NOTICE 1024 Сообщение с уведомлением, созданное пользователем. Она похожа на E_NOTICE за исключением того, что она генерируется PHP-скриптом с использованием функции trigger_error().
E_STRICT 2048 Не совсем ошибка, но срабатывает всякий раз, когда PHP встречает код, который может привести к проблемам или несовместимости пересылки.
E_ALL 8191 Все ошибки и предупреждения, кроме E_STRICT до PHP 5.4.0.

Дополнительные сведения об уровнях ошибок см. в справочнике по уровням ошибок PHP.

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

Базовая обработка ошибок с помощью функции die()

Рассмотрим следующий пример, в котором просто попытаемся открыть текстовый файл только для чтения.

<?php
// Пробуем открыть несуществующий файл
$file = fopen("sample.txt", "r"); // Выводит: Warning: fopen(sample.txt) [function.fopen]: failed to open stream: No such file or directory in C:wampwwwprojecttest.php on line 2
?>

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

<?php
if(file_exists("sample.txt")){
    $file = fopen("sample.txt", "r");
} else{
    die("Error: The file you are trying to access doesn't exist.");
}
?>

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

Используемая выше функция die() просто отображает пользовательское сообщение об ошибке и завершает текущий скрипт, если файл sample.txt не найден.

Создание собственного обработчика ошибок

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

Функция пользовательского обработчика ошибок должна иметь возможность обрабатывать как минимум два параметра (errno и errstr), однако она может дополнительно принимать три дополнительных параметра (errfile, errline и errcontext), как описано ниже:

Параметр Описание
Обязательно — следующие параметры обязательны
errno. Задает уровень ошибки в виде целого числа. Это соответствует соответствующей константе уровня ошибки (E_ERROR, E_WARNING и т. д.).
errstr. Задает сообщение об ошибке в виде строки.
Опционально — следующие параметры являются необязательными
errfile. Задает имя файла скрипта, в котором произошла ошибка.
errline. Задает номер строки, в которой произошла ошибка.
errcontext. Задает массив, содержащий все переменные и их значения, которые существовали на момент возникновения ошибки. Полезно для отладки.

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

<?php
// Функция обработчика ошибок
function customError($errno, $errstr){
    echo "<b>Error:</b> [$errno] $errstr";
}
?>

Вам нужно указать PHP, чтобы он использовал вашу пользовательскую функцию обработчика ошибок — просто вызовите встроенную функцию set_error_handler(), передав имя функции.

<?php
// Функция обработчика ошибок
function customError($errno, $errstr){
    echo "<b>Error:</b> [$errno] $errstr";
}
 
// Устанавливаем обработчик ошибок
set_error_handler("customError");
 
// Вызываем ошибку
echo($test);
?>

Регистрация ошибок

Журнал сообщений об ошибках в текстовом файле

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

<?php
function calcDivision($dividend, $divisor){
    if($divisor == 0){
        trigger_error("calcDivision(): The divisor cannot be zero", E_USER_WARNING);
        return false;
    } else{
        return($dividend / $divisor);
    }
}
function customError($errno, $errstr, $errfile, $errline, $errcontext){
    $message = date("Y-m-d H:i:s - ");
    $message .= "Error: [" . $errno ."], " . "$errstr in $errfile on line $errline, ";
    $message .= "Variables:" . print_r($errcontext, true) . "rn";
    
    error_log($message, 3, "logs/app_errors.log");
    die("There was a problem, please try again.");
}
set_error_handler("customError");
echo calcDivision(10, 0);
echo "This will never be printed.";
?>

Отправка сообщений об ошибках по электронной почте

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

<?php
function calcDivision($dividend, $divisor){
    if ($divisor == 0){
        trigger_error("calcDivision(): The divisor cannot be zero", E_USER_WARNING);
        return false;
    } else{
        return($dividend / $divisor);
    }
}
function customError($errno, $errstr, $errfile, $errline, $errcontext){
    $message = date("Y-m-d H:i:s - ");
    $message .= "Error: [" . $errno ."], " . "$errstr in $errfile on line $errline, ";
    $message .= "Variables:" . print_r($errcontext, true) . "rn";
    
    error_log($message, 1, "webmaster@example.com");
    die("There was a problem, please try again. Error report submitted to webmaster.");
}
set_error_handler("customError");
echo calcDivision(10, 0);
echo "This will never be printed.";
?>

Вызов ошибок

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

Чтобы вызвать ошибку в скрипте, вызовите функцию trigger_error(), передав сообщение об ошибке, которое вы хотите сгенерировать:

trigger_error("There was a problem.");

Рассмотрим следующую функцию, которая вычисляет деление двух чисел.

<?php
function calcDivision($dividend, $divisor){
    return($dividend / $divisor);
}
 
// Вызываем функцию
echo calcDivision(10, 0); // Выводит: Warning: Division by zero in C:wampwwwprojecttest.php on line 3
?>

Это сообщение выглядит не очень информативным. Рассмотрим следующий пример, в котором для генерации ошибки используется функция trigger_error().

<?php
function calcDivision($dividend, $divisor){
    if($divisor == 0){
        trigger_error("Делитель не может быть нулевым", E_USER_WARNING);
        return false;
    } else{
        return($dividend / $divisor);
    }
}
 
// Вызываем функцию
echo calcDivision(10, 0); // Выводит: Warning: Делитель не может быть нулевым C:wampwwwprojecterror.php on line 4
?>

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

0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии

А вот еще интересные материалы:

  • Яшка сломя голову остановился исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного где ошибка
  • Php вернуть ошибку 500
  • Php вернуть 403 ошибку