Определение источника времени выполнения значений PHP.INI, которые использовались для оценки предварительной обработки?

Есть ли способ определить через расширенное сообщение журнала ошибок или выходную переменную значение значений CORE PHP DIRECTIVES, используемых во время выполнения? Это будет идентично выходу phpinfo() для каждой ошибки.

Основным примером является переменная error_reporting. Если значение /etc/php.ini установлено в x, а phpinfo() заявляет, что этот файл php.ini является на самом деле предполагаемым источником ini-переменных. Но если пользователь реализует настройку времени выполнения для этой переменной в своем коде, можем ли мы вывести переменную error_reporting в момент выброса ошибки.

Например, когда файл php.ini имеет E_ALL и ~ E_STRICT, но в файлах журналов сообщается об ошибках E_STRICT, было бы замечательно иметь возможность увеличить вывод в файл журнала, который показывает "стек" всех системные переменные, активные или, по крайней мере, модифицированные, и их источник для каждой сообщаемой ошибки, возможно, через идентификатор ошибки, добавленный в журнал httpd, который соответствует необязательному файлу, который показывает эти параметры времени выполнения во время ошибки.

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

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

Я надеюсь, что я просто смотрю на это неправильно, и есть легкое исправление, которое мне не хватает.

Спасибо!

Ответ 1

Наконец, у меня есть полностью рабочий пример с "pecl runkit", поскольку мне не удалось установить apd в новый дистрибутив (это старая библиотека).

Вы можете установить runkit его с помощью PECL:

 pecl install runkit

и добавьте следующие строки в php.ini:

 extension=runkit.so
 runkit.internal_override=1

Я забыл упомянуть, что error_reporting может быть определен по-разному с помощью функции error_reporting() или ini_set. Поэтому мы должны заботиться о каждой функции. Сначала мы копируем старый ini_set с помощью runkit_function_copy, а затем переопределяем его с помощью runkit_function_redefine

Я использую debug_bactrace(1), чтобы получить вызывающий файл и номер строки.

И, наконец, чтобы уловить как нефатальную, так и фатальную ошибку, мы должны использовать set_error_handler и register_shutdown_function.

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

Ошибка (тип = 2): Деление на ноль в файле /var/www/html/test.php строка 47

Стек INI SET

  • error_reporting был определен здесь (по порядку):
    • /var/www/html/test.php, строка 16, значение: 0
    • /var/www/html/test.php, строка 17, значение: 2
    • /var/www/html/test.php, строка 18, значение: 1
    • /var/www/html/test.php, строка 19, значение: 32767

код:

<?php
runkit_function_copy("ini_set", "old_ini_set");

runkit_function_redefine("ini_set", '$key,$value', '
    global $iniset;
    $trace=debug_backtrace(1);
    $iniset[$key][]=$trace[0];
    old_ini_set($key, $value);');

runkit_function_redefine("error_reporting", '$value', '
    global $iniset;
    $trace=debug_backtrace(1);
    $iniset["error_reporting"][]=$trace[0];
    old_ini_set($key, $value);');

// let test now
ini_set("error_reporting", 0);
ini_set("error_reporting", E_WARNING);
error_reporting(E_ERROR);
ini_set("error_reporting", E_ALL);

set_error_handler("custom_error_handler");
register_shutdown_function("custom_error_handler");

function custom_error_handler($errno=NULL, $errstr=NULL) {
    global $iniset;
    if (!($error=func_get_args())) {
        $error=error_get_last();
        if ($error!=E_ERROR) $error=NULL;
    }
    if ($error) {
        echo "Error (type=$error[0]): $error[1]\n
            in file $error[2] line $error[3]<br />\n";
        echo "INI SET stack<br />\n";
        echo "<ul>";
        foreach ($iniset as $key=>$val) {
            echo "<li>$key was defined here (in order):<ul>";
            foreach ($val as $def) {
                echo "<li>{$def['file']}, line {$def['line']},
                      value: ".array_pop($def['args'])."</li>";
            }
            echo "</li></ul>";
        }

        echo "</table>";
    }
}

// division by 0
12/0;

Предыдущее сообщение:

Вы можете получить значение всех локальных и глобальных параметров конфигурации с помощью функции ini_get_all().

Чтобы получить значения как локального, так и глобального значения, вы можете установить для параметра $details значение true:

ini_get_all(NULL, true);

Вы можете получить значение для отдельных опций с помощью функций ini_get() (для времени выполнения) и get_cfg_var() (для глобальных значений php.ini).

$global_value=get_cfg_var("error_reporting");
$local_value=ini_get("error_reporting");

echo "Error reporting (Local value: $local_value, global value: $global_value)\n"

Чтобы увидеть эти результаты при возникновении ошибки, вы должны поймать ошибки.

Для нефатальных ошибок вы можете использовать set_error_handler()

Для фатальных ошибок вам необходимо определить shutdown_function(), см. Как поймать фатальную ошибку PHP

Ответ 2

Вы можете использовать error_reporting() без параметра, чтобы вернуть текущий статус сообщения об ошибках. Чтобы проверить конкретные функции, сделайте что-то вроде error_reporting() & (E_STRICT | E_COMPILE_WARNING), которое будет проверять, включены ли E_STRICT и E_COMPILE_WARNING.

Если вам действительно нужна эта деталь phpinfo(), вы всегда можете захватить вывод phpinfo и зарегистрировать его в журнале ошибок (или где-то еще), в комплекте с форматированием html. Например:

ob_start();
phpinfo();
error_log(ob_get_clean());

Обратите внимание, что это будет записывать только файл phpinfo() в журнал, а не выводить его в ответ.

Кроме того, вы можете рассмотреть возможность использования xdebug. Если вы ищете лучшую трассировку стека, это может помочь.

Ответ 3

Объем вашего вопроса немного неясен, поэтому я пишу то, что я знаю о обработке ошибок PHP, и даю задачу, которая делает это из нее верной.

Обнаружение изменения кода настроек php.ini

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

Решением для этого было бы не изменять настройки в коде и находить те места, есть инструмент: PHP Sniffer. Он имеет правило "Запретная функция", которое может быть настроено для поиска вызовов ini_set, error_reporting и других.

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

Получение текущих значений настроек во время выполнения

Вы можете зарегистрировать обратный вызов, который выполняется во время обычного выполнения кода с помощью register_tick_function. Он будет называться каждым числом "тиков", которое представляет собой количество операторов низкого уровня. Эффективно, вы можете заставить зарегистрированную функцию прыгать почти после каждого регулярного оператора script - обратите внимание, что это повлияет на время выполнения.

Сама функция может попытаться проанализировать все, что вам нравится, и отреагировать на изменения.

Получение текущих значений в случае ошибок/исключений

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

bool handler ( 
    int $errno , 
    string $errstr 
    [, string $errfile 
     [, int $errline 
      [, array $errcontext 
    ]]] 
)

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

Доступ к глобальным переменным возможен через массив $GLOBALS.

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

А теперь?

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