1<?php 2 3/* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Symfony\Component\Finder\Iterator; 13 14/** 15 * SortableIterator applies a sort on a given Iterator. 16 * 17 * @author Fabien Potencier <fabien@symfony.com> 18 * 19 * @implements \IteratorAggregate<string, \SplFileInfo> 20 */ 21class SortableIterator implements \IteratorAggregate 22{ 23 public const SORT_BY_NONE = 0; 24 public const SORT_BY_NAME = 1; 25 public const SORT_BY_TYPE = 2; 26 public const SORT_BY_ACCESSED_TIME = 3; 27 public const SORT_BY_CHANGED_TIME = 4; 28 public const SORT_BY_MODIFIED_TIME = 5; 29 public const SORT_BY_NAME_NATURAL = 6; 30 31 private $iterator; 32 private $sort; 33 34 /** 35 * @param \Traversable<string, \SplFileInfo> $iterator 36 * @param int|callable $sort The sort type (SORT_BY_NAME, SORT_BY_TYPE, or a PHP callback) 37 * 38 * @throws \InvalidArgumentException 39 */ 40 public function __construct(\Traversable $iterator, $sort, bool $reverseOrder = false) 41 { 42 $this->iterator = $iterator; 43 $order = $reverseOrder ? -1 : 1; 44 45 if (self::SORT_BY_NAME === $sort) { 46 $this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) { 47 return $order * strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname()); 48 }; 49 } elseif (self::SORT_BY_NAME_NATURAL === $sort) { 50 $this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) { 51 return $order * strnatcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname()); 52 }; 53 } elseif (self::SORT_BY_TYPE === $sort) { 54 $this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) { 55 if ($a->isDir() && $b->isFile()) { 56 return -$order; 57 } elseif ($a->isFile() && $b->isDir()) { 58 return $order; 59 } 60 61 return $order * strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname()); 62 }; 63 } elseif (self::SORT_BY_ACCESSED_TIME === $sort) { 64 $this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) { 65 return $order * ($a->getATime() - $b->getATime()); 66 }; 67 } elseif (self::SORT_BY_CHANGED_TIME === $sort) { 68 $this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) { 69 return $order * ($a->getCTime() - $b->getCTime()); 70 }; 71 } elseif (self::SORT_BY_MODIFIED_TIME === $sort) { 72 $this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) { 73 return $order * ($a->getMTime() - $b->getMTime()); 74 }; 75 } elseif (self::SORT_BY_NONE === $sort) { 76 $this->sort = $order; 77 } elseif (\is_callable($sort)) { 78 $this->sort = $reverseOrder ? static function (\SplFileInfo $a, \SplFileInfo $b) use ($sort) { return -$sort($a, $b); } : $sort; 79 } else { 80 throw new \InvalidArgumentException('The SortableIterator takes a PHP callable or a valid built-in sort algorithm as an argument.'); 81 } 82 } 83 84 /** 85 * @return \Traversable<string, \SplFileInfo> 86 */ 87 #[\ReturnTypeWillChange] 88 public function getIterator() 89 { 90 if (1 === $this->sort) { 91 return $this->iterator; 92 } 93 94 $array = iterator_to_array($this->iterator, true); 95 96 if (-1 === $this->sort) { 97 $array = array_reverse($array); 98 } else { 99 uasort($array, $this->sort); 100 } 101 102 return new \ArrayIterator($array); 103 } 104} 105