(Hypertext Preprocessor)

PHP 7.x не выводит ошибки в браузер

Александр » 05 май 2019, 01:55

Решил тут на днях небольшой скрипт написать на PHP. В качестве локального сервера на Windows 10 я использую OSPanel.
Конфигурация такова: Apache 2.4 с модулем php7apache2_4.dll
В php.ini вроде бы всё включено для вывода ошибок и предупреждений:
Код: Выделить всё
error_reporting = E_ALL
display_errors = On
display_startup_errors = On
log_errors = On

log_errors_max_len = 1024
ignore_repeated_errors = Off
ignore_repeated_source = Off
report_memleaks = Off
;report_zend_debug = 0
track_errors = On
;xmlrpc_errors = 0
;xmlrpc_error_number = 0
html_errors = On
;docref_root = "/phpmanual/"
;docref_ext = .html
;error_prepend_string = "<span style='color: #ff0000'>"
;error_append_string = "</span>"
error_log = "%sprogdir%/userdata/logs/%phpdriver%_error.log"
;windows.show_crt_warning

На виртуальном хосте test файла .htaccess нет.
Вывод функции phpinfo:
Не отображаются ошибки PHP 7.x, вывод phpinfo

Запускаю простейший скрипт с инструкцией включения несуществующего файла f.php.
Код: Выделить всё
<?php
include 'f.php';
?>

И вижу только пустую страницу в браузере, и error.log не создаётся.
Но стоит добавить в начало файла директиву error_reporting(E_ALL);
Код: Выделить всё
<?php
error_reporting(E_ALL);
include 'f.php';
?>

Как появляется ожидаемый Warning
Warning: include(f.php): failed to open stream: No such file or directory in S:\domains\test\clear-cache.php on line 7

Warning: include(): Failed opening 'f.php' for inclusion (include_path='.;s:/modules/php/PHP-7.1-x64;s:/modules/php/PHP-7.1-x64/PEAR/pear') in S:\domains\test\clear-cache.php on line 7

И в error.log всё это пишется.

Эта проблема наблюдается в PHP 7.0, PHP 7.1. В PHP 5.5, В PHP 5.6 с такой же конфигурацией в php.ini ошибки выводятся в браузер.

Поиски по интернету ответа толком не давали, только везде вариации указания в начале скрипта строк типа :
Код: Выделить всё
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

Мне же оказалось достаточно написать только
Код: Выделить всё
error_reporting(E_ALL);

А вот
Код: Выделить всё
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
ничего не давали.

Не понятно, как это работает.
Закрались мысли, что что-то поменялось в ядре PHP 7.x, то ли у меня в системе какой-то комплексный баг.
Что-то на этапе парсинга или интерпретации скрипта происходит.. Но что?
Позже натокнулся на сайте php.net/manual/en/language.errors.php7.php на упоминание, что в PHP 7.0 поменялась методика обработки ошибок:
PHP 7 changes how most errors are reported by PHP. Instead of reporting errors through the traditional error reporting mechanism used by PHP 5, most errors are now reported by throwing Error exceptions.

As with normal exceptions, these Error exceptions will bubble up until they reach the first matching catch block. If there are no matching blocks, then any default exception handler installed with set_exception_handler() will be called, and if there is no default exception handler, then the exception will be converted to a fatal error and will be handled like a traditional error.

Что-то в рунете про это не встретил информации.
В общем буду пробовать разобраться.
Александр
 
Сообщения: 397
Зарегистрирован: 20 мар 2014, 17:05

Re: PHP 7.x не выводит ошибки в браузер

Pasha programmist » 06 ноя 2019, 21:18

Закрались мысли, что что-то поменялось в ядре PHP 7.x, то ли у меня в системе какой-то комплексный баг.
Что-то на этапе парсинга или интерпретации скрипта происходит.. Но что?
Позже натокнулся на сайте php.net/manual/en/language.errors.php7.php на упоминание, что в PHP 7.0 поменялась методика обработки ошибок:

Ну так всё верно!
Из справки по PHP7
Код: Выделить всё
Многие фатальные и поправимые фатальные ошибки были переделаны в исключения в PHP 7. Эти исключения наследуют класс Error, который, в свою очередь, реализует интерфейс Throwable (новый базовый интерфейс, который наследуют все исключения).

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

Код: Выделить всё
В PHP 7 механизм сообщения об ошибках был сильно изменен. Традиционное оповещение об ошибке в PHP 5 было заменено новым механизмом, в котором большинство ошибок вызываются с помощью исключений класса Error.

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

Поскольку класс Error не наследуется от класса Exception, блок catch (Exception $e) { ... } для обработки неперехваченных исключений PHP 5 не может перехватить исключения Error. Для их перехвата используйте блок catch (Error $e) { ... } или установите обработчик исключений с помощью set_exception_handler().

И ещё, важно сказать

Если у вас PHP не показывает ошибки совсем — только белая страница


Это означает, что у вас скорее всего ошибка на этапе парсинга скрипта.
В таких случаях исполнение кода вообще не происходит, все директивы, что вы прописали для вывода ошибок в скрипте, тоже не будут исполнены — ведь скрипт просто не будет запущен после неудачного парсинга.
В общем, если у вас белая страница, то только php.ini или .htaccess вас спасут!
Для этого в php.ini обязательно должно быть прописано
Код: Выделить всё
display_errors = on

Если доступа к php.ini нет, то можно в
.htaccess прописать
Код: Выделить всё
php_flag display_errors on
php_flag display_startup_errors on

Кстати, в можно задать логирование ошибок в файл
Код: Выделить всё
php_value error_log logs_directory/errors_php.log

директорию 'logs_directory', конечно заранее создать нужно.

Не используйте PHP-константы в конфигурационных файлах сервера!


Они там не парсятся и трактуются все как 0 ! Для сервера эти константы: https://www.php.net/manual/en/errorfunc.constants.php ничего не значат.
Вместо них нужно использовать битовые маски (тип int).
Например, (и учитывая, что в новых версиях PHP значение константы E_ALL может измениться) рекомендуется использовать даже не число 32767 – актуальное на сегодня для php7, а максимально большое число типа int для 32-х битной платформы – 2147483647

Т. е. в httpd.conf или в .htaccess это примерно так может выглядеть:
Код: Выделить всё
    php_flag log_errors on
    php_flag display_errors on
    php_value error_reporting 2147483647
    php_value error_log /var/www/domains/example.com/php.error.log
Pasha programmist
 
Сообщения: 15
Зарегистрирован: 30 апр 2015, 22:52

Re: PHP 7.x не выводит ошибки в браузер — белая страница

Александр » 09 ноя 2019, 13:45

Pasha programmist, спасибо! Настолько ответ по тематике «управления ошибками в PHP» развёрнут вами, что считаю нужным перевод на английский сделать. Попозже займусь этим: создам тему «PHP errors & exceptions handling»
Последний раз редактировалось Александр 09 ноя 2019, 14:34, всего редактировалось 1 раз.
Александр
 
Сообщения: 397
Зарегистрирован: 20 мар 2014, 17:05

Re: PHP 7.x не выводит ошибки в браузер — белая страница

Александр » 09 ноя 2019, 14:29

Добавлю от себя ещё.
В PHP 7 ошибки и исключения реализуют интерфейс Throwable
Код: Выделить всё
Throwable {
/* Methods */
abstract public getMessage ( void ) : string
abstract public getCode ( void ) : int
abstract public getFile ( void ) : string
abstract public getLine ( void ) : int
abstract public getTrace ( void ) : array
abstract public getTraceAsString ( void ) : string
abstract public getPrevious ( void ) : Throwable
abstract public __toString ( void ) : string
}

Код: Выделить всё
Error implements Throwable {
/* Properties */
protected string $message ;
protected int $code ;
protected string $file ;
protected int $line ;
/* Methods */
public __construct ([ string $message = "" [, int $code = 0 [, Throwable $previous = NULL ]]] )
final public getMessage ( void ) : string
final public getPrevious ( void ) : Throwable
final public getCode ( void ) : mixed
final public getFile ( void ) : string
final public getLine ( void ) : int
final public getTrace ( void ) : array
final public getTraceAsString ( void ) : string
public __toString ( void ) : string
final private __clone ( void ) : void
}

То есть их можно соответственно ловить в блоках «try-catch».
Но в PHP 5 (напомню на всякий случай, что PHP 6 в природе не существует) нет класса Error и интерфейса Throwable. Там только класс Exception

Для PHP 5/7 портабельности перехват ошибок и исключений можно осуществить на основе следующего сниппета::
Код: Выделить всё
try
{
   // Код, генерирующий ошибку или исключение
}
catch (Throwable $t)
{
   // Сработает в PHP 7, а в PHP 5 будет проигнорирован
}
catch (Exception $e)
{
   // Сработает в PHP 5, в PHP 7 выполнен не будет т. к. сработает уже блок выше
}
Александр
 
Сообщения: 397
Зарегистрирован: 20 мар 2014, 17:05


Вернуться в PHP