Работа с подсказками кастрированного типа в Magento

15

Просто интересно, есть ли у кого-то лучшие стратегии, чем я думал, для проверки типов, чтобы сосуществовать с пользовательским обработчиком ошибок Magento. В частности, я задаюсь вопросом о "Catchable Fatal Errors", который генерируется в случае несоответствия напечатанных параметров. Вот пример из Mageкласса:

/**
 * Write exception to log
 *
 * @param Exception $e
 */
public static function logException(Exception $e)
{
    if (!self::getConfig()) {
        return;
    }
    $file = self::getStoreConfig('dev/log/exception_file');
    self::log("\n" . $e->__toString(), Zend_Log::ERR, $file);
}

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

Возможно повторно реализовать проверку типов в начале метода: $e instanceof Exceptionно такая тактика побеждает цель подсказки типа.

Любые советы подсказки?

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

Ответы:

5

Хороший вопрос +1

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

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

Записка перед взлетом

У меня никогда не было таких проблем, пока это не появилось. Разрабатывая в Magento с включенным режимом разработчика, я даже и секунды не об этом думаю. Так что каждый раз, когда я пукну , он будет отображаться и будет исправлен соответствующим образом.

Проблема с объяснением образца

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

Первый обработчик ошибок Magento для неуловимых ошибок app/code/core/Mage/Core/functions.php

/**
 * Custom error handler
 *
 * @param integer $errno
 * @param string $errstr
 * @param string $errfile
 * @param integer $errline
 */
function mageCoreErrorHandler($errno, $errstr, $errfile, $errline){
    /**
     * Some internal logic here for building the error message
     */

    $errorMessage .= ": {$errstr}  in {$errfile} on line {$errline}";
    if (Mage::getIsDeveloperMode()) {
        throw new Exception($errorMessage);
    } else {
        Mage::log($errorMessage, Zend_Log::ERR);
    }
}

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

Доказательство

мой testfile.php

require 'app/Mage.php';
Mage::app('admin')->setUseSessionInUrl(false);

// Test function which expect Customer_Model_Customer
function test(Customer_Model_Customer $customer)
{
    var_dump('Do not show me because ' . get_class($customer) . ' is not a customer.');
}

// Enabled developer mode
Mage::setIsDeveloperMode(true);

// Put a var in here
$noGood = Mage::app();

// Make some context
var_dump('hello');
try {
    // Call test function with a not accepted var
    test($noGood);

    // Tell if we get here
    var_dump('And we are here!');

} catch (Exception $e) {
    var_dump('You should die, because I am doing something which I should not do');
}

Результат

Режим разработчика включен. Правильный результат

string(5) "hello"
string(66) "You should die, because I am doing something which I should not do"

Режим разработчика отключен, неверный результат

string(5) "hello"
string(61) "Do not show me because Mage_Core_Model_App is not a customer."
string(16) "And we are here!"

Таким образом, он в конечном итоге пропустит ошибку и перейдет к следующей строке кода. Может быть, с еще более странными результатами. (как указывает @mpw)

Вывод

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

Конечно, при профессиональном развитии. Ошибки будут быть замечены и внимание будет оплачено. Способ предотвратить это в Magento - всегда включать режим разработчика в среде разработчика / тестирования.

ИМХО, это никогда не должно доходить до этой точки обсуждения, где проверка переменной во второй раз (по крайней мере, так я бы это описал) - это путь. Код должен быть протестирован перед выпуском в производственную среду. Это не должно быть необходимо.

Второстепенные мысли

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

Йерун
источник
> Из грубых при профессиональном развитии. Ошибки будут замечены и внимание уделено. Способ предотвратить это в Magento - всегда включать режим разработчика в среде разработчика / тестирования. ¶ Я согласен с этим. Моя цель - заставить Magento уважать языковые правила в производственном режиме. Похоже, для этого потребуется специальный модуль. Спасибо за ваше понимание!
mpw
Возможно, Magento должен выдать исключение в обоих случаях. Пользователю будет представлена ​​страница журнала ошибок Magento, а в var / exception будет соответствующий файл журнала, такой же, как и в обычных исключениях. Отличная выгода в том, что код не будет выполняться без уведомления. Вы можете скопировать файл функций в приложение / code / local и всегда выдавать исключение
Jeroen
1
Я решил пометить это как ответ. Несмотря на то, что я все еще думаю, что глушить подобные ошибки опасно, кажется маловероятным, что есть способ убедиться, что Magento уважает печатные символы, не открывая другие проблемы. Напоминание о том, что режим разработки должен быть включен, хорошо для будущих читателей, и это самый важный
вывод
2

Хороший вопрос. Я думаю, что это общая проблема E_RECOVERABLE_ERRORв PHP.

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

В PHP 7 и HHVM это уже решено.

С Magento хуже, потому что обработчик ошибок не работает с этим, начиная с класса ошибок PHP 5.2.

Более полезным видом обработки ошибок будет иметь дело с этим классом ошибок и превращать эти ошибки в ErrorException s. Пример (не мной, отсюда ):

set_error_handler(function($errno, $errstr, $errfile, $errline) {
    if ($errno === E_RECOVERABLE_ERROR) {
        throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
    }
    return false;
});

Таким образом, в свете Magento обработчиком ошибок по умолчанию является глобальная функция mageCoreErrorHandlerв app/code/core/Mage/Core/functions.php. Это получить зарегистрированный с помощью с Mage::app()помощью init()метода в Mage_Core_Model_App ( app/code/core/Mage/Core/Model/App.php) (через защищенный _initEnvironment()метод).

Тогда достаточно наблюдателя,controller_front_init_before который регистрирует ваш собственный обработчик ошибок PHP сверху (обработчики ошибок в PHP являются наращиваемыми):

$previous = set_error_handler(function($errno, $errstr, $errfile, $errline) use (&$previous) {
    if ($errno === E_RECOVERABLE_ERROR) {
        throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
    }
    if ($previous) {
        return call_user_func($previous, $errno, $errstr, $errfile, $errline);
    }
    return false;
});

ловимые фатальные ошибки затем превращаются в исключения, и вы можете обращаться с ними в своем собственном коде расширения, или они не будут обработаны и будут отображаться в журнале исключений (вместо того, чтобы ваш магазин запускал неверные типы, такие как текущее поведение, мертвые программы не ври ) В PHP 7 исключение, которое нужно искать, это не ErrorException, а TypeException (который является BaseException ) для теперь перехватываемых фатальных ошибок .

Все остальные ошибки передаются в обработчик ошибок Magento.

Примечание: я не пробовал это, это рецензия, но я знаю проблему, о которой вы спрашиваете, и анализ обработки ошибок был выполнен на 1.5.1.0 и проверен на 1.9.1.0 посредством анализа кода. Укладка обработчика ошибок должна работать. Я добавляю небольшой расширенный пример кода, который демонстрирует работу большинства частей.

Я еще не упаковал это как расширение magento, но оно должно быть прямым с modman. Я положу это на github тогда.

Приложение: Демонстрация ошибок обработчика

В следующем примере кода ( онлайн-демонстрация ) демонстрируется укладка обработчиков ошибок и создание исключений при обнаруживаемой фатальной ошибке :

<?php
/**
 * error handler demonstration
 *
 * stackable error handle with previous call and catchable error exceptions
 *
 * @author hakre <http://hakre.wordpress.com>
 * @link /magento//a/64972/4115
 */

set_error_handler(function() {
    $args = func_get_args();
    var_dump("me is the previous error handler", $args);
});

$previous = set_error_handler(function($errno, $errstr, $errfile, $errline) use (&$previous) {
    if ($errno === E_RECOVERABLE_ERROR) {
        throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
    }
    if ($previous) {
        return call_user_func($previous, $errno, $errstr, $errfile, $errline);
    }
    return false;
});

$test = function(callable $test) {};

$a = $undefined; // provoke little warning

$test(new stdClass); // provoke catchable fatal error

Выход программы

string(32) "me is the previous error handler"
array(4) {
  [0]=>
  int(8)
  [1]=>
  string(29) "Undefined variable: undefined"
  [2]=>
  string(45) "/tmp/execpad-0eca072b619d/source-0eca072b619d"
  [3]=>
  int(28)
}

Fatal error: Uncaught exception 'ErrorException' with message 'Argument 1 passed to {closure}() must be callable, object given, called in /tmp/execpad-0eca072b619d/source-0eca072b619d on line 30 and defined' in /tmp/execpad-0eca072b619d/source-0eca072b619d:26
Stack trace:
#0 /tmp/execpad-0eca072b619d/source-0eca072b619d(26): {closure}(4096, 'Argument 1 pass...', '/tmp/execpad-0e...', 26, Array)
#1 /tmp/execpad-0eca072b619d/source-0eca072b619d(30): {closure}(Object(stdClass))
#2 {main}
  thrown in /tmp/execpad-0eca072b619d/source-0eca072b619d on line 26
hakre
источник
Отличная рецензия. Во время тестирования было ли ощутимое снижение производительности от переустановки обработчика ошибок?
mpw
Я не так далеко. В ядре также есть связанная область, где в режиме разработки все предупреждения / ошибки преобразуются в исключение (а не в ErrorExceptuion - даже не регистрируются). Возможно, для этого требуется набор патчей, чтобы исправить это в здравом смысле. Для обработчика ошибок нет хорошего метода отправки, также здесь я как-то склонен исправлять ядро, даже чтобы добавить фиксированный обработчик ошибок по умолчанию.
hakre
1

Он уже обработан по умолчанию PHP, добавив (Exception $e)в определение параметра функции.

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

Йерун
источник
Посмотрите на mageCoreErrorHandlerфункцию. Ошибка, вызванная неправильными параметрами, будет обработана и подавлена ​​в режиме не для разработчиков, а также Exceptionв режиме разработчика.
mpw
Что-то серьезно не так, когда такое происходит в первую очередь. Magento должен mageCoreErrorHandlerбыть уверен, что посетители не получат сообщение об ошибке. Вы можете создать свое собственное, try{}catch(){}чтобы захватить их сами, и если вы не можете передать их.
Йерун
Принимая во внимание, что в случае подавляемой фатальной ошибки, не допускающей исключений, исключение не выдается, что мне даст попытка / отлов?
mpw
1
Я наконец получаю это после локального теста ... Вы очень правы, ошибка подавлена, и код продолжится. Я обновлю свой ответ и добавлю некоторые дополнительные мысли
Jeroen
Я выложу новый ответ, иначе наш разговор вообще не имеет смысла
Jeroen