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