Справа з натяком на нейтралізований тип у Magento


15

Цікаво, чи хтось має кращі стратегії, ніж я думав, для перевірки типу, щоб співіснувати зі спеціальним обробником помилок Magento. Зокрема, мені цікаво "Фатальні помилки, що підлягають уловлюванню", як викинуті у випадку невідповідності набраного тексту. Ось приклад з 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);
}

Через оброблювача помилок до методу може бути передано що завгодно, включаючи 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 - це завжди ввімкнути розробник в режимі розробника / тесту. ¶ Я згоден з цим. Моя мета - магенто дотримуватися мовних правил у режимі виробництва. Схоже, для цього знадобиться спеціальний модуль. Дякуємо за ваше розуміння!
mpw

Можливо, Magento повинен кинути виняток в обох випадках. Користувачеві буде представлена ​​сторінка журналу помилок Magento, а в var / виключення матиме відповідний логфайл, такий же, як і звичайні винятки. Велика уловка в тому, що код не буде виконаний без попереднього повідомлення. Ви можете скопіювати файл функцій у додаток / код / ​​локальний та завжди кинути виняток
Jeroen

1
Я вирішив позначити це як відповідь. Хоча я все ще думаю, що подібні помилки приглушення небезпечні, мабуть, мабуть, є спосіб переконатися, що Magento поважає typehints, не відкриваючи інших проблем. Нагадування про ввімкнення режиму Dev - корисне для майбутніх читачів, і це найважливіший винос
mpw

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, але це повинно бути прямо вперед з модменом. Я тоді покладу його на 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

Відмінний запис. Під час вашого тестування, чи було вимірним зниженням продуктивності від повторного встановлення обробника помилок?
mpw

Я поки що не. Також в ядрі є споріднена область, де в режимі розробки всі попередження / помилки перетворюються на виняток (а не ErrorExceptuion - навіть не реєструються). Для цього, можливо, потрібен набір патчів, щоб виправити це розумно. Для обробника помилок не існує хорошого методу відправки, також тут я якось схильний до виправлення ядра навіть для того, щоб внести фіксований обробник помилок за замовчуванням.
hakre,

1

Це вже обробляється PHP за замовчуванням, додаючи (Exception $e)у визначення параметра функції.

Ви не можете передавати цю функцію нічого іншого, крім винятку або розширення винятку.


Погляньте на mageCoreErrorHandlerфункцію. Помилка, спровокована неправильними параметрами, буде оброблена та придушена в режимі, що не розробник, і введена Exceptionв режимі розробника.
mpw

Щось серйозно не так, коли таке відбувається в першу чергу. Magento mageCoreErrorHandlerповинен бути впевнений, що відвідувачі не отримають помилки, кинуті їм в обличчя. Ви можете створити власну, try{}catch(){}щоб захопити їх самі, а якщо не можете передати їх далі.
Єроен

Зважаючи на відсутність винятків у випадку придушеної катабільної фатальної помилки, що б спроба / зловити отримала мене?
mpw

1
Я, нарешті, отримую це, після локального тесту ... Ви дуже праві, помилка придушена, і код продовжить. Я оновлю свою відповідь і додаду кілька зайвих думок
Jeroen

Я опублікую нову відповідь, інакше наша розмова взагалі не має сенсу
Jeroen
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.