Gérer les indices de type castré dans Magento

15

Je me demande simplement si quelqu'un a de meilleures stratégies que celles auxquelles j'ai pensé pour que la vérification de type coexiste avec le gestionnaire d'erreurs personnalisé de Magento. Plus précisément, je m'interroge sur les "erreurs fatales capturables", comme dans le cas d'une non-concordance de paramètres suggérée par type. Voici un exemple de la Mageclasse:

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

En raison du gestionnaire d'erreurs, tout peut être transmis à la méthode, y compris un Zend_Date(qui fonctionnera bien, mais semblera très déroutant dans votre journal d'exceptions), ou un Mage_Core_Model_App, qui aura en fait une erreur fatale.

Il est possible de $e instanceof Exceptionréimplémenter la vérification de type en haut d'une méthode: mais de telles tactiques vont à l'encontre du but d'une saisie de type.

Des suggestions d' indices ?

mpw
la source

Réponses:

5

Bonne question +1

A fait quelques recherches et tests après un bon point dans la direction après ma discussion avec @mpw sur ma première réponse. Je l'ai mal compris en partie la première fois.

Ajoutera du code pour clarifier afin que d'autres comprennent mieux le problème.

Une note avant le décollage

Je n'ai jamais eu de tels problèmes jusqu'à ce que cela arrive. Développer dans Magento avec le mode développeur activé, je n'y pense même pas une seconde. Ainsi, chaque fois que je péterai , il apparaîtra et sera corrigé en conséquence.

Le problème avec un échantillon explicatif

Vos erreurs fatales dites seront enregistrées (si activées) et le code continuera comme d'habitude car aucune erreur n'est levée par mageCoreErrorHandlerou le programme le fera exit.

Premier gestionnaire d'erreurs de base de Magento pour les erreurs non capturables 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);
    }
}

Comme vous pouvez le voir, en mode développeur, il vous dira quelque chose d'utile, il lance une erreur. Lorsqu'il est éteint, il se connecte (s'il est activé) et continue.

La preuve

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

Le résultat

Mode de développement activé. Résultat correct

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

Mode de développement désactivé, résultat incorrect

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

Ainsi, il finira par ignorer l'erreur et continuera sur la ligne de code suivante. Peut-être avec des résultats encore plus étranges. (comme le souligne @mpw)

Conclusion

Il pourrait arriver que quelqu'un développe d'une manière que les erreurs vont inaperçu et il sera éventuellement donner des résultats inattendus.

Bien sûr lors du développement d'une manière professionnelle. Les erreurs seront remarqués et l' attention est payée. Le moyen d'empêcher cela dans Magento est toujours d'activer le mode de développement dans un environnement développeur / test.

À mon humble avis, il ne devrait jamais arriver à ce point de discussion, où vérifier une variable une deuxième fois (du moins c'est ainsi que je le décrirais) est la voie à suivre. Le code doit être testé avant d'être publié sur les environnements de production. Cela ne devrait pas être nécessaire.

Doutes

Peut-être que Magento devrait s'arrêter après une erreur fatale. Ou générez un rapport et montrez-le au visiteur. De cette façon, les lignes de code suivantes ne seront jamais exécutées et les choses seront remarquées.

Jeroen
la source
> De grossier lors du développement de manière professionnelle. Des erreurs seront constatées et une attention particulière sera portée. Le moyen d'empêcher cela dans Magento est toujours d'activer le mode de développement dans un environnement développeur / test. ¶ Je suis d'accord avec ça. Mon objectif est que Magento respecte les règles linguistiques en mode production. On dirait qu'il faudra peut-être un module personnalisé. Merci pour votre perspicacité!
mpw
Peut-être que Magento devrait lever une exception dans les deux cas. L'utilisateur se verra présenter une page de journal d'erreurs Magento et dans var / exception aura un fichier journal correspondant, le même que les exceptions régulières. Le grand hic ici est que le code ne sera pas exécuté sans préavis. Vous pouvez copier le fichier de fonctions dans l'application / code / local et toujours lever une exception
Jeroen
1
J'ai décidé de marquer cela comme la réponse. Bien que je pense toujours qu'étouffer des erreurs comme celle-ci est dangereux, il semble peu probable qu'il existe un moyen de s'assurer que Magento respecte les typographies sans ouvrir d'autres problèmes. Le rappel de garder le mode dev activé est un bon
message
2

Bonne question. Je pense que c'est un problème général avec E_RECOVERABLE_ERRORPHP.

Ce que vous avez dans votre question est le gestionnaire d'exceptions, pas le gestionnaire d'erreurs. Le gestionnaire d'erreurs est à l'origine du problème réel dont vous discutez ici avec des erreurs fatales capturables ( E_RECOVERABLE_ERROR) .

PHP 7 et HHVM ont déjà résolu ce problème.

C'est pire avec Magento car le gestionnaire d'erreurs ne gère pas cela depuis la classe d'erreur PHP 5.2.

Un type plus utile de gestion des erreurs consisterait à traiter cette classe d’erreurs et à transformer ces erreurs en erreurs d’ exception . Exemple (pas par moi, d'ici ):

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

Donc, à la lumière de Magento, le gestionnaire d'erreurs par défaut est la fonction globale mageCoreErrorHandlerdans app/code/core/Mage/Core/functions.php. Il est enregistré via Mage::app()la init()méthode de Mage_Core_Model_App ( app/code/core/Mage/Core/Model/App.php) (via la _initEnvironment()méthode protégée ).

Un observateur surcontroller_front_init_before lequel enregistre votre propre gestionnaire d'erreurs PHP devrait suffire (les gestionnaires d'erreurs en PHP sont empilables):

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

les erreurs fatales capturables sont ensuite transformées en exceptions et vous pouvez les traiter dans votre propre code d'extension ou elles ne sont pas détectées et seront vues dans le journal des exceptions (au lieu d'avoir votre boutique exécutez gaga sur des types incorrects comme le comportement actuel, les programmes morts) ne mentez pas ). En PHP 7, l'exception à rechercher n'est pas alors ErrorException mais TypeException (qui est une BaseException ) pour les erreurs fatales désormais capturables .

Toutes les autres erreurs sont transmises au gestionnaire d'erreurs de Magento.

Remarque: je n'ai pas essayé cela, c'est une description mais je connais le problème que vous posez et l'analyse de la gestion des erreurs a été effectuée par rapport à 1.5.1.0 et vérifiée par rapport à 1.9.1.0 via l'analyse de code. L'empilement du gestionnaire d'erreurs devrait fonctionner. J'ajoute un petit exemple de code étendu qui montre que la plupart des pièces fonctionnent.

Je n'ai pas encore emballé cela comme une extension magento mais cela devrait être simple avec modman. Je vais le mettre sur github alors.

Annexe: Démo du gestionnaire d'erreurs

L'exemple de code suivant ( démonstration en ligne ) illustre l'empilement de gestionnaires d'erreurs et le lancement d'exceptions en cas d' erreur fatale capturable :

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

Sortie du programme

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
la source
Excellente rédaction. Au cours de vos tests, y a-t-il eu une dégradation mesurable des performances suite à la réinitialisation du gestionnaire d'erreurs?
mpw
Je ne l'ai pas encore fait. Il existe également une zone connexe dans le noyau où, en mode développement, tous les avertissements / erreurs sont convertis en exception (et non ErrorExceptuion - même pas enregistrés). Cela nécessite peut-être un jeu de correctifs pour résoudre ce problème d'une manière saine. Pour le gestionnaire d'erreurs, il n'y a pas de bonne méthode d'envoi disponible, ici aussi j'ai tendance à patcher le noyau même pour apporter un gestionnaire d'erreurs par défaut fixe.
hakre
1

Il est déjà géré par défaut par PHP en ajoutant (Exception $e)la définition des paramètres de la fonction.

Vous ne pouvez pas transmettre autre chose à cette fonction qu'une exception ou une extension d'exception.

Jeroen
la source
Jetez un oeil à la mageCoreErrorHandlerfonction. Une erreur déclenchée par des paramètres incorrects sera gérée et supprimée en mode non développeur, et lancera un Exceptionen mode développeur.
mpw
Quelque chose ne va vraiment pas lorsque cela se produit en premier lieu. Magento doit mageCoreErrorHandlerêtre sûr que les visiteurs ne recevront pas d'erreur au visage. Vous pouvez en construire un try{}catch(){}pour les saisir vous-même, et si vous ne pouvez pas les transmettre.
Jeroen
Étant donné qu'aucune exception n'est levée dans le cas d'une erreur fatale supprimable par catachable, qu'est-ce que le try / catch me procurerait
mpw
1
Je comprends enfin, après un test local ... Vous avez tout à fait raison, l'erreur est supprimée et le code continuera. Je mettrai à jour ma réponse et ajouterai quelques réflexions supplémentaires
Jeroen
Je posterai une nouvelle réponse sinon notre conversation n'a aucun sens
Jeroen