From 507e21e9ccc1448107805a9d6529b7a249047fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20B=C3=BCchler?= <info@buechler.pro> Date: Tue, 30 Nov 2021 10:00:38 +0200 Subject: [PATCH] [BUGFIX] Make file processing configuration serializable This allows to use the mask property from the TypoScript imgResource function again. Resolves: #96116 Releases: main, 11.5 Change-Id: Ia069a687e98a91437959e1bbd4efc5d59fb2017c Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/72395 Tested-by: core-ci <typo3@b13.com> Tested-by: Christian Kuhn <lolli@schwarzbu.ch> Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch> Tested-by: Nikita Hovratov <nikita.h@live.de> Reviewed-by: Nikita Hovratov <nikita.h@live.de> --- .../core/Classes/Resource/ProcessedFile.php | 4 +- .../Resource/ProcessedFileRepository.php | 3 +- .../Resource/Processing/AbstractTask.php | 3 +- .../Resource/Service/ConfigurationService.php | 55 ++++++++++++++ .../Resource/Fixtures/ProcessedFileTest.jpg | 0 .../Functional/Resource/ProcessedFileTest.php | 71 +++++++++++++++++++ .../Service/ConfigurationServiceTest.php | 55 ++++++++++++++ 7 files changed, 188 insertions(+), 3 deletions(-) create mode 100644 typo3/sysext/core/Classes/Resource/Service/ConfigurationService.php create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/ProcessedFileTest.jpg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/ProcessedFileTest.php create mode 100644 typo3/sysext/core/Tests/Unit/Resource/Service/ConfigurationServiceTest.php diff --git a/typo3/sysext/core/Classes/Resource/ProcessedFile.php b/typo3/sysext/core/Classes/Resource/ProcessedFile.php index a3dce434486d..6fee23181cac 100644 --- a/typo3/sysext/core/Classes/Resource/ProcessedFile.php +++ b/typo3/sysext/core/Classes/Resource/ProcessedFile.php @@ -16,6 +16,7 @@ namespace TYPO3\CMS\Core\Resource; use TYPO3\CMS\Core\Resource\Processing\TaskTypeRegistry; +use TYPO3\CMS\Core\Resource\Service\ConfigurationService; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MathUtility; @@ -143,6 +144,7 @@ class ProcessedFile extends AbstractFile protected function reconstituteFromDatabaseRecord(array $databaseRow) { $this->taskType = $this->taskType ?: $databaseRow['task_type']; + // @todo In case the original configuration contained file objects the reconstitution fails. See ConfigurationService->serialize() $this->processingConfiguration = $this->processingConfiguration ?: unserialize($databaseRow['configuration'] ?? ''); $this->originalFileSha1 = $databaseRow['originalfilesha1']; @@ -399,7 +401,7 @@ class ProcessedFile extends AbstractFile $properties['name'] = $this->getName(); } - $properties['configuration'] = serialize($this->processingConfiguration); + $properties['configuration'] = (new ConfigurationService())->serialize($this->processingConfiguration); return array_merge($properties, [ 'storage' => $this->getStorage()->getUid(), diff --git a/typo3/sysext/core/Classes/Resource/ProcessedFileRepository.php b/typo3/sysext/core/Classes/Resource/ProcessedFileRepository.php index 46b39b42b32c..d998a4d2ab3e 100644 --- a/typo3/sysext/core/Classes/Resource/ProcessedFileRepository.php +++ b/typo3/sysext/core/Classes/Resource/ProcessedFileRepository.php @@ -20,6 +20,7 @@ use Psr\Log\LoggerAwareTrait; use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Imaging\ImageManipulation\Area; +use TYPO3\CMS\Core\Resource\Service\ConfigurationService; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -240,7 +241,7 @@ class ProcessedFileRepository extends AbstractRepository implements LoggerAwareI $queryBuilder->expr()->eq('task_type', $queryBuilder->createNamedParameter($taskType)), $queryBuilder->expr()->eq( 'configurationsha1', - $queryBuilder->createNamedParameter(sha1(serialize($configuration))) + $queryBuilder->createNamedParameter(sha1((new ConfigurationService())->serialize($configuration))) ) ) ->executeQuery() diff --git a/typo3/sysext/core/Classes/Resource/Processing/AbstractTask.php b/typo3/sysext/core/Classes/Resource/Processing/AbstractTask.php index 8a66263876d6..8a41c9d3e840 100644 --- a/typo3/sysext/core/Classes/Resource/Processing/AbstractTask.php +++ b/typo3/sysext/core/Classes/Resource/Processing/AbstractTask.php @@ -18,6 +18,7 @@ namespace TYPO3\CMS\Core\Resource\Processing; use TYPO3\CMS\Core\Resource; use TYPO3\CMS\Core\Resource\File; use TYPO3\CMS\Core\Resource\ProcessedFile; +use TYPO3\CMS\Core\Resource\Service\ConfigurationService; /** * Abstract base implementation of a task. @@ -91,7 +92,7 @@ abstract class AbstractTask implements TaskInterface return [ $this->getSourceFile()->getUid(), $this->getType() . '.' . $this->getName() . $this->getSourceFile()->getModificationTime(), - serialize($this->configuration), + (new ConfigurationService())->serialize($this->configuration), ]; } diff --git a/typo3/sysext/core/Classes/Resource/Service/ConfigurationService.php b/typo3/sysext/core/Classes/Resource/Service/ConfigurationService.php new file mode 100644 index 000000000000..a8f45eabf2cc --- /dev/null +++ b/typo3/sysext/core/Classes/Resource/Service/ConfigurationService.php @@ -0,0 +1,55 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3\CMS\Core\Resource\Service; + +use TYPO3\CMS\Core\Resource\FileInterface; + +/** + * Resources can contain configurations: For example to define image dimensions or + * image masking. Resource configurations form part of the object identity and are + * used to create an object signature that itself is used to cache objects but NOT + * to reconstruct them. To prevent objects to be reconstructed they MUST NOT be + * serialized. This is why an object signature is obtained by serializing its array + * rather than serializing it directly. But attention...as well the objects array + * MUST NOT contain resource objects which could be the case when a configuration + * defines image masking. Here this service comes into play: it serializes any + * object configuration, as well those containing resources. + */ +class ConfigurationService +{ + public function serialize(array $configuration): string + { + return serialize($this->makeSerializable($configuration)); + } + + /** + * Recursively substitute file objects with their array representation. + */ + protected function makeSerializable(array $configuration): array + { + return array_map(function ($value) { + if (is_array($value)) { + return $this->makeSerializable($value); + } + if ($value instanceof FileInterface) { + return $value->toArray(); + } + return $value; + }, $configuration); + } +} diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/ProcessedFileTest.jpg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/ProcessedFileTest.jpg new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/typo3/sysext/core/Tests/Functional/Resource/ProcessedFileTest.php b/typo3/sysext/core/Tests/Functional/Resource/ProcessedFileTest.php new file mode 100644 index 000000000000..729f46e98367 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/ProcessedFileTest.php @@ -0,0 +1,71 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3\CMS\Core\Tests\Functional\Resource; + +use TYPO3\CMS\Core\Core\Bootstrap; +use TYPO3\CMS\Core\Resource\ProcessedFile; +use TYPO3\CMS\Core\Resource\ResourceFactory; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; + +class ProcessedFileTest extends FunctionalTestCase +{ + private const TEST_IMAGE = 'fileadmin/ProcessedFileTest.jpg'; + + /** + * @var array<string, non-empty-string> + */ + protected array $pathsToProvideInTestInstance = [ + 'typo3/sysext/core/Tests/Functional/Resource/Fixtures/ProcessedFileTest.jpg' => 'fileadmin/ProcessedFileTest.jpg', + ]; + + public function setUp(): void + { + parent::setUp(); + $this->importCSVDataSet(__DIR__ . '/../Fixtures/be_users.csv'); + $this->setUpBackendUser(1); + Bootstrap::initializeLanguageObject(); + } + + /** + * @test + */ + public function processedFileArrayCanBeSerialized(): void + { + $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); + $originalFile = $resourceFactory->retrieveFileOrFolderObject(self::TEST_IMAGE); + $someProcessedFile = new ProcessedFile( + $originalFile, + ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, + [] + ); + $processedFile = new ProcessedFile( + $originalFile, + ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, + [ + 'width' => '2000c', + 'height' => '300c-60', + 'm' => [ + 'bgImg' => $someProcessedFile, + 'mask' => $someProcessedFile, + ], + ], + ); + serialize($processedFile->toArray()); + } +} diff --git a/typo3/sysext/core/Tests/Unit/Resource/Service/ConfigurationServiceTest.php b/typo3/sysext/core/Tests/Unit/Resource/Service/ConfigurationServiceTest.php new file mode 100644 index 000000000000..e25f1bf3372b --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Resource/Service/ConfigurationServiceTest.php @@ -0,0 +1,55 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3\CMS\Core\Tests\Unit\Resource\Service; + +use TYPO3\CMS\Core\Resource\ProcessedFile; +use TYPO3\CMS\Core\Resource\Service\ConfigurationService; +use TYPO3\TestingFramework\Core\Unit\UnitTestCase; + +class ConfigurationServiceTest extends UnitTestCase +{ + /** + * @test + */ + public function serializeSubstitutesFileObject(): void + { + $fileMock = $this->createMock(ProcessedFile::class); + $fileMock->method('toArray')->willReturn(['id' => '1:test.jpg']); + $configuration = [ + 'width' => '2000c', + 'height' => '300c-60', + 'foo' => $fileMock, + 'maskImages' => [ + 'maskImage' => $fileMock, + 'backgroundImage' => $fileMock, + 'bar' => 'bar1', + ], + ]; + $expected = [ + 'width' => '2000c', + 'height' => '300c-60', + 'foo' => $fileMock->toArray(), + 'maskImages' => [ + 'maskImage' => $fileMock->toArray(), + 'backgroundImage' => $fileMock->toArray(), + 'bar' => 'bar1', + ], + ]; + self::assertSame(serialize($expected), (new ConfigurationService())->serialize($configuration)); + } +} -- GitLab