SAPI streams и ошибки

Мы знаем, что PHP имеет собственные обертки доступа к стандартным I/O потокам (streams). Так же мы знаем, что в PHP-мире есть множество SAPI, но сегодня мы поговорим о 2-х часто используемых: CLI и FPM

FPM SAPI

Тут мы имеем 3 потока:

  • php://input — для чтения raw body из POST запроса
  • php://output — аналог функции print и echo.
  • php://stderr — для записи в поток ошибок

нюанс: за 1 интерпритацию из php://input  можно прочитать только 1 раз, при чтении второй раз - результатом будет пустая строка.

CLI SAPI

Тут мы имеем стандартные 3 потока:

  • php://stdin — для чтения из потока ввода (пример использования)
  • php://stdout — для записи в поток вывода
  • php://stderr — для записи в поток ошибок

Обратите внимание, что в CLI SAPI есть нюансы, а так же две глобальные переменные:

  • $argc
  • $argv

Обработчик ошибок

Мы знаем, что есть множество настроек и возникающие ошибки можно обрабатывать написав собственный обработчик ошибок, например:

function handleError($type, $message, $file = '', $line = 0, array $extra = array()) {
    $text = 'type='.$type.PHP_EOL.
    'message='.$message.PHP_EOL.
    'file='.$file.PHP_EOL.
    'line='.$line.PHP_EOL;
    error_log('handleError: '.$text);
}
set_error_handler('handleError', E_ALL);

Ошибки

Многие наивно полагают, что обработчик будет задествован при записи в поток php://stderr:

file_put_contents('php://stderr', 'my error', FILE_APPEND);

на самом же деле, обработчик ошибок никак не отреагирует на то, что Вы пишите в php://stderr т.к. это всего лишь поток.

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

  • trigger_error('my error', E_USER_ERROR);
  • error_log('my error');// если error_log задан (см. ниже), то обработчик не получит уведомление об ошибке

По-умолчанию параметр error_log не задан и когда в PHP возникает ошибка, то PHP отдает ее пользовательскому обработчику ошибок, но если пользовательский обработчик ошибок не задан, то PHP отправляет ошибку в php://stderr, а Nginx читает из стандартного потока stderr, и записывает в /var/log/nginx/site.error.log

*напомню, что мы можем тестировать PHP не используя nginx »

FPM SAPI и Nginx

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

error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
display_errors = Off

Если error_log не задан:

При возникновении ошибки PHP-FPM может действовать по-разному в зависимости от следующего параметра:

display_errors = 0 — PHP-FPM отдает Nginx-у хеадер 500 (по-умолчанию в php.ini), ошибки пишутся в php://stderr, а Nginx читает из стандартного потока stderr, и записывает в /var/log/nginx/site.error.log

display_errors = 1 — PHP-FPM отдает Nginx-у хеадер 200 а ошибки пишет в поток php://output

Обратите внимание, что PHP будет выполнять эти действия только, если параметр log_errors = On (по умолчанию On)

Если error_log задан:

Для разработчика важно видеть все ошибки и лично мне приятно смотреть их в одном файле, поэтому я в конфиге /etc/php5/fpm/pool.d/www.conf указал:

php_admin_value[error_log] = /var/tmp/error.log

Пробую:

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

$text = 'error'.time().PHP_EOL;

file_put_contents('php://stderr', $text, FILE_APPEND);// способ 1 (не записывает ошибку в лог-файл И не отправляет ее в обработчик И nginx ее не прочтет т.к. это не настоящий php://stderr)

$stream = fopen('php://stderr', 'w+');// способ 2 (не записывает ошибку И не отправляет ее в обработчик И nginx ее не прочтет т.к. это не настоящий php://stderr)
fwrite($stream, $text);

error_log('1'.$text, 4);// способ 3 (не записывает ошибку в лог-файл), но отправляет ошибку в настоящий php://stderr поэтому nginx запишет ошибку в /var/log/nginx/site.error.log
error_log('2'.$text);// отправит ошибку в системный регистратор PHP, который запишет ошибку в лог-файл
trigger_error('3'.$text, E_USER_ERROR);// записывает ошибку или отправляет ошибку в обработчик ошибок (если таковой задан)

и все бы хорошо, но все что пишется в php://stderr не пишется в настоящий php://stderr при этом в мануале написано, что можно все писать в error_log-файл благодаря:

catch_workers_output = yes

Но, к сожалению, включение параметра catch_workers_output не помогает (service php5-fpm restart делал). Протестировал на версиях:

  • PHP 5.6.11-1ubuntu3.1
  • PHP 5.6.14-0+deb8u1 (fpm-fcgi) (built: Oct 4 2015 16:13:04)

Возможно в следующих версиях PHP-FPM это будет работать.

Итог: придется не использовать параметр error_log и использовать свой хэндлер ошибок.

p.s. совет, вместо того, чтобы каждый раз открывать поток, используйте константы с открытыми потоками. Это позволит сразу выявить, есть ли в текущем SAPI возможность использовать указанный стрим (если нет, то константы не будет и вы получите PHP Fatal error), это лучше, чем дебажить и недоумевать (читай ниже). 

Источники: 1 - 2 - 3 - 4 - 5

p.s. Настройка catch_workers_output не пашет

Случайно обнаружил, что используя PHP как модуль Apache, все что пишется 'php://stdout' попадало в syslog, решил повторить ситуацию с помощью php-fpm, но ничего не вышло и даже включив параметр catch_workers_output, то что попадает в 'php://stdout', не попадало в главный лог ошибок. Тестировал так:

file_put_contents('php://stdout', 'text', FILE_APPEND);
$stream = fopen('php://stdout', 'w+');
fwrite($stream, 'text');

Оцени публикацию:
  • 2,10
Оценили человек: 2

Похожие статьи:

Справочники и учебники:


Предложения и пожелания:
Ваше имя:
Ваш E-mail:
Сколько будет Οдин + Τри
Главная
X

Новые заметки:

Про что мы забываем когда делаем оценку задачи по времени

Список вопросов для собеседования разработчика по телефону

Symfony2 авторизация без Doctrine2 для чайника

Phpstorm7 LiveEdit

Жесткий хабр или не хабр, тогда кто?

Яндекс.Деньги мошенничество

Как узнать какие страницы в поиске яндекса или это секрет

Последние комменты:

Yapro CMS:

Здравствуйте, Гость | Войти | Регистрация | Карта сайта | RSS ленты | Ошибка в тексте? Выделите её мышкой и нажмите: Ctrl + Enter

youtube.com/watch?v=7hFivbgIEqk

При полном или частичном использовании материалов данного сайта, ссылка на сайт "yapro.ru" обязательна как на источник информации.
Автоматический импорт материалов и информации с сайта запрещен.
Copyrights © 2007 - 2018 YaPro.Ru

Главная » Веб-мастеру » PHP »