Мы знаем, что PHP имеет собственные обертки доступа к стандартным I/O потокам (streams). Так же мы знаем, что в PHP-мире есть множество SAPI, но сегодня мы поговорим о 2-х часто используемых: CLI и FPM
Тут мы имеем 3 потока:
нюанс: за 1 интерпретацию из php://input можно прочитать только 1 раз, при чтении второй раз - результатом будет пустая строка.
Тут мы имеем стандартные 3 потока:
Обратите внимание, что в CLI SAPI есть нюансы, а так же две глобальные переменные:
Мы знаем, что есть множество настроек и возникающие ошибки можно обрабатывать написав собственный обработчик ошибок, например:
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 т.к. это всего лишь поток.
Однако, послать ошибку в текущий обработчик можно с помощью функций:
По-умолчанию параметр error_log не задан и когда в PHP возникает ошибка, то PHP отдает ее пользовательскому обработчику ошибок, но если пользовательский обработчик ошибок не задан, то PHP отправляет ошибку в php://stderr, а Nginx читает из стандартного потока stderr, и записывает в /var/log/nginx/site.error.log
*напомню, что мы можем тестировать PHP не используя nginx »
На продакшен среде чтобы логировать ошибки и в тоже время пользователь не видел ошибки, выставляют:
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
display_errors = Off
При возникновении ошибки 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)
Для разработчика важно видеть все ошибки и лично мне приятно смотреть их в одном файле, поэтому я в конфиге /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-FPM это будет работать.
Итог: придется не использовать параметр error_log и использовать свой хэндлер ошибок.
p.s. совет, вместо того, чтобы каждый раз открывать поток, используйте константы с открытыми потоками. Это позволит сразу выявить, есть ли в текущем SAPI возможность использовать указанный стрим (если нет, то константы не будет и вы получите PHP Fatal error), это лучше, чем дебажить и недоумевать (читай ниже).
Источники: 1 - 2 - 3 - 4 - 5 (pdf)
Случайно обнаружил, что используя 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');