Magento CLI 2.4.0 - ErrorHandler transforme les erreurs PHP en exceptions en mode production

Sep 01 2020

Préface: je préfère développer en mode développeur, faire en sorte que Magento détecte toutes les E_NOTICE|E_WARNING|etc.erreurs PHP , leur lance une exception et nettoie ces problèmes avant de valider mon code. Le problème réside cependant dans une extension tierce, et j'ai l'intention de leur signaler le problème.

Problème:

Comme le titre l'indique, Magento est toujours en train de convertir les erreurs de niveau PHP en exceptions en Magento\Framework\App\ErrorHandler::handlermode production. Dans le cadre de mes efforts actuels de développement, j'ai compter sur une extension tierce partie, et ils ont un bug logique qui est à l' origine de ce qui suit E_NOTICEerreur de niveau: Notice: Trying to access array offset on value of type null in /path/to/project/vendor/magento/module-catalog-import-export/Model/Import/Product.php on line 1281.

Toute personne familière avec cette zone de code particulière peut se sentir obligée de déclarer que le bogue doit être nettoyé, sinon l'importation de produits ne fonctionnera pas en raison d'un manque de données sur les lignes type_idet _attribute_set. Sachez que ce n'est pas le cas, car plus loin dans l'extension tierce, le module tiers définira correctement ces clés importantes avant l'importation réelle dans la base de données.

Retour à mon problème actuel: En raison de ce problème dans l'extension tierce, je ne peux pas tester l'exécution des importations localement jusqu'à la fin en mode développeur, car (comme prévu) Magento convertira cette erreur de niveau PHP E_NOTICE en une exception. Provoque l'échec de l'importation. Par conséquent, localement, j'ai décidé de basculer Magento en mode production afin d'éviter que ces erreurs PHP ne soient converties en exceptions. Malheureusement, cela ne semble pas avoir résolu le problème et ErrorHandlercontinue de générer des exceptions pour ces problèmes pendant que Magento est en mode production.

J'ai décidé de regarder de plus près le code de Magento, et je n'arrive pas à déterminer moi-même comment (mode de production) Magento est censé éviter de lancer des exceptions pour les erreurs PHP en premier lieu. En parcourant le code, je peux voir que:

  1. bin/magento sera le point d'entrée de l'exécution, car j'exécute l'importation depuis CLI
  2. bin/magento effectuera une inclusion sur app/bootstrap.php
  3. bootstrap.php définit immédiatement le niveau de rapport d'erreur de PHP sur E_ALL
  4. bin/magento continue à définir le gestionnaire d'erreurs de PHP sur \Magento\Framework\App\ErrorHandler
  5. bin/magento crée une application console Symfony et s'exécute.

Parce que app/bootstrap.phpdéfinit le niveau de rapport d'erreur sur E_ALL, quel que soit le niveau de rapport actuel, je ne peux que supposer que quelque part le long de la pile d'exécution, Magento devrait consulter le mode d'application actuel. Si vous exécutez en tant que "production", effectuez quelque chose du error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING ...)genre de manière à empêcher le mode production de lancer des exceptions pour de telles erreurs. Cependant, nulle part je n'ai pu trouver une telle logique. Par conséquent, je ne sais pas comment le mode de production empêche généralement ErrorHandlerde convertir ces erreurs PHP en exceptions en premier lieu.

Si je modifie directement ErrorHandler.php, juste avant le conditionnel qui retourne false, en ajoutant ce qui suit:

echo "errNo: $errorNo\nerror_reporting: ". error_reporting() . "\nbitwise result: " . ($errorNo & error_reporting()) . "\nE_ALL: " . E_ALL . "\n";

J'obtiens la sortie suivante dans la console:

errNo: 8
error_reporting: 32767
bitwise result: 8
E_ALL: 32767

Par conséquent, le return false;n'est jamais satisfait et le code procède à la conversion de l' E_NOTICEerreur de niveau en une exception. Si directement avant cette ligne: $errorNo = $errorNo & error_reporting();j'ajoute ce qui suit: error_reporting(E_ALL & ~E_NOTICE);le gestionnaire d'erreurs renverra false comme prévu. Évidemment, ce n’est pas une solution, mais ce à quoi j’ai maintenant temporairement recours pour continuer à progresser dans ma tâche actuelle.

Quelques points supplémentaires à noter:

  • J'ai essayé de passer au binaire PHP -d display_errors=0, ce qui ne donne aucun effet (car PHP signalera toujours des erreurs pour le error_log)
  • J'ai essayé de passer au binaire PHP -d log_errors=0, ce qui ne donne aucun effet
  • J'ai essayé de passer au binaire PHP -d error_reporting=0, ce qui ne donne évidemment aucun effet en raison du bootstrap.phpchangement de Magento en E_ALL.
  • J'ai essayé tout ce qui précède individuellement, ainsi qu'en même temps.
  • J'ai vidé le cache, ainsi que le cache désactivé, ainsi que soufflé var/cacheetgenerated/code
  • J'ai époustouflé toute mon application (sauvegarde d' app/etc/env.phpabord), procédé à une réinitialisation matérielle de HEAD, exécuter composer installpour récupérer les dépendances fraîches, afin d'effacer tout ce que j'ai pu manquer qui est mis en cache, ce qui ne donne aucun effet (restauré env.phpet re -compiler le mode de production)
  • Le cache est actuellement configuré pour le cache du système de fichiers prêt à l'emploi sous le var/cacherépertoire de Magento

Dernières notes:

  • Pour la plupart, c'est encore presque une installation prête à l'emploi de Magento 2.4.0. Il y a eu peu de personnalisation, certainement pas de révision du error_reporting()niveau par moi-même (ou par d'autres développeurs, car je suis le seul développeur à toucher le code à ce stade).
  • L'extension tierce est la firebear/importexportversion 3.4.3. L'examen de cette extension ne montre pas non plus qu'elle change de error_reporting()niveau.
  • version php: 7.4.9

TL; DR: Je ne sais pas pourquoi le mode de production Magento continue à autoriser le ErrorHandlerlancement d'exceptions pour des erreurs PHP mineures.

MODIFIER 2020.10.07

Réponse à la réponse de FactoryAidan: (ne couvre pas la question d'origine, mais explique pourquoi l'erreur de niveau E_NOTICE se produit en premier lieu)

C'est mon opinion personnelle que l'apparition de l'erreur de niveau E_NOTICE est principalement à blâmer en raison d'un bogue logique dans Firebear_ImportExport, par opposition à un bogue logique dans le code principal de Magento. Permettez-moi également de préfixer ce qui suit avec une réitération que je suis d'accord avec votre déclaration "si le code principal de Magento était vérifié s'il nullétait retourné $this->skuProcessor->getNewSku($lastSku), ce ne serait pas un problème en premier lieu lors de l'utilisation de l'extension Firebear.

Maintenant, quant à mon raisonnement pour lequel je pointe toujours Firebear pour cela (il y aura également des critiques envers Magento, mais le principal problème ici est que Firebear ne suit pas ce que Magento a fait).

Dans Magento 2.4.0, si nous regardons les \Magento\CatalogImportExport\Model\Import\Productlignes 2481-2488, nous trouverons ce qui suit:

if ($this->isSkuExist($sku) && Import::BEHAVIOR_REPLACE !== $this->getBehavior()) { // can we get all necessary data from existent DB product? check for supported type of existing product if (isset($this->_productTypeModels[$this->getExistingSku($sku)['type_id']])) {
        $this->skuProcessor->addNewSku( $sku,
            $this->prepareNewSkuData($sku)
        );

En verbalisant cette logique, nous pouvons voir que le code fait ce qui suit (en omettant les aspects non importants):

  • Si le sku existe déjà
  • Si les sku existants type_idsont valides
  • Ajoutez ensuite ce sku existant au tableau de nouveaux skus du SkuProcessor .

-- Attends quoi? ajouter l' existant au nouveau tableau skus ? pourquoi ce n'est pas nouveau, nous venons littéralement de confirmer qu'il existe déjà. (c'est là que réside ma critique envers Magento.

En parcourant les méthodes invoquées ici, nous arrivons essentiellement à la conclusion que: "Magento s'assure que lors de la mise à jour des skus existants, nous ne sommes pas autorisés à modifier le type_idet le attribute_set_id". Magento y \Magento\CatalogImportExport\Model\Import\Product::_prepareNewSkuData()parvient d'une manière quelque peu étrange, en s'appuyant sur la construction d'un tableau qui a type_idet des attr_set_codevaleurs de l'existant, en passant ce tableau au SkuProcessor comme un "nouveau sku" bien qu'il s'agisse en fait de données d'un existant.

Ensuite, à travers ces manigances, Magento s'est maintenant trompé pour toujours définir spécifiquement le type_idet attr_set_codedans la \Magento\CatalogImportExport\Model\Import\Product::_prepareRowForDb()méthode lors de la gestion des skus existants.

Quant à Firebear? Ils ont remplacé \ Magento \ CatalogImportExport \ Model \ Import \ Product , and these lines 2481-2488 of the original model don't ever execute. Firebear (intentionally, per their documentation on their website) allows for changing of both type_id andattr_set_code` sur les produits existants. Ce qui, à mon avis, est bien. Cependant, après qu'un produit a passé la validation initiale, ils négligent de faire de même "ajouter les données sku existantes, au tableau des nouveaux skus", et c'est pourquoi l'erreur de niveau E_NOTICE se produit maintenant.

Peut-être qu'ils n'ont pas fait cela intentionnellement, de sorte que les type_idet attr_set_codene soient pas "réinitialisés" par la _prepareRowForDb()méthode native . Cependant, même si tel est le cas, ce serait vraiment simple pour eux d'appeler simplement la _prepareRowForDbméthode parente , puis de définir ces deux valeurs de clé de tableau avec les données fournies par l'importation.

En fin de compte, ma solution a été de reprendre la Model\Import\Productclasse une fois de plus, en étendant Firebear's, qui étend Magento. Ici, je révise le niveau de rapport d'erreur avant d'appeler parent::_prepareRowForDb()le jeu de niveau de rapport d'erreur à sa valeur d'origine par la suite.

Mon code source:

    /***
     * E_NOTICE level error is breaking import on updates of existing products
     * because \Magento\Framework\App\ErrorHandler is catching the error
     * and converting it into an exception.
     *
     * This error is occurring due to a logical bug in firebear/importexport
     *
     * Native magento/module-catalog-import-export will invoke
     * \Magento\CatalogImportExport\Model\Import\Product\SkuProcessor::addNewSku()
     * for rows that have passed validation, when the import behavior update.
     *
     * firebear/importexport does not, and so the SkuProcessor returns
     * null to \Magento\CatalogImportExport\Model\Import\Product::_prepareRowForDb()
     * when updating existing SKUs. Immediately afterwards, this error occurs:
     *
     * Notice: Trying to access array offset on value of type null in
     * vendor/magento/module-catalog-import-export/Model/Import/Product.php on line 1281
     *
     * Combinations of package versions that first discovered to yield this logic error:
     *      Magento Open Source 2.4.0
     *          firebear/importexport: 3.4.3
     *          magento/module-catalog-import-export: 101.1.0
     *
     * {@inheritDoc}
     * @see \Firebear\ImportExport\Model\Import\Product::_prepareRowForDb()
     * phpcs:disable PSR2.Methods.MethodDeclaration.Underscore
     */
    protected function _prepareRowForDb(array $rowData): array { $level = error_reporting();
        if (E_NOTICE & $level === 0) { return parent::_prepareRowForDb($rowData);
        }

        error_reporting($level & ~E_NOTICE); $rowData = parent::_prepareRowForDb($rowData); error_reporting($level);

        return $rowData;
    }

Réponses

2 FactoryAidan Oct 08 2020 at 05:48

🔥 J'utilise également Magento 2.4.0 et Firebear.

⚠️ Ceci est le résultat d'un bug de Magento Core dans:

  • vendor/magento/module-catalog-import-export/Model/Import/Product.php::_prepareRowForDb

où:

  • Ligne 1280: $this->skuProcessor->getNewSku($lastSku)peut avoir un type de retour array|nullmais nulln'est pas gérée.

✅ Utilisez le .patchfichier suivant sur votre installation existante qui mettra à jour le module principal bogué:

  • M2.4.0 Patch pour gérer les deux types de retour tableau | null de getNewSku()

Pour installer le patch avec composer.json, voir le premier commentaire dans l'essentiel lié ☝️.

ℹ️ Cette solution a réussi ses tests dans les environnements Magento Enterprise Cloud Edition.