Skip to content

Commit f2dbf23

Browse files
committed
Merge remote-tracking branch 'magento-l3/ACP2E-315' into L3_PR_21-12-13
2 parents 44e055e + 0d43a14 commit f2dbf23

File tree

2 files changed

+80
-6
lines changed

2 files changed

+80
-6
lines changed

app/code/Magento/Catalog/Model/Product/Image.php

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class Image extends \Magento\Framework\Model\AbstractModel
2929
/**
3030
* Config path for the jpeg image quality value
3131
*/
32-
const XML_PATH_JPEG_QUALITY = 'system/upload_configuration/jpeg_quality';
32+
public const XML_PATH_JPEG_QUALITY = 'system/upload_configuration/jpeg_quality';
3333

3434
/**
3535
* @var int
@@ -836,7 +836,37 @@ public function getWatermarkHeight()
836836
public function clearCache()
837837
{
838838
$directory = $this->_catalogProductMediaConfig->getBaseMediaPath() . '/cache';
839-
$this->_mediaDirectory->delete($directory);
839+
$directoryToDelete = $directory;
840+
// Fixes issue when deleting cache directory at the same time that images are being
841+
// lazy-loaded on storefront leading to new directories and files generation in the cache directory
842+
// that would prevent deletion of the cache directory.
843+
// RCA: the method delete() recursively enumerates and delete all subdirectories and files before deleting
844+
// the target directory, which gives other processes time to create directories and files in the same directory.
845+
// Solution: Rename the directory to simulate deletion and delete the destination directory afterward
846+
847+
try {
848+
//generate name in format: \.[0-9A-ZA-z-_]{3} (e.g .QX3)
849+
$tmpDirBasename = strrev(strtr(base64_encode(random_bytes(2)), '+/=', '-_.'));
850+
$tmpDirectory = $this->_catalogProductMediaConfig->getBaseMediaPath() . '/' . $tmpDirBasename;
851+
//delete temporary directory if exists
852+
if ($this->_mediaDirectory->isDirectory($tmpDirectory)) {
853+
$this->_mediaDirectory->delete($tmpDirectory);
854+
}
855+
//rename the directory to simulate deletion and delete the destination directory
856+
if ($this->_mediaDirectory->isDirectory($directory) &&
857+
true === $this->_mediaDirectory->getDriver()->rename(
858+
$this->_mediaDirectory->getAbsolutePath($directory),
859+
$this->_mediaDirectory->getAbsolutePath($tmpDirectory)
860+
)
861+
) {
862+
$directoryToDelete = $tmpDirectory;
863+
}
864+
} catch (\Throwable $exception) {
865+
//ignore exceptions thrown during renaming
866+
$directoryToDelete = $directory;
867+
}
868+
869+
$this->_mediaDirectory->delete($directoryToDelete);
840870

841871
$this->_coreFileStorageDatabase->deleteFolder($this->_mediaDirectory->getAbsolutePath($directory));
842872
$this->clearImageInfoFromCache();
@@ -870,6 +900,7 @@ protected function _fileExists($filename)
870900
public function getResizedImageInfo()
871901
{
872902
try {
903+
$image = null;
873904
if ($this->isBaseFilePlaceholder() == true) {
874905
$image = $this->imageAsset->getSourceFile();
875906
} else {
@@ -920,6 +951,7 @@ private function getImageSize($imagePath)
920951
{
921952
$imageInfo = $this->loadImageInfoFromCache($imagePath);
922953
if (!isset($imageInfo['size'])) {
954+
// phpcs:ignore Magento2.Functions.DiscouragedFunction
923955
$imageSize = getimagesize($imagePath);
924956
$this->saveImageInfoToCache(['size' => $imageSize], $imagePath);
925957
return $imageSize;

app/code/Magento/Catalog/Test/Unit/Model/Product/ImageTest.php

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Magento\Framework\Filesystem;
1818
use Magento\Framework\Filesystem\Directory\Write;
1919
use Magento\Framework\Filesystem\Directory\WriteInterface;
20+
use Magento\Framework\Filesystem\DriverInterface;
2021
use Magento\Framework\Image\Factory;
2122
use Magento\Framework\Model\Context;
2223
use Magento\Framework\Serialize\SerializerInterface;
@@ -140,7 +141,7 @@ protected function setUp(): void
140141

141142
$this->mediaDirectory = $this->getMockBuilder(Write::class)
142143
->disableOriginalConstructor()
143-
->onlyMethods(['create', 'isFile', 'isExist', 'getAbsolutePath'])
144+
->onlyMethods(['create', 'isFile', 'isExist', 'getAbsolutePath', 'isDirectory', 'getDriver', 'delete'])
144145
->getMock();
145146

146147
$this->filesystem = $this->createMock(Filesystem::class);
@@ -503,15 +504,56 @@ public function testIsCached(): void
503504
}
504505

505506
/**
507+
* @param bool $isRenameSuccessful
508+
* @param string $expectedDirectoryToDelete
506509
* @return void
507-
*/
508-
public function testClearCache(): void
509-
{
510+
* @throws \Magento\Framework\Exception\FileSystemException
511+
* @dataProvider clearCacheDataProvider
512+
*/
513+
public function testClearCache(
514+
bool $isRenameSuccessful,
515+
string $expectedDirectoryToDelete
516+
): void {
517+
$driver = $this->createMock(DriverInterface::class);
518+
$this->mediaDirectory->method('getAbsolutePath')
519+
->willReturnCallback(
520+
function (string $path) {
521+
return 'path/to/media/' . $path;
522+
}
523+
);
524+
$this->mediaDirectory->expects($this->exactly(2))
525+
->method('isDirectory')
526+
->willReturnOnConsecutiveCalls(false, true);
527+
$this->mediaDirectory->expects($this->once())
528+
->method('getDriver')
529+
->willReturn($driver);
530+
$driver->expects($this->once())
531+
->method('rename')
532+
->with(
533+
'path/to/media/catalog/product/cache',
534+
$this->matchesRegularExpression('/^path\/to\/media\/catalog\/product\/\.[0-9A-ZA-z-_]{3}$/')
535+
)
536+
->willReturn($isRenameSuccessful);
537+
$this->mediaDirectory->expects($this->once())
538+
->method('delete')
539+
->with($this->matchesRegularExpression($expectedDirectoryToDelete));
540+
510541
$this->coreFileHelper->expects($this->once())->method('deleteFolder')->willReturn(true);
511542
$this->cacheManager->expects($this->once())->method('clean');
512543
$this->image->clearCache();
513544
}
514545

546+
/**
547+
* @return array
548+
*/
549+
public function clearCacheDataProvider(): array
550+
{
551+
return [
552+
[true, '/^catalog\/product\/\.[0-9A-ZA-z-_]{3}$/'],
553+
[false, '/^catalog\/product\/cache$/'],
554+
];
555+
}
556+
515557
/**
516558
* @return void
517559
*/

0 commit comments

Comments
 (0)