From 62093895aacf5b65b7576c8967acb8ad2a9ed767 Mon Sep 17 00:00:00 2001 From: Bashev Date: Thu, 20 Aug 2026 09:14:25 +0300 Subject: [PATCH 1/2] Fix PHP 8.5 "null as array offset" deprecation in AbstractPdf::_getRenderer() On PHP 8.5, using null as an array offset is deprecated. When a PDF renderer type resolves to null (e.g. a grouped-product order item whose real product type is null), _getRenderer() calls isset($this->_renderers[$type]) with $type === null, emitting a deprecation at AbstractPdf.php. In production mode, where deprecations are promoted to exceptions, PDF generation aborts. Guard the array offset against null before the isset() check so a null type falls back to the 'default' renderer, consistent with the other PHP 8.5 null-array-offset fixes (e.g. #40889). Covered by a unit test that promotes deprecations to exceptions and asserts the null type resolves to the default renderer (fails on the unpatched code). --- .../Sales/Model/Order/Pdf/AbstractPdf.php | 2 +- .../Unit/Model/Order/Pdf/AbstractTest.php | 76 +++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php b/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php index 2a67810a2889b..255804270d406 100644 --- a/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php +++ b/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php @@ -856,7 +856,7 @@ protected function _initRenderer($type) */ protected function _getRenderer($type) { - if (!isset($this->_renderers[$type])) { + if ($type === null || !isset($this->_renderers[$type])) { $type = 'default'; } diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Pdf/AbstractTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Pdf/AbstractTest.php index 3ae5088da49fe..aa714ea0a5b2c 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Pdf/AbstractTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Pdf/AbstractTest.php @@ -20,6 +20,7 @@ use Magento\Sales\Model\Order\Invoice; use Magento\Sales\Model\Order\Pdf\AbstractPdf; use Magento\Sales\Model\Order\Pdf\Config; +use Magento\Sales\Model\Order\Pdf\Items\AbstractItems; use Magento\Sales\Model\Order\Pdf\ItemsFactory; use Magento\Sales\Model\Order\Pdf\Total\DefaultTotal; use Magento\Sales\Model\Order\Pdf\Total\Factory; @@ -288,6 +289,81 @@ public function testDrawLineBlocksPropagatesNewPageToSiblingColumns(): void $this->assertSame(['name-line-2', 'sku-line'], $drawnOnPageTwo); } + /** + * A null renderer type must fall back to the 'default' renderer without emitting a + * "Using null as an array offset is deprecated" notice (PHP 8.5), which in production + * mode is promoted to an exception and breaks PDF generation. + * + * @return void + * @throws \ReflectionException + */ + public function testGetRendererFallsBackToDefaultForNullType(): void + { + $paymentData = $this->createMock(Data::class); + $string = $this->createMock(StringUtils::class); + $scopeConfig = $this->createMock(ScopeConfigInterface::class); + $filesystem = $this->createMock(Filesystem::class); + $pdfConfig = $this->createMock(Config::class); + $pdfTotalFactory = $this->createMock(Factory::class); + $pdfItemsFactory = $this->createMock(ItemsFactory::class); + $localeMock = $this->createMock(TimezoneInterface::class); + $translate = $this->createMock(StateInterface::class); + $addressRenderer = $this->createMock(Renderer::class); + $taxHelper = $this->createMock(TaxHelper::class); + $fileStorageDatabase = $this->createMock(Database::class); + $rtlTextHandler = $this->createMock(RtlTextHandler::class); + $image = $this->createMock(Image::class); + + $model = $this->getMockBuilder(AbstractPdf::class) + ->setConstructorArgs([ + $paymentData, + $string, + $scopeConfig, + $filesystem, + $pdfConfig, + $pdfTotalFactory, + $pdfItemsFactory, + $localeMock, + $translate, + $addressRenderer, + [], + $fileStorageDatabase, + $rtlTextHandler, + $image, + $taxHelper + ]) + ->onlyMethods(['getPdf']) + ->getMock(); + + $defaultRenderer = $this->createMock(AbstractItems::class); + $pdfItemsFactory->expects($this->once()) + ->method('get') + ->with('default_renderer_model') + ->willReturn($defaultRenderer); + + $renderersProperty = new \ReflectionProperty(AbstractPdf::class, '_renderers'); + $renderersProperty->setValue( + $model, + ['default' => ['model' => 'default_renderer_model', 'renderer' => null]] + ); + + // Promote deprecations to exceptions so a null array offset regression fails the test. + set_error_handler( + static function (int $errno, string $errstr): bool { + throw new \RuntimeException($errstr); + }, + E_DEPRECATED + ); + try { + $reflectionMethod = new \ReflectionMethod(AbstractPdf::class, '_getRenderer'); + $actual = $reflectionMethod->invoke($model, null); + } finally { + restore_error_handler(); + } + + $this->assertSame($defaultRenderer, $actual); + } + /** * Generate the array for multiline block * From 3555a1e1b2ea2bb84ad022c2a4160e0aad15a982 Mon Sep 17 00:00:00 2001 From: Bashev Date: Thu, 20 Aug 2026 18:55:06 +0300 Subject: [PATCH 2/2] Remove deprecation-handling logic from AbstractTest::_getRenderer unit test --- .../Test/Unit/Model/Order/Pdf/AbstractTest.php | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Pdf/AbstractTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Pdf/AbstractTest.php index aa714ea0a5b2c..8d85dd96bb956 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Pdf/AbstractTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Pdf/AbstractTest.php @@ -347,19 +347,8 @@ public function testGetRendererFallsBackToDefaultForNullType(): void ['default' => ['model' => 'default_renderer_model', 'renderer' => null]] ); - // Promote deprecations to exceptions so a null array offset regression fails the test. - set_error_handler( - static function (int $errno, string $errstr): bool { - throw new \RuntimeException($errstr); - }, - E_DEPRECATED - ); - try { - $reflectionMethod = new \ReflectionMethod(AbstractPdf::class, '_getRenderer'); - $actual = $reflectionMethod->invoke($model, null); - } finally { - restore_error_handler(); - } + $reflectionMethod = new \ReflectionMethod(AbstractPdf::class, '_getRenderer'); + $actual = $reflectionMethod->invoke($model, null); $this->assertSame($defaultRenderer, $actual); }