Magento CLI 2.4.0 - ErrorHandler está transformando erros de PHP em exceções no modo de produção
Prefácio: Eu prefiro desenvolver no modo de desenvolvedor, fazer com que o Magento detecte todos os E_NOTICE|E_WARNING|etc.erros do PHP , lance uma exceção sobre eles e limpe esses problemas antes de enviar meu código. O problema, entretanto, reside em uma extensão de terceiros e pretendo relatar o problema a eles.
Problema:
Como o título afirma, Magento ainda está convertendo erros de nível de PHP em exceções Magento\Framework\App\ErrorHandler::handlerdurante o modo de produção. Como parte dos meus esforços de desenvolvimento atuais, estou tendo que contar com uma terceira extensão partido, e eles têm um bug lógica que está a causar o seguinte E_NOTICEerro nível: 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.
Qualquer pessoa familiarizada com esta área específica do código pode se sentir compelido a declarar que o bug precisa ser corrigido ou então a importação de produtos não funcionará devido à falta de dados nas linhas type_ide _attribute_set. Saiba que este não é o caso, pois mais adiante, na extensão de terceiros, o módulo de terceiros configurará adequadamente essas chaves importantes antes da importação real para o banco de dados.
De volta ao meu problema: devido a este problema na extensão de terceiros, não consigo testar a execução de importações localmente até a conclusão enquanto estou no modo de desenvolvedor, porque (como esperado) o Magento converterá este erro de nível E_NOTICE do PHP em uma exceção. Fazendo com que a importação falhe. Portanto, localmente, decidi mudar o Magento para o modo de produção para evitar que esses erros de PHP sejam convertidos em exceções. Infelizmente, isso não parece ter resolvido o problema e ErrorHandlercontinua a lançar exceções para esses problemas enquanto o Magento está no modo de produção.
Eu decidi dar uma olhada no código do Magento, e eu mesmo não consigo determinar como (modo de produção) o Magento deve evitar lançar exceções para erros de PHP em primeiro lugar. Percorrendo o código, posso ver que:
bin/magentoserá o ponto de entrada da execução, pois estou executando a importação da CLIbin/magentoirá realizar uma inclusão emapp/bootstrap.phpbootstrap.phpimediatamente define o nível de relatório de erros do PHP paraE_ALLbin/magentoprossegue para definir o manipulador de erros do PHP para\Magento\Framework\App\ErrorHandlerbin/magentocria um aplicativo de console Symfony e executa.
Como app/bootstrap.phpdefine o nível de relatório de erro para E_ALL, independentemente do nível de relatório atual, só posso supor que em algum lugar ao longo da pilha de execução o Magento teria que consultar o modo de aplicativo atual. Se estiver executando como "produção", execute algo error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING ...)semelhante a de modo a evitar que o modo de produção gere exceções para esses erros. No entanto, em nenhum lugar consegui encontrar tal lógica. Portanto, não consigo entender como o modo de produção normalmente evita que os ErrorHandlererros do PHP em exceções sejam convertidos em primeiro lugar.
Se eu modificar diretamente ErrorHandler.php, logo antes da condicional que retorna falso, adicionando o seguinte:
echo "errNo: $errorNo\nerror_reporting: ". error_reporting() . "\nbitwise result: " . ($errorNo & error_reporting()) . "\nE_ALL: " . E_ALL . "\n";
Recebo a seguinte saída no console:
errNo: 8
error_reporting: 32767
bitwise result: 8
E_ALL: 32767
Portanto, o return false;nunca é atendido e o código prossegue para converter o E_NOTICEerro de nível em uma exceção. Se estiver diretamente antes desta linha: $errorNo = $errorNo & error_reporting();adiciono o seguinte: error_reporting(E_ALL & ~E_NOTICE);o manipulador de erros retornará falso conforme o esperado. Obviamente, esta não é uma solução, mas aquela a que agora recorri temporariamente para continuar a progredir na minha tarefa atual.
Algumas coisas adicionais a serem observadas:
- Tentei passar para o binário do PHP
-d display_errors=0, que não produz nenhum efeito (já que o PHP ainda relatará erros para oerror_log) - Eu tentei passar para o binário PHP
-d log_errors=0, que não produz nenhum efeito - Eu tentei passar para o binário PHP
-d error_reporting=0, o que obviamente não produz nenhum efeito devido àbootstrap.phpalteração do Magento paraE_ALL. - Tentei todos os itens acima individualmente, e também imediatamente.
- Limpei o cache, bem como desativei o cache, e também limpei
var/cacheegenerated/code - Eu destruí todo o meu aplicativo (fazendo backup
app/etc/env.phpprimeiro), procedi para realizar um hard reset do HEAD, executecomposer installpara buscar novas dependências, de modo a limpar qualquer coisa que eu possa ter perdido que está em cache, o que não produz efeito (restauradoenv.phpe restaurado -compilar modo de produção) - O cache está atualmente configurado para o cache do sistema de arquivos pronto para uso no
var/cachediretório do Magento
Últimas notas:
- Para a maior parte, isso ainda é quase uma instalação fora da caixa do Magento 2.4.0. Pouco foi feito em termos de customização, certamente nenhuma revisão do
error_reporting()nível por mim mesmo (ou outros desenvolvedores, já que sou o único desenvolvedor que está tocando o código neste momento). - A extensão de terceiros é a
firebear/importexportversão3.4.3. A revisão desta extensão também não mostra que ela está mudando oerror_reporting()nível. - versão php: 7.4.9
TL; DR: Eu não sei por que o modo de produção do Magento continua a permitir o ErrorHandlerlançamento de exceções para pequenos erros de PHP.
EDITAR 2020.10.07
Resposta à resposta do FactoryAidan: (não cobre a pergunta original, mas explica por que o erro de nível E_NOTICE ocorre em primeiro lugar)
É minha opinião pessoal que a ocorrência do erro de nível E_NOTICE é principalmente a culpada devido a um bug lógico no Firebear_ImportExport, ao contrário de um bug lógico no código principal do Magento. Deixe-me também prefixar o seguinte com uma reiteração de que concordo com sua declaração "se o código do núcleo do Magento nullfoi verificado se foi retornado $this->skuProcessor->getNewSku($lastSku), isso não seria um problema em primeiro lugar ao usar a extensão do Firebear.
Agora, quanto ao meu raciocínio por que eu ainda aponto o dedo para o Firebear por isso (também incluirão algumas críticas ao Magento, mas o principal problema aqui é que o Firebear não segue o que o Magento fez).
No Magento 2.4.0, se olharmos as \Magento\CatalogImportExport\Model\Import\Productlinhas 2481-2488, encontraremos o seguinte:
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)
);
Verbalizando essa lógica, podemos ver que o código está fazendo o seguinte (omitindo aspectos não importantes):
- Se o sku já existe
- Se o sku existente
type_idé válido - Em seguida, adicione este sku existente ao array de novos skus do SkuProcessor .
-- Espere o que? adicionar o existente ao novo array skus ? porque não é novo, literalmente apenas confirmamos que já existe. (é aqui que reside a minha crítica ao Magento.
Escolhendo os métodos invocados aqui, essencialmente chegamos à conclusão de que: "O Magento garante que, ao atualizar skus existentes, não temos permissão para modificar o type_ide o attribute_set_id". Magento faz isso de uma maneira um tanto estranha, apoiando-se na \Magento\CatalogImportExport\Model\Import\Product::_prepareNewSkuData()construção de um array que tem type_ide attr_set_codevalores do existente, passando esse array para o SkuProcessor como um "novo sku", apesar de ser na verdade dados de um existente.
Então, por meio dessas travessuras, o Magento agora enganou-se para sempre definir especificamente o type_ide attr_set_codedentro do \Magento\CatalogImportExport\Model\Import\Product::_prepareRowForDb()método ao lidar com skus existentes.
Quanto ao urso de fogo? Eles substituíram \ 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` em produtos existentes. O que, na minha opinião, está bom. No entanto, após um produto ter passado na validação inicial, eles se esquecem de fazer o mesmo "adicionar os dados sku existentes à matriz de novos skus", e é por isso que o erro de nível E_NOTICE agora ocorre.
Talvez eles não tenham feito isso intencionalmente, para que type_ide attr_set_codenão sejam "redefinidos" pelo _prepareRowForDb()método nativo . No entanto, mesmo se esse for o caso, seria muito simples para eles simplesmente chamar o _prepareRowForDbmétodo pai e, em seguida, definir esses dois valores de chave de array com os dados fornecidos pela importação.
No final das Model\Import\Productcontas , minha solução foi retomar a classe mais uma vez, estendendo o Firebear, que estende o Magento. Aqui, eu reviso o nível de relatório de erro antes de chamar parent::_prepareRowForDb()o definir o nível de relatório de erro de volta ao seu valor original posteriormente.
Meu código fonte:
/***
* 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;
}
Respostas
🔥 Também estou usando Magento 2.4.0 e Firebear.
⚠️ Este é o resultado de um bug do Magento Core em:
vendor/magento/module-catalog-import-export/Model/Import/Product.php::_prepareRowForDb
Onde:
- Linha 1280:
$this->skuProcessor->getNewSku($lastSku)pode ter um tipo de retorno de,array|nullmasnullnão é tratada.
✅ Use o seguinte .patcharquivo em sua instalação existente, que atualizará o módulo do núcleo com bug:
- Patch M2.4.0 para lidar com ambos os tipos de retorno de array | nulos de getNewSku()
Para instalar o patch composer.json, veja o primeiro comentário no link principal ☝️.
ℹ️ Esta solução passou com sucesso nos testes de ambientes Magento Enterprise Cloud Edition.