This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php declare(strict_types=1); |
||
2 | //////////////////////////////////////////////////////////////////////////////// |
||
3 | // __________ __ ________ __________ |
||
4 | // \______ \ |__ ______ / _____/ ____ _____ ______\______ \ _______ ___ |
||
5 | // | ___/ | \\____ \/ \ ____/ __ \\__ \\_ __ \ | _// _ \ \/ / |
||
6 | // | | | Y \ |_> > \_\ \ ___/ / __ \| | \/ | ( <_> > < |
||
7 | // |____| |___| / __/ \______ /\___ >____ /__| |______ /\____/__/\_ \ |
||
8 | // \/|__| \/ \/ \/ \/ \/ |
||
9 | // ----------------------------------------------------------------------------- |
||
10 | // Designed and Developed by Brad Jones <brad @="bjc.id.au" /> |
||
11 | // ----------------------------------------------------------------------------- |
||
12 | //////////////////////////////////////////////////////////////////////////////// |
||
13 | |||
14 | namespace Gears; |
||
15 | |||
16 | use Closure; |
||
17 | use Exception; |
||
18 | use Traversable; |
||
19 | use ArrayIterator; |
||
20 | use ReflectionClass; |
||
21 | use ReflectionException; |
||
22 | use Gears\String\Str; |
||
23 | use Composer\Autoload\ClassLoader; |
||
24 | use Symfony\Component\Finder\Finder; |
||
25 | use Symfony\Component\Finder\SplFileInfo; |
||
26 | |||
27 | class ClassFinder implements IClassFinder |
||
28 | { |
||
29 | /** |
||
30 | * The composer class loader as returned from `vendor/autoload.php`. |
||
31 | * |
||
32 | * @var ClassLoader |
||
33 | */ |
||
34 | protected $composer; |
||
35 | |||
36 | /** |
||
37 | * This will be filled with fully qualified class names as they are found |
||
38 | * by searching through the various class maps provided by composer. |
||
39 | * |
||
40 | * @var array|string[] |
||
41 | */ |
||
42 | protected $foundClasses = []; |
||
43 | |||
44 | /** |
||
45 | * The namespace to filter by will be stored here. |
||
46 | * |
||
47 | * > NOTE: This must be set, otherwise we could have a |
||
48 | * very large data set to search through. |
||
49 | * |
||
50 | * @var string |
||
51 | */ |
||
52 | protected $namespace; |
||
53 | |||
54 | /** |
||
55 | * The interface name to filter by will be stored here. |
||
56 | * |
||
57 | * @var string |
||
58 | */ |
||
59 | protected $implements; |
||
60 | |||
61 | /** |
||
62 | * The parent class to filter by will be stored here. |
||
63 | * |
||
64 | * @var string |
||
65 | */ |
||
66 | protected $extends; |
||
67 | |||
68 | /** |
||
69 | * An optional custom filter method can be set. |
||
70 | * Otherwise we will use the `defaultFilter` method in this class. |
||
71 | * |
||
72 | * @var Closure |
||
73 | */ |
||
74 | protected $filter; |
||
75 | |||
76 | /** |
||
77 | * Constructor. |
||
78 | * |
||
79 | * @param ClassLoader $composer We rely on the information provided by the |
||
80 | * composer class maps in order to find classes |
||
81 | * for you. |
||
82 | */ |
||
83 | public function __construct(ClassLoader $composer) |
||
84 | { |
||
85 | $this->composer = $composer; |
||
86 | } |
||
87 | |||
88 | public function namespace(string $namespace): IClassFinder |
||
89 | { |
||
90 | $this->namespace = $namespace; return $this; |
||
91 | } |
||
92 | |||
93 | public function implements(string $interface): IClassFinder |
||
94 | { |
||
95 | $this->implements = $interface; return $this; |
||
96 | } |
||
97 | |||
98 | public function extends(string $parent): IClassFinder |
||
99 | { |
||
100 | $this->extends = $parent; return $this; |
||
101 | } |
||
102 | |||
103 | public function filterBy(Closure $filter): IClassFinder |
||
104 | { |
||
105 | if ($this->implements !== null || $this->extends != null) |
||
106 | { |
||
107 | throw new Exception |
||
108 | ( |
||
109 | 'Can not set a custom filter and filter '. |
||
110 | 'by `implements` or `extends`!' |
||
111 | ); |
||
112 | } |
||
113 | |||
114 | $this->filter = $filter; return $this; |
||
115 | } |
||
116 | |||
117 | public function search(): array |
||
118 | { |
||
119 | $this->foundClasses = []; |
||
120 | |||
121 | if ($this->namespace === null) |
||
122 | { |
||
123 | throw new Exception('Namespace must be set!'); |
||
124 | } |
||
125 | |||
126 | $this->searchClassMap(); |
||
127 | $this->searchPsrMaps(); |
||
128 | $this->runFilter(); |
||
129 | |||
130 | $this->namespace = null; |
||
131 | $this->implements = null; |
||
132 | $this->extends = null; |
||
133 | |||
134 | return $this->foundClasses; |
||
135 | } |
||
136 | |||
137 | public function getIterator(): Traversable |
||
138 | { |
||
139 | return new ArrayIterator($this->search()); |
||
140 | } |
||
141 | |||
142 | public function count(): int |
||
143 | { |
||
144 | return iterator_count($this->getIterator()); |
||
145 | } |
||
146 | |||
147 | /** |
||
148 | * Searches the composer class map. |
||
149 | * |
||
150 | * Results are added to the `$foundClasses` array. |
||
151 | * |
||
152 | * @return void |
||
153 | */ |
||
154 | protected function searchClassMap() |
||
155 | { |
||
156 | foreach ($this->composer->getClassMap() as $fqcn => $file) |
||
157 | { |
||
158 | View Code Duplication | if (Str::s($fqcn)->is($this->namespace.'*')) |
|
0 ignored issues
–
show
|
|||
159 | { |
||
160 | $this->foundClasses[realpath($file)] = $fqcn; |
||
161 | } |
||
162 | } |
||
163 | } |
||
164 | |||
165 | /** |
||
166 | * Searches the composer PSR-x class maps. |
||
167 | * |
||
168 | * Results are added to the `$foundClasses` array. |
||
169 | * |
||
170 | * @return void |
||
171 | */ |
||
172 | protected function searchPsrMaps() |
||
173 | { |
||
174 | $prefixes = array_merge |
||
175 | ( |
||
176 | $this->composer->getPrefixes(), |
||
177 | $this->composer->getPrefixesPsr4() |
||
178 | ); |
||
179 | |||
180 | $trimmedNs = Str::s($this->namespace)->trimRight('\\'); |
||
181 | |||
182 | $nsSegments = $trimmedNs->split('\\'); |
||
183 | |||
184 | foreach ($prefixes as $ns => $dirs) |
||
185 | { |
||
186 | $foundSegments = Str::s($ns)->trimRight('\\') |
||
187 | ->longestCommonPrefix($trimmedNs)->split('\\'); |
||
188 | |||
189 | foreach ($foundSegments as $key => $segment) |
||
190 | { |
||
191 | if ((string) $nsSegments[$key] !== (string) $segment) |
||
192 | { |
||
193 | continue 2; |
||
194 | } |
||
195 | } |
||
196 | |||
197 | foreach ($dirs as $dir) |
||
198 | { |
||
199 | foreach ((new Finder)->in($dir)->files()->name('*.php') as $file) |
||
200 | { |
||
201 | if ($file instanceof SplFileInfo) |
||
202 | { |
||
203 | $fqcn = (string)Str::s($file->getRelativePathname()) |
||
204 | ->trimRight('.php') |
||
205 | ->replace('/', '\\') |
||
206 | ->ensureLeft($ns); |
||
207 | |||
208 | View Code Duplication | if (Str::s($fqcn)->is($this->namespace.'*')) |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
209 | { |
||
210 | $this->foundClasses[$file->getRealPath()] = $fqcn; |
||
211 | } |
||
212 | } |
||
213 | } |
||
214 | } |
||
215 | } |
||
216 | } |
||
217 | |||
218 | /** |
||
219 | * Runs a filter over the array of `$foundCLasses`. |
||
220 | * |
||
221 | * Also ensures the classes are real by creating a ReflectionClass instance. |
||
222 | * By default we use the `defaultFilter` method. |
||
223 | * |
||
224 | * @return void |
||
225 | */ |
||
226 | protected function runFilter() |
||
227 | { |
||
228 | foreach ($this->foundClasses as $file => $fqcn) |
||
229 | { |
||
230 | try |
||
231 | { |
||
232 | $rClass = new ReflectionClass($fqcn); |
||
233 | } |
||
234 | catch (ReflectionException $e) |
||
235 | { |
||
236 | $result = false; |
||
237 | } |
||
238 | |||
239 | if ($this->filter === null) |
||
240 | { |
||
241 | $result = $this->defaultFilter($rClass); |
||
242 | } |
||
243 | else |
||
244 | { |
||
245 | $result = call_user_func($this->filter, $rClass); |
||
246 | } |
||
247 | |||
248 | if ($result === false) |
||
249 | { |
||
250 | unset($this->foundClasses[$file]); |
||
251 | } |
||
252 | } |
||
253 | } |
||
254 | |||
255 | /** |
||
256 | * The default filter run by `runFilter()`. |
||
257 | * |
||
258 | * Further filters by interface or parent class and also filters |
||
259 | * out actual Interfaces, Abstract Classes and Traits. |
||
260 | * |
||
261 | * @param ReflectionClass $rClass |
||
262 | * @return bool |
||
263 | */ |
||
264 | protected function defaultFilter(ReflectionClass $rClass) |
||
265 | { |
||
266 | if ($this->implements !== null) |
||
267 | { |
||
268 | return $rClass->implementsInterface($this->implements) && !$rClass->isInterface(); |
||
269 | } |
||
270 | |||
271 | if ($this->extends !== null) |
||
272 | { |
||
273 | return $rClass->isSubclassOf($this->extends) && !$rClass->isAbstract(); |
||
274 | } |
||
275 | |||
276 | return (!$rClass->isInterface() && !$rClass->isAbstract() && !$rClass->isTrait()); |
||
277 | } |
||
278 | } |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.