Magento CLI 2.4.0-ErrorHandler가 프로덕션 모드에서 PHP 오류를 예외로 전환 함

Sep 01 2020

서문 : 저는 개발자 모드에서 개발하고 Magento가 모든 PHP E_NOTICE|E_WARNING|etc.오류를 포착하고 예외를 발생시키고 코드를 커밋하기 전에 이러한 문제를 정리 하는 것을 선호 합니다. 그러나 문제는 타사 확장 프로그램에 있으며 문제를보고하려고합니다.

문제:

제목에서 알 수 있듯이 Magento는 Magento\Framework\App\ErrorHandler::handler프로덕션 모드 에서 PHP 수준 오류를 예외로 변환하고 있습니다. 현재 개발 노력의 일환으로 타사 확장 프로그램에 의존해야하며 다음 E_NOTICE수준 오류를 일으키는 논리적 버그가 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있습니다..

이 특정 코드 영역에 익숙한 사람은 버그를 정리해야한다고 말해야한다고 느낄 수 있습니다. 그렇지 않으면 행 type_id및 .NET에 대한 데이터 부족으로 인해 제품 가져 오기가 작동하지 않습니다 _attribute_set. 제 3 자 확장에서 더 나아가서 제 3 자 모듈은 실제로 db로 가져 오기 전에 이러한 중요한 키를 적절하게 설정합니다.

내 문제로 돌아 가기 : 타사 확장의이 문제로 인해 개발자 모드에서 로컬로 가져 오기 실행을 테스트 할 수 없습니다. 예상대로 Magento가이 PHP E_NOTICE 수준 오류를 예외로 변환하기 때문입니다. 가져 오기가 실패합니다. 따라서 로컬에서 이러한 PHP 오류가 예외로 변환되는 것을 방지하기 위해 Magento를 프로덕션 모드로 전환하기로 결정했습니다. 불행히도 이것은 문제를 해결하지 못한 것 ErrorHandler같으며 Magento가 프로덕션 모드에있는 동안이 문제에 대한 예외를 계속 발생시킵니다.

나는 Magento의 코드를 자세히 살펴보기로 결정했고, 처음에 Magento가 PHP 오류에 대한 예외를 던지는 것을 피해야하는 방법 (프로덕션 모드)을 결정할 수없는 것 같습니다. 코드를 밟으면 다음을 볼 수 있습니다.

  1. bin/magento CLI에서 가져 오기를 실행하므로 실행의 진입 점이됩니다.
  2. bin/magento 포함을 수행합니다 app/bootstrap.php
  3. bootstrap.php 즉시 PHP의 오류보고 수준을 E_ALL
  4. bin/magento PHP의 오류 처리기를 설정합니다. \Magento\Framework\App\ErrorHandler
  5. bin/magento Symfony 콘솔 응용 프로그램을 만들고 실행합니다.

현재보고 수준에 관계없이 app/bootstrap.php오류보고 수준을로 설정 하기 때문에 E_ALLMagento 실행 스택의 어딘가에서 현재 애플리케이션 모드를 참조해야한다고 가정 할 수 있습니다. "프로덕션"으로 실행하는 경우 error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING ...)프로덕션 모드에서 이러한 오류에 대한 예외가 발생하지 않도록하는 작업을 수행합니다. 그러나 나는 어디에서도 그러한 논리를 찾을 수 없었다. 따라서 프로덕션 모드가 일반적으로 ErrorHandler이러한 PHP 오류를 처음에 예외로 변환하는 것을 방지하는 방법에 대해 손실 이 있습니다.

내가 직접 수정 ErrorHandler.php하면 false를 반환하는 조건부 바로 앞에 다음을 추가합니다.

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

콘솔에 다음 출력이 표시됩니다.

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

따라서는 return false;충족되지 않으며 코드는 E_NOTICE레벨 오류를 예외로 변환합니다 . 이 줄 바로 앞에 $errorNo = $errorNo & error_reporting();다음을 추가합니다. error_reporting(E_ALL & ~E_NOTICE);오류 처리기가 예상대로 false를 반환합니다. 분명히 이것은 해결책이 아니지만 현재 작업을 계속 진행하기 위해 일시적으로 의지 한 것입니다.

참고할 몇 가지 추가 사항 :

  • 나는 PHP 바이너리에 전달하려고 시도했지만 -d display_errors=0아무런 효과가 없습니다 (PHP는 여전히 오류를보고하므로 error_log)
  • PHP 바이너리로 전달하려고했지만 -d log_errors=0효과가 없습니다.
  • 나는 PHP 바이너리로 전달하려고 시도했는데 -d error_reporting=0, Magento bootstrap.php가 E_ALL.
  • 위의 모든 것을 한 번에 개별적으로 시도했습니다.
  • 나는 플러시 캐시뿐만 아니라 장애인 캐시를 가지고뿐만 아니라 날아 var/cache와generated/code
  • 내 전체 응용 프로그램 (백업 날아왔다 app/etc/env.php, HEAD에서 하드 리셋을 실행하기 위해 진행 첫번째), composer install그래서 닦아로, 신선한 종속성을 가져가 어떤 효과를 얻을 수 없습니다 캐시 내가 놓친를 (복원 env.php하고 다시 -컴파일 프로덕션 모드)
  • 캐시는 현재 Magento의 var/cache디렉토리 아래에 즉시 사용 가능한 파일 시스템 캐시로 구성되어 있습니다.

마지막 몇 가지 참고 :

  • 대부분의 경우 이것은 Magento 2.4.0의 거의 즉시 설치되는 것입니다. 커스터마이제이션 측면에서 거의 없었으며, 확실히 error_reporting()혼자서 (또는 다른 개발자가이 시점에서 코드를 만지는 유일한 개발자 이기 때문에) 레벨을 수정하지 않았습니다 .
  • 타사 확장은 firebear/importexport버전 3.4.3입니다. 이 확장을 검토해도 error_reporting()레벨 이 변경되고 있음이 표시되지 않습니다 .
  • PHP 버전 : 7.4.9

요약 : Magento 프로덕션 모드 ErrorHandler에서 사소한 PHP 오류에 대한 예외를 계속 throw 하는 이유를 모르겠습니다 .

2020.10.07 수정

FactoryAidan의 답변에 대한 응답 : (원래 질문은 다루지 않지만 처음에 E_NOTICE 수준 오류가 발생하는 이유를 설명합니다.)

E_NOTICE 레벨 오류의 발생은 주로 핵심 Magento 코드의 논리적 버그가 아닌 Firebear_ImportExport의 로직 버그로 인한 것이라고 제 개인적인 의견입니다. 또한 "Magento 코어 코드 null가에서 반환 되었는지 확인 했으므로 $this->skuProcessor->getNewSku($lastSku)Firebear의 확장을 사용할 때 처음에는 문제가되지 않을 것입니다.

이제 내가 여전히 Firebear를 가리키는 이유에 대해 (마 젠토에 대한 비판도 포함되지만 여기서 주요 문제는 Firebear가 Magento가 한 일을 따르지 않는 것입니다).

Magento 2.4.0에서 \Magento\CatalogImportExport\Model\Import\Product2481-2488 행 을 보면 다음을 찾을 수 있습니다.

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

이 논리를 말로 표현하면 코드가 다음을 수행하고 있음을 알 수 있습니다 (중요하지 않은 측면 생략).

  • SKU가 이미있는 경우
  • 기존 SKU type_id가 유효한 경우
  • 그런 다음이 기존 sku 를 SkuProcessor의 새 skus 배열에 추가 합니다.

-- 무엇을 기다립니다? 새 skus 배열에 기존 을 추가 하시겠습니까? 왜 새롭지 않은지, 우리는 문자 그대로 이미 존재한다는 것을 확인했습니다. (이것은 Magento에 대한 나의 비판이있는 곳입니다.

방법을 따기는 우리가 본질적으로 그 결론에 도달, 여기에 호출 : "마 젠토는 기존의 SKU를 업데이트 할 때, 우리는 수정할 수 없습니다 보장되는 type_id과를 attribute_set_id". Magento는 기존의 값 과 값 \Magento\CatalogImportExport\Model\Import\Product::_prepareNewSkuData()을 가진 배열을 구축하는 데 의지하여이 배열을 실제로 기존 데이터 임에도 불구하고 "새로운 sku"로 SkuProcessor에 전달 함으로써 다소 이상한 방식으로 진행됩니다.type_idattr_set_code

그런 다음 이러한 헛소리를 통해 Magento는 기존 skus를 처리 할 때 항상 메서드 내에서 type_idand를 구체적으로 설정하도록 속임수를 사용했습니다 .attr_set_code\Magento\CatalogImportExport\Model\Import\Product::_prepareRowForDb()

파이어 베어는? 기존 제품에서 \ 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`를 재정의했습니다. 제 생각에는 괜찮습니다. 그러나 제품이 초기 유효성 검사를 통과 한 후에는 "기존 sku 데이터를 새 skus 배열에 추가"하는 작업을 무시하고 이것이 E_NOTICE 수준 오류가 발생하는 이유입니다.

어쩌면 그들은 의도적으로 그렇게를하는 것이,이 작업을 수행하지 않았다 type_id및 attr_set_code네이티브로 "다시"아니다 _prepareRowForDb()방법. 그러나이 경우에도 부모 _prepareRowForDb메서드 를 호출 하고 나중에 가져 오기에서 제공된 데이터로이 두 배열 키 값을 설정하는 것은 정말 간단합니다 .

궁극적으로 내 해결책은 Model\Import\Product클래스를 다시 한 번 인수하여 Firebear를 확장하여 Magento를 확장하는 것이 었습니다 . 여기서는 parent::_prepareRowForDb()나중에 오류보고 수준을 원래 값으로 다시 설정 하기 전에 오류보고 수준을 수정합니다 .

내 소스 코드 :

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

답변

2 FactoryAidan Oct 08 2020 at 05:48

🔥 Magento 2.4.0과 Firebear도 사용하고 있습니다.

⚠️ 이것은 다음과 같은 Magento Core 버그의 결과입니다.

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

어디:

  • 라인 1280 : $this->skuProcessor->getNewSku($lastSku)반환 유형을 가질 수 array|null있지만 null처리되지 않습니다.

✅ .patch기존 설치에서 버그가 발생한 코어 모듈을 업데이트 할 다음 파일을 사용하세요.

  • M2.4.0 패치에서 두 array | null 반환 유형을 처리합니다. getNewSku()

로 패치를 설치하려면 composer.json요점 링크 ☝️의 첫 번째 주석을 참조하십시오.

ℹ️ 이 솔루션은 Magento Enterprise Cloud Edition 환경에서 테스팅 을 성공적으로 통과했습니다.