Tratar con insinuaciones de tipo castrado en Magento

15

Me pregunto si alguien tiene mejores estrategias de las que he pensado para que la verificación de tipos coexista con el controlador de errores personalizado de Magento. Específicamente, me pregunto acerca de los "Errores fatales capturables" que se generan en el caso de una falta de coincidencia de parámetros de tipo insinuado. Aquí hay un ejemplo de la Mageclase:

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

Debido al controlador de errores, se puede pasar cualquier cosa al método, incluido un Zend_Date(que funcionará bien, pero se ve muy confuso en su registro de excepciones), o un Mage_Core_Model_App, que en realidad tendrá un error fatal.

Es posible volver a implementar la verificación de tipos en la parte superior de un método: $e instanceof Exceptionpero tales tácticas anulan el propósito de una sugerencia de tipografía.

Cualquier las indirectas sugerencias?

mpw
fuente

Respuestas:

5

Buena pregunta +1

Hice algunas investigaciones y pruebas después de un buen punto en la dirección después de mi discusión con @mpw sobre mi primera respuesta. En parte lo entendí mal la primera vez.

Agregará un código para aclarar para que otros entiendan mejor el problema.

Una nota antes de despegar

Nunca tuve tales problemas hasta que esto surgió. Desarrollando en Magento con el modo de desarrollador habilitado, ni siquiera pienso un segundo en esto. Entonces, cada vez que me tire un pedo , aparecerá y se corregirá en consecuencia.

El problema con una muestra explicativa

Sus errores fatales se registrarán (si están habilitados) y el código continuará como de costumbre porque no se genera ningún error mageCoreErrorHandlero el programa lo hará exit.

El primer controlador de errores principales de Magento para errores que no se pueden detectar 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);
    }
}

Como puede ver, en modo desarrollador le dirá algo útil, arroja un error. Cuando está apagado, se registrará (si está habilitado) y continuará.

La prueba

Mi 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');
}

El resultado

Modo de desarrollador habilitado. Resultado correcto

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

Modo de desarrollador deshabilitado, resultado incorrecto

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

Por lo tanto, eventualmente se saltará el error y continuará en la siguiente línea de código. Quizás con resultados aún más extraños. (como señala @mpw)

Conclusión

Se podría ocurrir que alguien se está desarrollando de manera que los errores irán desapercibidos y se finalmente dar resultados inesperados.

Por supuesto cuando se desarrolla de manera profesional. Se notarán errores y se prestará atención . La forma de evitar esto en Magento es siempre habilitar el modo de desarrollador en un entorno de desarrollador / prueba.

En mi humilde opinión, nunca debería llegar a este punto de discusión, donde verificar una variable por segunda vez (al menos así es como lo describiría) es el camino a seguir. El código debe probarse antes de su lanzamiento en entornos de producción. Debería no ser necesaria.

Segundos pensamientos

Quizás Magento debería detenerse después de un error fatal. O generar un informe y mostrárselo al visitante. De esta manera, las siguientes líneas de código nunca se ejecutarán, y las cosas se notarán.

Jeroen
fuente
> De grosor cuando se desarrolla de manera profesional. Se notarán errores y se prestará atención. La forma de evitar esto en Magento es siempre habilitar el modo de desarrollador en un entorno de desarrollador / prueba. ¶ Estoy de acuerdo con eso. Mi objetivo es que Magento respete las reglas del lenguaje en modo producción. Parece que tal vez requerirá un módulo personalizado. Gracias por tu perspicacia!
mpw
Quizás Magento debería lanzar una excepción en ambos casos. Al usuario se le presentará una página de registro de errores de Magento y en var / exception tendrá un archivo de registro coincidente, lo mismo que las excepciones regulares. El gran problema aquí es que el código no se ejecutará sin previo aviso. Puede copiar el archivo de funciones a la aplicación / código / local y siempre lanzar una excepción
Jeroen
1
He decidido marcar esto como la respuesta. Aunque sigo pensando que los errores de amortiguación como ese son peligrosos, parece poco probable que haya una manera de asegurarse de que Magento respete los tipos de letra sin abrir otros problemas. El recordatorio para mantener activado el modo de desarrollo es bueno para futuros lectores, y esa es la comida para llevar más importante
mpw
2

Buena pregunta. Creo que este es un problema general E_RECOVERABLE_ERRORen PHP.

Lo que tiene en su pregunta es el controlador de excepciones, no el controlador de errores. El controlador de errores está causando el problema real que discute aquí con errores fatales detectables ( E_RECOVERABLE_ERROR) .

PHP 7 y HHVM ya tienen esto resuelto.

Es peor con Magento porque el controlador de errores no se ocupa de esto desde la clase de error PHP 5.2.

Un tipo más útil de manejo de errores sería tratar con esta clase de error y convertir estos errores en ErrorException s. Ejemplo (no por mí, desde aquí ):

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

Entonces, a la luz de Magento, el controlador de errores predeterminado es la función global mageCoreErrorHandleren app/code/core/Mage/Core/functions.php. Se registra a través Mage::app()del init()método de Mage_Core_Model_App ( app/code/core/Mage/Core/Model/App.php) (a través del _initEnvironment()método protegido ).

Entonces debería ser suficiente un observadorcontroller_front_init_before que registre su propio controlador de errores PHP en la parte superior (los controladores de errores en PHP son apilables):

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

los errores fatales detectables se convierten en excepciones y puede tratarlos en su propio código de extensión o no se detectan y se verán en el registro de excepciones (en lugar de que su tienda ejecute gaga en tipos incorrectos como el comportamiento actual es, programas muertos) no mientas ) En PHP 7, la excepción a buscar no es ErrorException , sino TypeException (que es una BaseException ) para los errores fatales ahora detectables .

Todos los demás errores se pasan al controlador de errores de Magento.

Nota: No he intentado esto, es una redacción, pero sé el problema sobre el que está preguntando y el análisis de manejo de errores se realizó en 1.5.1.0 y se verificó en 1.9.1.0 a través del análisis de código. El apilamiento del controlador de errores debería funcionar. Adjunto un pequeño código de ejemplo extendido que demuestra que la mayoría de las partes funcionan.

Todavía no he empaquetado esto como una extensión de magento, pero debería ser sencillo con modman. Lo pondré en github entonces.

Apéndice: Demo del controlador de errores

El siguiente ejemplo de código ( demostración en línea ) demuestra el apilamiento de los manejadores de errores y el lanzamiento de excepciones ante errores fatales detectables :

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

Programa de salida

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
fuente
Excelente redacción. Durante las pruebas, ¿hubo una degradación del rendimiento mensurable al restablecer el controlador de errores?
mpw
No lo he hecho hasta ahora. También hay un área relacionada en el núcleo donde, en el modo de desarrollo, todas las advertencias / errores se convierten a Excepción (y no a ErrorExceptuion , ni siquiera se registran). Esto quizás requiera un parche para arreglar esto de una manera sensata. Para el gestor de errores, no existe un método de despacho buena disposición, también aquí de alguna manera tiendo a base de parches, incluso para llevar un gestor de errores por defecto fijado en.
hakre
1

Ya se maneja por defecto en PHP al agregar (Exception $e)la definición del parámetro de función.

No puede pasar nada más a esta función que una Excepción o extensión de Excepción.

Jeroen
fuente
Echa un vistazo a la mageCoreErrorHandlerfunción. Un error desencadenado por parámetros incorrectos será manejado y suprimido en modo no desarrollador y arrojará un Exceptionmodo desarrollador.
mpw
Algo está muy mal cuando eso sucede en primer lugar. Magento tiene mageCoreErrorHandlerque asegurarse de que los visitantes no reciban un error en la cara. Puede construir uno propio try{}catch(){}para agarrarlos usted mismo, y si no puede transmitirlos.
Jeroen
Teniendo en cuenta que no se lanzan excepciones en el caso de un error fatal descartable suprimido, ¿qué me traería el try / catch?
mpw
1
Finalmente lo entiendo, después de una prueba local ... Tienes mucha razón, el error se suprime y el código continuará. Actualizaré mi respuesta y agregaré algunas ideas adicionales
Jeroen
Publicaré una nueva respuesta; de lo contrario, nuestra conversación no tiene ningún sentido
Jeroen