Radzenie sobie z podpowiedziami typu kastrowanego w Magento

15

Zastanawiam się tylko, czy ktoś ma lepsze strategie niż myślałem, aby sprawdzanie typu współistniało z niestandardową obsługą błędów Magento. W szczególności zastanawiam się nad „możliwymi do uchwycenia błędami krytycznymi”, które są zgłaszane w przypadku niedopasowania parametru z podaną czcionką. Oto przykład z Mageklasy:

/**
 * 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);
}

Z powodu procedury obsługi błędów wszystko może zostać przekazane do metody, w tym Zend_Date(która będzie działała dobrze, ale będzie wyglądać bardzo myląco w dzienniku wyjątków) lub a Mage_Core_Model_App, która faktycznie będzie miała błąd krytyczny.

Możliwe jest ponowne zaimplementowanie sprawdzania typu u góry metody: $e instanceof Exceptionale takie taktyki pokonują cel podpowiedzi.

Wszelkie sugestie podpowiedzi ?

mpw
źródło

Odpowiedzi:

5

Dobre pytanie +1

Przeprowadziłem badania i testy po dobrym punkcie w kierunku po mojej dyskusji z @mpw na mojej pierwszej odpowiedzi. Po raz pierwszy częściowo to źle zrozumiałem.

Dodam trochę kodu, aby wyjaśnić, aby inni lepiej zrozumieli problem.

Notatka przed startem

Nigdy nie miałem takich problemów, dopóki to się nie wydarzyło. Rozwijając się w Magento z włączonym trybem programisty, nawet o tym nie myślę. Więc za każdym razem, gdy pierdnę , pojawi się i zostanie odpowiednio poprawiony.

Problem z próbką wyjaśniającą

Twoje krytyczne błędy mówienia zostaną zarejestrowane (jeśli włączone), a kod będzie kontynuowany jak zwykle, ponieważ żaden błąd nie zostanie zgłoszony mageCoreErrorHandlerani program nie exit.

Podstawowa procedura obsługi błędów Magento w przypadku błędów niewykrywalnych 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);
    }
}

Jak widać, w trybie programisty powie coś pożytecznego, zgłasza błąd. Po wyłączeniu rejestruje się (jeśli jest włączone) i kontynuuje.

Dowód

Mój 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');
}

Wynik

Tryb programowania włączony. Prawidłowy wynik

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

Tryb programowania wyłączony, niepoprawny wynik

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

Więc w końcu pominie błąd i przejdzie do następnego wiersza kodu. Może z jeszcze dziwniejszym rezultatem. (jak wskazuje @mpw)

Wniosek

To mogło się zdarzyć, że ktoś rozwija się w taki sposób, że błędy przejdzie niezauważony i będzie ostatecznie dać nieoczekiwane rezultaty.

Oczywiście przy profesjonalnym rozwoju. Błędy będą być zauważone i uwaga jest wypłacana. Sposobem na uniknięcie tego w Magento jest zawsze włączenie trybu programistycznego w środowisku programistycznym / testowym.

IMHO nigdy nie powinien przejść do tego punktu dyskusji, w którym sprawdzanie zmiennej po raz drugi (przynajmniej tak to opisałbym) jest właściwą drogą. Kod powinien zostać przetestowany przed wydaniem w środowiskach produkcyjnych. To powinno nie być potrzebne.

Namysł

Może Magento powinien zatrzymać się po fatalnym błędzie. Lub wygeneruj raport i pokaż go odwiedzającemu. W ten sposób kolejne wiersze kodu nigdy nie zostaną wykonane i wszystko zostanie zauważone.

Jeroen
źródło
> Gruboziarniste przy profesjonalnym rozwoju. Błędy zostaną zauważone i uwaga zostanie zwrócona. Sposobem na uniknięcie tego w Magento jest zawsze włączenie trybu programistycznego w środowisku programistycznym / testowym. ¶ Zgadzam się z tym. Moim celem jest, aby Magento przestrzegał reguł językowych w trybie produkcyjnym. Wygląda na to, że będzie to wymagać niestandardowego modułu. Dzięki za wgląd!
mpw
Może w obu przypadkach Magento powinien wprowadzić wyjątek. Użytkownikowi zostanie wyświetlona strona dziennika błędów Magento, aw var / wyjątku będzie pasował plik dziennika, taki sam jak zwykłe wyjątki. Wielkim haczykiem jest to, że kod nie zostanie wykonany bez powiadomienia. Możesz skopiować plik funkcji do app / code / local i zawsze zgłaszać wyjątek
Jeroen
1
Postanowiłem zaznaczyć to jako odpowiedź. Chociaż nadal uważam, że takie tłumienie błędów jest niebezpieczne, wydaje się mało prawdopodobne, aby istniał sposób, aby upewnić się, że Magento przestrzega podpowiedzi bez otwierania innych problemów. Przypomnienie o włączeniu trybu
deweloperskiego
2

Dobre pytanie. Myślę, że jest to ogólny problem E_RECOVERABLE_ERRORw PHP.

To, co masz w swoim pytaniu, to procedura obsługi wyjątków, a nie procedura obsługi błędów. Program obsługi błędów powoduje rzeczywisty problem, który tutaj omawiasz,E_RECOVERABLE_ERROR z możliwymi do wykrycia błędami krytycznymi ( ) .

PHP 7 i HHVM już to rozwiązały.

Gorzej z Magento, ponieważ program obsługi błędów nie radzi sobie z tym od klasy błędów PHP 5.2.

Bardziej użytecznym rodzajem obsługi błędów byłoby radzenie sobie z tą klasą błędów i zamiana tych błędów w ErrorException s. Przykład (nie przeze mnie, stąd ):

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

Tak więc w świetle Magento domyślną obsługą błędów jest funkcja globalna mageCoreErrorHandlerw app/code/core/Mage/Core/functions.php. Zostaje zarejestrowany za Mage::app()pomocą init()metody Mage_Core_Model_App ( app/code/core/Mage/Core/Model/App.php) (za pomocą _initEnvironment()metody chronionej ).

W takim przypadku wystarczy obserwator, nacontroller_front_init_before którym rejestruje się własny program obsługi błędów PHP (programy obsługi błędów w PHP można ustawiać jeden na drugim):

$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;
});

wykrywalne błędy krytyczne są następnie przekształcane w wyjątki i można sobie z nimi poradzić we własnym kodzie rozszerzenia lub są one niewyłapane i będą widoczne w dzienniku wyjątków (zamiast tego, że sklep uruchamia gaga na niewłaściwych typach, takich jak obecne zachowanie, martwe programy nie kłam ). W PHP 7 wyjątkiem, którego należy szukać, nie jest ErrorException, ale TypeException (który jest BaseException ) dla wykrywalnych obecnie błędów krytycznych .

Wszystkie pozostałe błędy są przekazywane do modułu obsługi błędów Magento.

Uwaga: nie próbowałem tego, jest to zapis, ale znam problem, o który pytasz, a analiza obsługi błędów została wykonana w stosunku do wersji 1.5.1.0 i zweryfikowana w stosunku do wersji 1.9.1.0 poprzez analizę kodu. Układanie procedur obsługi błędów powinno działać. Dołączam trochę rozszerzonego przykładowego kodu, który pokazuje, że większość części działa.

Nie spakowałem tego jeszcze jako rozszerzenia Magento, ale powinno być prosto z Modmanem. W takim razie włożę to na github.

Dodatek: Demonstracja modułu obsługi błędów

Poniższy przykład kodu ( demonstracja online ) demonstruje układanie programów obsługi błędów i zgłaszanie wyjątków w przypadku krytycznego błędu krytycznego :

<?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

Wyjście programu

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
źródło
Doskonały opis. Czy podczas testowania nastąpiło mierzalne pogorszenie wydajności po ponownym ustawieniu modułu obsługi błędów?
mpw
Jak dotąd Istnieje również pokrewny obszar w rdzeniu, w którym w trybie programowania wszystkie ostrzeżenia / błędy są konwertowane na wyjątki (a nie ErrorExceptuion - nawet niezalogowane). Być może wymaga to zestawu poprawek, aby to naprawić w rozsądny sposób. W przypadku procedury obsługi błędów nie ma dobrej metody wysyłki, również tutaj mam tendencję do łatania rdzenia, nawet w celu wprowadzenia stałej domyślnej procedury obsługi błędów.
hakre
1

Jest już obsługiwany domyślnie przez PHP poprzez dodanie (Exception $e)definicji parametru funkcji.

Do tej funkcji nie można przekazać niczego poza wyjątkiem lub rozszerzeniem wyjątku.

Jeroen
źródło
Spójrz na mageCoreErrorHandlerfunkcję. Błąd wywołany przez niepoprawne parametry zostanie obsłużony i zniesiony w trybie innym niż programista, a także Exceptionw trybie deweloperskim.
mpw 13.03.15
Coś jest naprawdę nie tak, kiedy tak się dzieje. Magento musi mageCoreErrorHandlermieć pewność, że odwiedzający nie otrzymają błędu w ich twarz. Możesz zbudować własny, try{}catch(){}aby sam je złapać, a jeśli nie możesz ich przekazać.
Jeroen,
Biorąc pod uwagę, że nie zgłoszono żadnych wyjątków w przypadku stłumionego, krytycznego błędu krytycznego, co przyniosłaby mi próba złapania?
MPW
1
W końcu rozumiem, po lokalnym teście ... Masz rację, błąd jest pomijany i kod będzie kontynuowany. Zaktualizuję moją odpowiedź i dodam kilka dodatkowych przemyśleń
Jeroen,
Wyślę nową odpowiedź, w przeciwnym razie nasza rozmowa w ogóle nie ma sensu
Jeroen