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 */ 19class SortableIterator implements \IteratorAggregate 20{ 21 public const SORT_BY_NONE = 0; 22 public const SORT_BY_NAME = 1; 23 public const SORT_BY_TYPE = 2; 24 public const SORT_BY_ACCESSED_TIME = 3; 25 public const SORT_BY_CHANGED_TIME = 4; 26 public const SORT_BY_MODIFIED_TIME = 5; 27 public const SORT_BY_NAME_NATURAL = 6; 28 29 private $iterator; 30 private $sort; 31 32 /** 33 * @param \Traversable $iterator The Iterator to filter 34 * @param int|callable $sort The sort type (SORT_BY_NAME, SORT_BY_TYPE, or a PHP callback) 35 * 36 * @throws \InvalidArgumentException 37 */ 38 public function __construct(\Traversable $iterator, $sort, bool $reverseOrder = false) 39 { 40 $this->iterator = $iterator; 41 $order = $reverseOrder ? -1 : 1; 42 43 if (self::SORT_BY_NAME === $sort) { 44 $this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) { 45 return $order * strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname()); 46 }; 47 } elseif (self::SORT_BY_NAME_NATURAL === $sort) { 48 $this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) { 49 return $order * strnatcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname()); 50 }; 51 } elseif (self::SORT_BY_TYPE === $sort) { 52 $this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) { 53 if ($a->isDir() && $b->isFile()) { 54 return -$order; 55 } elseif ($a->isFile() && $b->isDir()) { 56 return $order; 57 } 58 59 return $order * strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname()); 60 }; 61 } elseif (self::SORT_BY_ACCESSED_TIME === $sort) { 62 $this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) { 63 return $order * ($a->getATime() - $b->getATime()); 64 }; 65 } elseif (self::SORT_BY_CHANGED_TIME === $sort) { 66 $this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) { 67 return $order * ($a->getCTime() - $b->getCTime()); 68 }; 69 } elseif (self::SORT_BY_MODIFIED_TIME === $sort) { 70 $this->sort = static function (\SplFileInfo $a, \SplFileInfo $b) use ($order) { 71 return $order * ($a->getMTime() - $b->getMTime()); 72 }; 73 } elseif (self::SORT_BY_NONE === $sort) { 74 $this->sort = $order; 75 } elseif (\is_callable($sort)) { 76 $this->sort = $reverseOrder ? static function (\SplFileInfo $a, \SplFileInfo $b) use ($sort) { return -$sort($a, $b); } : $sort; 77 } else { 78 throw new \InvalidArgumentException('The SortableIterator takes a PHP callable or a valid built-in sort algorithm as an argument.'); 79 } 80 } 81 82 /** 83 * @return \Traversable 84 */ 85 public function getIterator() 86 { 87 if (1 === $this->sort) { 88 return $this->iterator; 89 } 90 91 $array = iterator_to_array($this->iterator, true); 92 93 if (-1 === $this->sort) { 94 $array = array_reverse($array); 95 } else { 96 uasort($array, $this->sort); 97 } 98 99 return new \ArrayIterator($array); 100 } 101} 102