@@ -49,7 +49,6 @@ class Product extends AbstractEntity
49
49
{
50
50
private const DEFAULT_GLOBAL_MULTIPLE_VALUE_SEPARATOR = ', ' ;
51
51
public const CONFIG_KEY_PRODUCT_TYPES = 'global/importexport/import_product_types ' ;
52
- private const HASH_ALGORITHM = 'sha256 ' ;
53
52
54
53
/**
55
54
* Size of bunch - part of products to save in one step.
@@ -264,6 +263,11 @@ class Product extends AbstractEntity
264
263
*/
265
264
protected $ _mediaGalleryAttributeId = null ;
266
265
266
+ /**
267
+ * @var string
268
+ */
269
+ private $ hashAlgorithm = 'crc32c ' ;
270
+
267
271
/**
268
272
* @var array
269
273
* @codingStandardsIgnoreStart
@@ -904,7 +908,7 @@ public function __construct(
904
908
$ this ->linkProcessor = $ linkProcessor ?? ObjectManager::getInstance ()
905
909
->get (LinkProcessor::class);
906
910
$ this ->linkProcessor ->addNameToIds ($ this ->_linkNameToId );
907
-
911
+ $ this -> hashAlgorithm = ( version_compare ( PHP_VERSION , ' 8.1.0 ' ) >= 0 ) ? ' xxh128 ' : ' crc32c ' ;
908
912
parent ::__construct (
909
913
$ jsonHelper ,
910
914
$ importExportData ,
@@ -1572,6 +1576,7 @@ protected function _saveProducts()
1572
1576
$ priceIsGlobal = $ this ->_catalogData ->isPriceGlobal ();
1573
1577
$ previousType = null ;
1574
1578
$ prevAttributeSet = null ;
1579
+ $ productMediaPath = $ this ->getProductMediaPath ();
1575
1580
while ($ bunch = $ this ->_dataSourceModel ->getNextBunch ()) {
1576
1581
$ entityRowsIn = [];
1577
1582
$ entityRowsUp = [];
@@ -1583,7 +1588,6 @@ protected function _saveProducts()
1583
1588
$ imagesForChangeVisibility = [];
1584
1589
$ uploadedImages = [];
1585
1590
$ existingImages = $ this ->getExistingImages ($ bunch );
1586
- $ this ->addImageHashes ($ existingImages );
1587
1591
$ attributes = [];
1588
1592
foreach ($ bunch as $ rowNum => $ rowData ) {
1589
1593
try {
@@ -1630,6 +1634,7 @@ protected function _saveProducts()
1630
1634
$ rowData ,
1631
1635
$ storeId ,
1632
1636
$ existingImages ,
1637
+ $ productMediaPath ,
1633
1638
$ uploadedImages ,
1634
1639
$ imagesForChangeVisibility ,
1635
1640
$ labelsForUpdate ,
@@ -1683,7 +1688,6 @@ protected function _saveProducts()
1683
1688
private function saveProductEntityPhase (array $ rowData , array &$ entityRowsUp , array &$ entityRowsIn ) : void
1684
1689
{
1685
1690
$ rowSku = $ rowData [self ::COL_SKU ];
1686
- // 1. Entity phase
1687
1691
if ($ this ->isSkuExist ($ rowSku )) {
1688
1692
// existing row
1689
1693
if (isset ($ rowData ['attribute_set_code ' ])) {
@@ -1801,6 +1805,7 @@ private function saveProductTierPricesPhase(array $rowData, bool $priceIsGlobal,
1801
1805
* @param array $rowData
1802
1806
* @param int $storeId
1803
1807
* @param array $existingImages
1808
+ * @param string $productMediaPath
1804
1809
* @param array $uploadedImages
1805
1810
* @param array $imagesForChangeVisibility
1806
1811
* @param array $labelsForUpdate
@@ -1815,6 +1820,7 @@ private function saveProductMediaGalleryPhase(
1815
1820
array &$ rowData ,
1816
1821
int $ storeId ,
1817
1822
array $ existingImages ,
1823
+ string $ productMediaPath ,
1818
1824
array &$ uploadedImages ,
1819
1825
array &$ imagesForChangeVisibility ,
1820
1826
array &$ labelsForUpdate ,
@@ -1848,10 +1854,11 @@ private function saveProductMediaGalleryPhase(
1848
1854
$ position = 0 ;
1849
1855
foreach ($ rowImages as $ column => $ columnImages ) {
1850
1856
foreach ($ columnImages as $ columnImageKey => $ columnImage ) {
1851
- $ hash = filter_var ($ columnImage , FILTER_VALIDATE_URL )
1852
- ? $ this ->getRemoteFileHash ($ columnImage )
1853
- : $ this ->getFileHash ($ this ->joinFilePaths ($ this ->getUploader ()->getTmpDir (), $ columnImage ));
1854
- $ uploadedFile = $ this ->findImageByHash ($ rowExistingImages , $ hash );
1857
+ $ uploadedFile = $ this ->findImageByColumnImage (
1858
+ $ productMediaPath ,
1859
+ $ rowExistingImages ,
1860
+ $ columnImage
1861
+ );
1855
1862
if (!$ uploadedFile && !isset ($ uploadedImages [$ columnImage ])) {
1856
1863
$ uploadedFile = $ this ->uploadMediaFiles ($ columnImage );
1857
1864
$ uploadedFile = $ uploadedFile ?: $ this ->getSystemFile ($ columnImage );
@@ -1966,7 +1973,7 @@ private function saveProductAttributesPhase(
1966
1973
$ productType = $ previousType ;
1967
1974
}
1968
1975
if ($ productType === null ) {
1969
- throw new Skip ('Unknown Product Type ' );
1976
+ throw new Skip (__ ( 'Unknown Product Type ' ) );
1970
1977
}
1971
1978
}
1972
1979
$ productTypeModel = $ this ->_productTypeModels [$ productType ];
@@ -2034,54 +2041,34 @@ private function saveProductAttributesPhase(
2034
2041
}
2035
2042
2036
2043
/**
2037
- * Returns image hash by path
2044
+ * Returns image content by path
2038
2045
*
2039
2046
* @param string $path
2040
2047
* @return string
2041
2048
* @throws \Magento\Framework\Exception\FileSystemException
2042
2049
*/
2043
- private function getFileHash (string $ path ): string
2050
+ private function getFileContent (string $ path ): string
2044
2051
{
2045
- $ content = '' ;
2046
2052
if ($ this ->_mediaDirectory ->isFile ($ path )
2047
2053
&& $ this ->_mediaDirectory ->isReadable ($ path )
2048
2054
) {
2049
- $ content = $ this ->_mediaDirectory ->readFile ($ path );
2055
+ return $ this ->_mediaDirectory ->readFile ($ path );
2050
2056
}
2051
- return $ content ? hash ( self :: HASH_ALGORITHM , $ content ) : '' ;
2057
+ return '' ;
2052
2058
}
2053
2059
2054
2060
/**
2055
- * Returns hash for remote file
2061
+ * Returns content for remote file
2056
2062
*
2057
2063
* @param string $filename
2058
2064
* @return string
2059
2065
*/
2060
- private function getRemoteFileHash (string $ filename ): string
2061
- {
2062
- $ hash = hash_file (self ::HASH_ALGORITHM , $ filename );
2063
- return $ hash !== false ? $ hash : '' ;
2064
- }
2065
-
2066
- /**
2067
- * Generate hashes for existing images for comparison with newly uploaded images.
2068
- *
2069
- * @param array $images
2070
- * @return void
2071
- */
2072
- private function addImageHashes (array &$ images ): void
2066
+ private function getRemoteFileContent (string $ filename ): string
2073
2067
{
2074
- $ productMediaPath = $ this ->getProductMediaPath ();
2075
- foreach ($ images as $ storeId => $ skus ) {
2076
- foreach ($ skus as $ sku => $ files ) {
2077
- foreach ($ files as $ path => $ file ) {
2078
- $ hash = $ this ->getFileHash ($ this ->joinFilePaths ($ productMediaPath , $ file ['value ' ]));
2079
- if ($ hash ) {
2080
- $ images [$ storeId ][$ sku ][$ path ]['hash ' ] = $ hash ;
2081
- }
2082
- }
2083
- }
2084
- }
2068
+ // phpcs:disable Magento2.Functions.DiscouragedFunction
2069
+ $ content = file_get_contents ($ filename );
2070
+ // phpcs:enable Magento2.Functions.DiscouragedFunction
2071
+ return $ content !== false ? $ content : '' ;
2085
2072
}
2086
2073
2087
2074
/**
@@ -3328,24 +3315,93 @@ private function getRowExistingStockItem(array $rowData): StockItemInterface
3328
3315
}
3329
3316
3330
3317
/**
3331
- * Returns image that matches the provided hash
3318
+ * Returns image that matches the provided image content
3332
3319
*
3320
+ * @param string $productMediaPath
3333
3321
* @param array $images
3334
- * @param string $hash
3322
+ * @param string $columnImage
3335
3323
* @return string
3336
3324
*/
3337
- private function findImageByHash ( array $ images , string $ hash ): string
3325
+ private function findImageByColumnImage ( string $ productMediaPath , array & $ images , string $ columnImage ): string
3338
3326
{
3339
- $ value = '' ;
3340
- if ($ hash ) {
3341
- foreach ($ images as $ image ) {
3342
- if (isset ($ image ['hash ' ]) && $ image ['hash ' ] === $ hash ) {
3343
- $ value = $ image ['value ' ];
3344
- break ;
3327
+ $ content = filter_var ($ columnImage , FILTER_VALIDATE_URL )
3328
+ ? $ this ->getRemoteFileContent ($ columnImage )
3329
+ : $ this ->getFileContent ($ this ->joinFilePaths ($ this ->getUploader ()->getTmpDir (), $ columnImage ));
3330
+ if (!$ content ) {
3331
+ return '' ;
3332
+ }
3333
+ if ($ this ->shouldUseHash ($ images )) {
3334
+ return $ this ->findImageByColumnImageUsingHash ($ productMediaPath , $ images , $ content );
3335
+ } else {
3336
+ return $ this ->findImageByColumnImageUsingContent ($ productMediaPath , $ images , $ content );
3337
+ }
3338
+ }
3339
+
3340
+ /**
3341
+ * Returns image that matches the provided image content using hash
3342
+ *
3343
+ * @param string $productMediaPath
3344
+ * @param array $images
3345
+ * @param string $content
3346
+ * @return string
3347
+ */
3348
+ private function findImageByColumnImageUsingHash (string $ productMediaPath , array &$ images , string $ content ): string
3349
+ {
3350
+ $ hash = hash ($ this ->hashAlgorithm , $ content );
3351
+ foreach ($ images as &$ image ) {
3352
+ if (!isset ($ image ['hash ' ])) {
3353
+ $ imageContent = $ this ->getFileContent ($ this ->joinFilePaths ($ productMediaPath , $ image ['value ' ]));
3354
+ if (!$ imageContent ) {
3355
+ $ image ['hash ' ] = '' ;
3356
+ continue ;
3345
3357
}
3358
+ $ image ['hash ' ] = hash ($ this ->hashAlgorithm , $ imageContent );
3359
+ }
3360
+ if (!empty ($ image ['hash ' ]) && $ image ['hash ' ] === $ hash ) {
3361
+ return $ image ['value ' ];
3346
3362
}
3347
3363
}
3348
- return $ value ;
3364
+ return '' ;
3365
+ }
3366
+
3367
+ /**
3368
+ * Returns image that matches the provided image content using content
3369
+ *
3370
+ * @param string $productMediaPath
3371
+ * @param array $images
3372
+ * @param string $content
3373
+ * @return string
3374
+ */
3375
+ private function findImageByColumnImageUsingContent (
3376
+ string $ productMediaPath ,
3377
+ array &$ images ,
3378
+ string $ content
3379
+ ): string {
3380
+ foreach ($ images as &$ image ) {
3381
+ if (!isset ($ image ['content ' ])) {
3382
+ $ image ['content ' ] = $ this ->getFileContent (
3383
+ $ this ->joinFilePaths ($ productMediaPath , $ image ['value ' ])
3384
+ );
3385
+ }
3386
+ if ($ content === $ image ['content ' ]) {
3387
+ return $ image ['value ' ];
3388
+ }
3389
+ }
3390
+ return '' ;
3391
+ }
3392
+
3393
+ /**
3394
+ * Returns true if we should use hash instead of just comparing content
3395
+ *
3396
+ * @param array $images
3397
+ * @return bool
3398
+ */
3399
+ private function shouldUseHash (array $ images ): bool
3400
+ {
3401
+ if (count ($ images ) > 100 ) {
3402
+ return true ;
3403
+ }
3404
+ return false ;
3349
3405
}
3350
3406
3351
3407
/**
0 commit comments