14
14
15
15
/**
16
16
* Driver for AWS S3 IO operations.
17
+ *
18
+ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
17
19
*/
18
20
class AwsS3 implements DriverInterface
19
21
{
@@ -173,21 +175,15 @@ public function filePutContents($path, $content, $mode = null, $context = null):
173
175
*/
174
176
public function readDirectoryRecursively ($ path = null ): array
175
177
{
176
- return $ this ->adapter ->listContents (
177
- $ this ->normalizeRelativePath ($ path ),
178
- true
179
- );
178
+ return $ this ->readPath ($ path , true );
180
179
}
181
180
182
181
/**
183
182
* @inheritDoc
184
183
*/
185
184
public function readDirectory ($ path ): array
186
185
{
187
- return $ this ->adapter ->listContents (
188
- $ this ->normalizeRelativePath ($ path ),
189
- false
190
- );
186
+ return $ this ->readPath ($ path , false );
191
187
}
192
188
193
189
/**
@@ -402,11 +398,11 @@ public function stat($path): array
402
398
'ctime ' => 0 ,
403
399
'blksize ' => 0 ,
404
400
'blocks ' => 0 ,
405
- 'size ' => $ metaInfo ['size ' ],
406
- 'type ' => $ metaInfo ['type ' ],
407
- 'mtime ' => $ metaInfo ['timestamp ' ],
401
+ 'size ' => $ metaInfo ['size ' ] ?? 0 ,
402
+ 'type ' => $ metaInfo ['type ' ] ?? 0 ,
403
+ 'mtime ' => $ metaInfo ['timestamp ' ] ?? 0 ,
408
404
'disposition ' => null ,
409
- 'mimetype ' => $ metaInfo ['mimetype ' ]
405
+ 'mimetype ' => $ metaInfo ['mimetype ' ] ?? 0
410
406
];
411
407
}
412
408
@@ -415,7 +411,36 @@ public function stat($path): array
415
411
*/
416
412
public function search ($ pattern , $ path ): array
417
413
{
418
- throw new FileSystemException (__ ('Method %1 is not supported ' , __METHOD__ ));
414
+ return $ this ->glob (rtrim ($ path , '/ ' ) . '/ ' . ltrim ($ pattern , '/ ' ));
415
+ }
416
+
417
+ /**
418
+ * Emulate php glob function for AWS S3 storage
419
+ *
420
+ * @param string $pattern
421
+ * @return array
422
+ * @throws FileSystemException
423
+ */
424
+ private function glob (string $ pattern ): array
425
+ {
426
+ $ directoryContent = [];
427
+
428
+ $ patternFound = preg_match ('(\*|\?|\[.+\]) ' , $ pattern , $ parentPattern , PREG_OFFSET_CAPTURE );
429
+ if ($ patternFound ) {
430
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
431
+ $ parentDirectory = \dirname (substr ($ pattern , 0 , $ parentPattern [0 ][1 ] + 1 ));
432
+ $ leftover = substr ($ pattern , $ parentPattern [0 ][1 ]);
433
+ $ index = strpos ($ leftover , '/ ' );
434
+ $ searchPattern = $ this ->getSearchPattern ($ pattern , $ parentPattern , $ parentDirectory , $ index );
435
+
436
+ if ($ this ->isDirectory ($ parentDirectory . '/ ' )) {
437
+ $ directoryContent = $ this ->getDirectoryContent ($ parentDirectory , $ searchPattern , $ leftover , $ index );
438
+ }
439
+ } elseif ($ this ->isDirectory ($ pattern ) || $ this ->isFile ($ pattern )) {
440
+ $ directoryContent [] = $ pattern ;
441
+ }
442
+
443
+ return $ directoryContent ;
419
444
}
420
445
421
446
/**
@@ -630,4 +655,98 @@ private function getWarningMessage(): ?string
630
655
631
656
return null ;
632
657
}
658
+
659
+ /**
660
+ * Read directory by path and is recursive flag
661
+ *
662
+ * @param string $path
663
+ * @param bool $isRecursive
664
+ * @return array
665
+ */
666
+ private function readPath (string $ path , $ isRecursive = false ): array
667
+ {
668
+ $ relativePath = $ this ->normalizeRelativePath ($ path );
669
+ $ contentsList = $ this ->adapter ->listContents (
670
+ $ relativePath ,
671
+ $ isRecursive
672
+ );
673
+ $ itemsList = [];
674
+ foreach ($ contentsList as $ item ) {
675
+ if (isset ($ item ['path ' ])
676
+ && $ item ['path ' ] !== $ relativePath
677
+ && strpos ($ item ['path ' ], $ relativePath ) === 0 ) {
678
+ $ itemsList [] = $ item ['path ' ];
679
+ }
680
+ }
681
+
682
+ return $ itemsList ;
683
+ }
684
+
685
+ /**
686
+ * Get search pattern for directory
687
+ *
688
+ * @param string $pattern
689
+ * @param array $parentPattern
690
+ * @param string $parentDirectory
691
+ * @param int|bool $index
692
+ * @return string
693
+ */
694
+ private function getSearchPattern (string $ pattern , array $ parentPattern , string $ parentDirectory , $ index ): string
695
+ {
696
+ $ parentLength = \strlen ($ parentDirectory );
697
+ if ($ index !== false ) {
698
+ $ searchPattern = substr (
699
+ $ pattern ,
700
+ $ parentLength + 1 ,
701
+ $ parentPattern [0 ][1 ] - $ parentLength + $ index - 1
702
+ );
703
+ } else {
704
+ $ searchPattern = substr ($ pattern , $ parentLength + 1 );
705
+ }
706
+
707
+ $ replacement = [
708
+ '/\*/ ' => '.* ' ,
709
+ '/\?/ ' => '. ' ,
710
+ '/\// ' => '\/ '
711
+ ];
712
+ return preg_replace (array_keys ($ replacement ), array_values ($ replacement ), $ searchPattern );
713
+ }
714
+
715
+ /**
716
+ * Get directory content by given search pattern
717
+ *
718
+ * @param string $parentDirectory
719
+ * @param string $searchPattern
720
+ * @param string $leftover
721
+ * @param int|bool $index
722
+ * @return array
723
+ * @throws FileSystemException
724
+ */
725
+ private function getDirectoryContent (
726
+ string $ parentDirectory ,
727
+ string $ searchPattern ,
728
+ string $ leftover ,
729
+ $ index
730
+ ): array {
731
+ $ items = $ this ->readDirectory ($ parentDirectory . '/ ' );
732
+ $ directoryContent = [];
733
+ foreach ($ items as $ item ) {
734
+ if (preg_match ('/ ' . $ searchPattern . '$/ ' , $ item )
735
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
736
+ && strpos (basename ($ item ), '. ' ) !== 0 ) {
737
+ if ($ index === false || \strlen ($ leftover ) === $ index + 1 ) {
738
+ $ directoryContent [] = $ this ->isDirectory ($ item )
739
+ ? rtrim ($ item , '/ ' ) . '/ '
740
+ : $ item ;
741
+ } elseif (strlen ($ leftover ) > $ index + 1 ) {
742
+ // phpcs:ignore Magento2.Performance.ForeachArrayMerge
743
+ $ directoryContent = array_merge (
744
+ $ directoryContent ,
745
+ $ this ->glob ("{$ parentDirectory }/ {$ item }" . substr ($ leftover , $ index ))
746
+ );
747
+ }
748
+ }
749
+ }
750
+ return $ directoryContent ;
751
+ }
633
752
}
0 commit comments