1<?php 2declare(strict_types = 1); 3namespace TYPO3\CMS\Core\Resource\Search\Result; 4 5/* 6 * This file is part of the TYPO3 CMS project. 7 * 8 * It is free software; you can redistribute it and/or modify it under 9 * the terms of the GNU General Public License, either version 2 10 * of the License, or any later version. 11 * 12 * For the full copyright and license information, please read the 13 * LICENSE.txt file that was distributed with this source code. 14 * 15 * The TYPO3 project - inspiring people to share! 16 */ 17 18use TYPO3\CMS\Core\Resource\Driver\DriverInterface; 19use TYPO3\CMS\Core\Resource\File; 20use TYPO3\CMS\Core\Utility\PathUtility; 21 22/** 23 * Decorator for a search result with files, which filters 24 * the result based on given filters. 25 */ 26class DriverFilteredSearchResult implements FileSearchResultInterface 27{ 28 /** 29 * @var FileSearchResultInterface 30 */ 31 private $searchResult; 32 33 /** 34 * @var DriverInterface 35 */ 36 private $driver; 37 38 /** 39 * @var callable[] 40 */ 41 private $filters; 42 43 /** 44 * @var array 45 */ 46 private $result; 47 48 public function __construct(FileSearchResultInterface $searchResult, DriverInterface $driver, array $filters) 49 { 50 $this->searchResult = $searchResult; 51 $this->driver = $driver; 52 $this->filters = $filters; 53 } 54 55 /** 56 * @return int 57 * @see Countable::count() 58 */ 59 public function count(): int 60 { 61 $this->initialize(); 62 63 return count($this->result); 64 } 65 66 /** 67 * @return File 68 * @see Iterator::current() 69 */ 70 public function current(): File 71 { 72 $this->initialize(); 73 74 return current($this->result); 75 } 76 77 /** 78 * @return int 79 * @see Iterator::key() 80 */ 81 public function key(): int 82 { 83 $this->initialize(); 84 85 return key($this->result); 86 } 87 88 /** 89 * @see Iterator::next() 90 */ 91 public function next(): void 92 { 93 $this->initialize(); 94 next($this->result); 95 } 96 97 /** 98 * @see Iterator::rewind() 99 */ 100 public function rewind(): void 101 { 102 $this->initialize(); 103 reset($this->result); 104 } 105 106 /** 107 * @return bool 108 * @see Iterator::valid() 109 */ 110 public function valid(): bool 111 { 112 $this->initialize(); 113 114 return current($this->result) !== false; 115 } 116 117 private function initialize(): void 118 { 119 if ($this->result === null) { 120 $this->result = $this->applyFilters(...iterator_to_array($this->searchResult)); 121 } 122 } 123 124 /** 125 * Filter out identifiers by calling all attached filters 126 * 127 * @param File[] $files 128 * @return array 129 */ 130 private function applyFilters(File ...$files): array 131 { 132 $filteredFiles = []; 133 foreach ($files as $file) { 134 $itemIdentifier = $file->getIdentifier(); 135 $itemName = PathUtility::basename($itemIdentifier); 136 $parentIdentifier = PathUtility::dirname($itemIdentifier); 137 $matches = true; 138 foreach ($this->filters as $filter) { 139 if (!is_callable($filter)) { 140 continue; 141 } 142 $result = $filter($itemName, $itemIdentifier, $parentIdentifier, [], $this->driver); 143 // We have to use -1 as the „don't include“ return value, as call_user_func() will return FALSE 144 // If calling the method succeeded and thus we can't use that as a return value. 145 if ($result === -1) { 146 $matches = false; 147 } 148 if ($result === false) { 149 throw new \RuntimeException( 150 'Could not apply file/folder name filter ' . $filter[0] . '::' . $filter[1], 151 1543617278 152 ); 153 } 154 } 155 if ($matches) { 156 $filteredFiles[] = $file; 157 } 158 } 159 160 return $filteredFiles; 161 } 162} 163