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 |
||
2 | |||
3 | /* |
||
4 | * doctrine-base-repositories (https://github.com/juliangut/doctrine-base-repositories). |
||
5 | * Doctrine2 utility repositories. |
||
6 | * |
||
7 | * @license MIT |
||
8 | * @link https://github.com/juliangut/doctrine-base-repositories |
||
9 | * @author Julián Gutiérrez <[email protected]> |
||
10 | */ |
||
11 | |||
12 | declare(strict_types=1); |
||
13 | |||
14 | namespace Jgut\Doctrine\Repository; |
||
15 | |||
16 | use Doctrine\Common\Util\Inflector; |
||
17 | |||
18 | /** |
||
19 | * Repository trait. |
||
20 | * |
||
21 | * @method mixed find($id, $lockMode = null, $lockVersion = null) |
||
22 | * @method mixed findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) |
||
23 | * @method mixed findOneBy(array $criteria, array $orderBy = null) |
||
24 | * @method mixed findAll() |
||
25 | */ |
||
26 | trait RepositoryTrait |
||
27 | { |
||
28 | /** |
||
29 | * Supported magic methods. |
||
30 | * |
||
31 | * @var array |
||
32 | */ |
||
33 | protected static $supportedMethods = [ |
||
34 | 'findBy', |
||
35 | 'findOneBy', |
||
36 | 'findPaginatedBy', |
||
37 | 'removeBy', |
||
38 | 'removeOneBy', |
||
39 | 'countBy', |
||
40 | ]; |
||
41 | |||
42 | /** |
||
43 | * Methods that support exception throwing on fail. |
||
44 | * |
||
45 | * @var array |
||
46 | */ |
||
47 | protected static $falibleMethods = [ |
||
48 | 'findBy', |
||
49 | 'findOneBy', |
||
50 | 'findPaginatedBy', |
||
51 | ]; |
||
52 | |||
53 | /** |
||
54 | * Auto flush changes. |
||
55 | * |
||
56 | * @var bool |
||
57 | */ |
||
58 | protected $autoFlush = false; |
||
59 | |||
60 | /** |
||
61 | * New object factory. |
||
62 | * |
||
63 | * @var callable |
||
64 | */ |
||
65 | protected $objectFactory; |
||
66 | |||
67 | /** |
||
68 | * Get automatic manager flushing. |
||
69 | * |
||
70 | * @return bool |
||
71 | */ |
||
72 | public function isAutoFlush(): bool |
||
73 | { |
||
74 | return $this->autoFlush; |
||
75 | } |
||
76 | |||
77 | /** |
||
78 | * Set automatic manager flushing. |
||
79 | * |
||
80 | * @param bool $autoFlush |
||
81 | */ |
||
82 | public function setAutoFlush(bool $autoFlush = true) |
||
83 | { |
||
84 | $this->autoFlush = $autoFlush; |
||
85 | } |
||
86 | |||
87 | /** |
||
88 | * Manager flush. |
||
89 | */ |
||
90 | public function flush() |
||
91 | { |
||
92 | $this->getManager()->flush(); |
||
93 | } |
||
94 | |||
95 | /** |
||
96 | * Find elements or throw an exception if none found. |
||
97 | * |
||
98 | * @param array $criteria |
||
99 | * @param array|null $orderBy |
||
100 | * @param int|null $limit |
||
101 | * @param int|null $offset |
||
102 | * |
||
103 | * @throws FindException |
||
104 | * |
||
105 | * @return object[] |
||
106 | */ |
||
107 | public function findByOrFail(array $criteria, array $orderBy = null, int $limit = null, int $offset = null): array |
||
108 | { |
||
109 | $objects = $this->findBy($criteria, $orderBy, $limit, $offset); |
||
0 ignored issues
–
show
|
|||
110 | |||
111 | if (\count($objects) === 0) { |
||
112 | throw new FindException('FindBy did not return any results'); |
||
113 | } |
||
114 | |||
115 | return $objects; |
||
116 | } |
||
117 | |||
118 | /** |
||
119 | * Find elements or throw an exception if none found. |
||
120 | * |
||
121 | * @param array $criteria |
||
122 | * |
||
123 | * @throws FindException |
||
124 | * |
||
125 | * @return object |
||
126 | */ |
||
127 | public function findOneByOrFail(array $criteria) |
||
128 | { |
||
129 | $object = $this->findOneBy($criteria); |
||
130 | |||
131 | if ($object === null) { |
||
132 | throw new FindException('FindOneBy did not return any results'); |
||
133 | } |
||
134 | |||
135 | return $object; |
||
136 | } |
||
137 | |||
138 | /** |
||
139 | * Find one object by a set of criteria or create a new one. |
||
140 | * |
||
141 | * @param array $criteria |
||
142 | * |
||
143 | * @throws \RuntimeException |
||
144 | * |
||
145 | * @return object |
||
146 | */ |
||
147 | public function findOneByOrGetNew(array $criteria) |
||
148 | { |
||
149 | $object = $this->findOneBy($criteria); |
||
150 | |||
151 | if ($object === null) { |
||
152 | $object = $this->getNew(); |
||
153 | } |
||
154 | |||
155 | return $object; |
||
156 | } |
||
157 | |||
158 | /** |
||
159 | * Get a new managed object instance. |
||
160 | * |
||
161 | * @throws \RuntimeException |
||
162 | * |
||
163 | * @return object |
||
164 | */ |
||
165 | public function getNew() |
||
166 | { |
||
167 | $object = \call_user_func($this->getObjectFactory()); |
||
168 | |||
169 | if (!$this->canBeManaged($object)) { |
||
170 | throw new \RuntimeException( |
||
171 | \sprintf( |
||
172 | 'Object factory must return an instance of %s. "%s" returned', |
||
173 | $this->getClassName(), |
||
174 | \is_object($object) ? \get_class($object) : \gettype($object) |
||
175 | ) |
||
176 | ); |
||
177 | } |
||
178 | |||
179 | return $object; |
||
180 | } |
||
181 | |||
182 | /** |
||
183 | * Get object factory. |
||
184 | * |
||
185 | * @return callable |
||
186 | */ |
||
187 | private function getObjectFactory(): callable |
||
188 | { |
||
189 | if ($this->objectFactory === null) { |
||
190 | $className = $this->getClassName(); |
||
191 | |||
192 | $this->objectFactory = function () use ($className) { |
||
193 | return new $className(); |
||
194 | }; |
||
195 | } |
||
196 | |||
197 | return $this->objectFactory; |
||
198 | } |
||
199 | |||
200 | /** |
||
201 | * Set object factory. |
||
202 | * |
||
203 | * @param callable $objectFactory |
||
204 | */ |
||
205 | public function setObjectFactory(callable $objectFactory) |
||
206 | { |
||
207 | $this->objectFactory = $objectFactory; |
||
208 | } |
||
209 | |||
210 | /** |
||
211 | * Add objects. |
||
212 | * |
||
213 | * @param object|iterable $objects |
||
214 | * @param bool $flush |
||
215 | * |
||
216 | * @throws \InvalidArgumentException |
||
217 | */ |
||
218 | public function add($objects, bool $flush = false) |
||
219 | { |
||
220 | $this->runManagerAction('persist', $objects, $flush); |
||
221 | } |
||
222 | |||
223 | /** |
||
224 | * Remove all objects. |
||
225 | * |
||
226 | * @param bool $flush |
||
227 | */ |
||
228 | public function removeAll(bool $flush = false) |
||
229 | { |
||
230 | $this->runManagerAction('remove', $this->findAll(), $flush); |
||
231 | } |
||
232 | |||
233 | /** |
||
234 | * Remove object filtered by a set of criteria. |
||
235 | * |
||
236 | * @param array $criteria |
||
237 | * @param bool $flush |
||
238 | */ |
||
239 | public function removeBy(array $criteria, bool $flush = false) |
||
240 | { |
||
241 | $this->runManagerAction('remove', $this->findBy($criteria), $flush); |
||
242 | } |
||
243 | |||
244 | /** |
||
245 | * Remove first object filtered by a set of criteria. |
||
246 | * |
||
247 | * @param array $criteria |
||
248 | * @param bool $flush |
||
249 | */ |
||
250 | public function removeOneBy(array $criteria, bool $flush = false) |
||
251 | { |
||
252 | $this->runManagerAction('remove', $this->findOneBy($criteria), $flush); |
||
253 | } |
||
254 | |||
255 | /** |
||
256 | * Remove objects. |
||
257 | * |
||
258 | * @param object|iterable|string|int $objects |
||
259 | * @param bool $flush |
||
260 | * |
||
261 | * @throws \InvalidArgumentException |
||
262 | */ |
||
263 | public function remove($objects, bool $flush = false) |
||
264 | { |
||
265 | if (!\is_object($objects) && !is_iterable($objects)) { |
||
266 | $objects = $this->find($objects); |
||
267 | } |
||
268 | |||
269 | $this->runManagerAction('remove', $objects, $flush); |
||
270 | } |
||
271 | |||
272 | /** |
||
273 | * Refresh objects. |
||
274 | * |
||
275 | * @param object|iterable $objects |
||
276 | * |
||
277 | * @throws \InvalidArgumentException |
||
278 | */ |
||
279 | public function refresh($objects) |
||
280 | { |
||
281 | $backupAutoFlush = $this->autoFlush; |
||
282 | |||
283 | $this->autoFlush = false; |
||
284 | $this->runManagerAction('refresh', $objects, false); |
||
285 | |||
286 | $this->autoFlush = $backupAutoFlush; |
||
287 | } |
||
288 | |||
289 | /** |
||
290 | * Detach objects. |
||
291 | * |
||
292 | * @param object|iterable $objects |
||
293 | * |
||
294 | * @throws \InvalidArgumentException |
||
295 | */ |
||
296 | public function detach($objects) |
||
297 | { |
||
298 | $backupAutoFlush = $this->autoFlush; |
||
299 | |||
300 | $this->autoFlush = false; |
||
301 | $this->runManagerAction('detach', $objects, false); |
||
302 | |||
303 | $this->autoFlush = $backupAutoFlush; |
||
304 | } |
||
305 | |||
306 | /** |
||
307 | * Get all objects count. |
||
308 | * |
||
309 | * @return int |
||
310 | */ |
||
311 | public function countAll(): int |
||
312 | { |
||
313 | return $this->countBy([]); |
||
314 | } |
||
315 | |||
316 | /** |
||
317 | * Get object count filtered by a set of criteria. |
||
318 | * |
||
319 | * @param mixed $criteria |
||
320 | * |
||
321 | * @return int |
||
322 | */ |
||
323 | abstract public function countBy($criteria): int; |
||
324 | |||
325 | /** |
||
326 | * Adds support for magic methods. |
||
327 | * |
||
328 | * @param string $method |
||
329 | * @param array $arguments |
||
330 | * |
||
331 | * @throws \BadMethodCallException |
||
332 | * |
||
333 | * @return mixed |
||
334 | */ |
||
335 | public function __call($method, $arguments) |
||
336 | { |
||
337 | if (\count($arguments) === 0) { |
||
338 | throw new \BadMethodCallException(\sprintf( |
||
339 | 'You need to call %s::%s with a parameter', |
||
340 | $this->getClassName(), |
||
341 | $method |
||
342 | )); |
||
343 | } |
||
344 | |||
345 | $baseMethod = $this->getSupportedMethod($method); |
||
346 | |||
347 | if (\in_array($baseMethod, static::$falibleMethods, true) && \preg_match('/OrFail$/', $method)) { |
||
348 | $field = \substr($method, \strlen($baseMethod), -6); |
||
349 | $method = $baseMethod . 'OrFail'; |
||
350 | } elseif ($baseMethod === 'findOneBy' && \preg_match('/OrGetNew$/', $method)) { |
||
351 | $field = \substr($method, \strlen($baseMethod), -8); |
||
352 | $method = 'findOneByOrGetNew'; |
||
353 | } else { |
||
354 | $field = \substr($method, \strlen($baseMethod)); |
||
355 | $method = $baseMethod; |
||
356 | } |
||
357 | |||
358 | return $this->callSupportedMethod($method, Inflector::camelize($field), $arguments); |
||
359 | } |
||
360 | |||
361 | /** |
||
362 | * Get supported magic method. |
||
363 | * |
||
364 | * @param string $method |
||
365 | * |
||
366 | * @throws \BadMethodCallException |
||
367 | * |
||
368 | * @return string |
||
369 | */ |
||
370 | private function getSupportedMethod(string $method): string |
||
371 | { |
||
372 | foreach (static::$supportedMethods as $supportedMethod) { |
||
373 | if (\strpos($method, $supportedMethod) === 0) { |
||
374 | return $supportedMethod; |
||
375 | } |
||
376 | } |
||
377 | |||
378 | throw new \BadMethodCallException(\sprintf( |
||
379 | 'Undefined method "%s". Method call must start with one of "%s"!', |
||
380 | $method, |
||
381 | \implode('", "', static::$supportedMethods) |
||
382 | )); |
||
383 | } |
||
384 | |||
385 | /** |
||
386 | * Internal method call. |
||
387 | * |
||
388 | * @param string $method |
||
389 | * @param string $fieldName |
||
390 | * @param array $arguments |
||
391 | * |
||
392 | * @throws \BadMethodCallException |
||
393 | * |
||
394 | * @return mixed |
||
395 | */ |
||
396 | protected function callSupportedMethod(string $method, string $fieldName, array $arguments) |
||
397 | { |
||
398 | $classMetadata = $this->getClassMetadata(); |
||
399 | |||
400 | if (!$classMetadata->hasField($fieldName) && !$classMetadata->hasAssociation($fieldName)) { |
||
401 | throw new \BadMethodCallException(\sprintf( |
||
402 | 'Invalid call to %s::%s. Field "%s" does not exist', |
||
403 | $this->getClassName(), |
||
404 | $method, |
||
405 | $fieldName |
||
406 | )); |
||
407 | } |
||
408 | |||
409 | // @codeCoverageIgnoreStart |
||
410 | $parameters = \array_merge( |
||
411 | [$fieldName => $arguments[0]], |
||
412 | \array_slice($arguments, 1) |
||
413 | ); |
||
414 | |||
415 | return \call_user_func_array([$this, $method], $parameters); |
||
416 | // @codeCoverageIgnoreEnd |
||
417 | } |
||
418 | |||
419 | /** |
||
420 | * Run manager action. |
||
421 | * |
||
422 | * @param string $action |
||
423 | * @param object|iterable $objects |
||
424 | * @param bool $flush |
||
425 | * |
||
426 | * @throws \InvalidArgumentException |
||
427 | */ |
||
428 | protected function runManagerAction(string $action, $objects, bool $flush) |
||
429 | { |
||
430 | $manager = $this->getManager(); |
||
431 | |||
432 | if (!is_iterable($objects)) { |
||
433 | $objects = \array_filter([$objects]); |
||
434 | } |
||
435 | |||
436 | foreach ($objects as $object) { |
||
437 | if (!$this->canBeManaged($object)) { |
||
438 | throw new \InvalidArgumentException( |
||
439 | \sprintf( |
||
440 | 'Managed object must be a %s. "%s" given', |
||
441 | $this->getClassName(), |
||
442 | \is_object($object) ? \get_class($object) : \gettype($object) |
||
443 | ) |
||
444 | ); |
||
445 | } |
||
446 | |||
447 | $manager->$action($object); |
||
448 | } |
||
449 | |||
450 | // @codeCoverageIgnoreStart |
||
451 | if ($objects instanceof \Traversable) { |
||
452 | $objects = \iterator_to_array($objects); |
||
453 | } |
||
454 | // @codeCoverageIgnoreEnd |
||
455 | |||
456 | $this->flushObjects($objects, $flush); |
||
0 ignored issues
–
show
It seems like
$objects defined by parameter $objects on line 428 can also be of type object ; however, Jgut\Doctrine\Repository...ryTrait::flushObjects() does only seem to accept array , maybe add an additional type check?
This check looks at variables that have been passed in as parameters and are passed out again to other methods. If the outgoing method call has stricter type requirements than the method itself, an issue is raised. An additional type check may prevent trouble. ![]() |
|||
457 | } |
||
458 | |||
459 | /** |
||
460 | * Flush managed objects. |
||
461 | * |
||
462 | * @param array $objects |
||
463 | * @param bool $flush |
||
464 | */ |
||
465 | protected function flushObjects(array $objects, bool $flush) |
||
466 | { |
||
467 | if ($flush || $this->autoFlush) { |
||
468 | $this->getManager()->flush($objects); |
||
469 | } |
||
470 | } |
||
471 | |||
472 | /** |
||
473 | * Check if the object is of the proper type. |
||
474 | * |
||
475 | * @param object $object |
||
476 | * |
||
477 | * @return bool |
||
478 | */ |
||
479 | protected function canBeManaged($object): bool |
||
480 | { |
||
481 | $managedClass = $this->getClassName(); |
||
482 | |||
483 | return $object instanceof $managedClass; |
||
484 | } |
||
485 | |||
486 | /** |
||
487 | * Returns the fully qualified class name of the objects managed by the repository. |
||
488 | * |
||
489 | * @return string |
||
490 | */ |
||
491 | abstract public function getClassName(): string; |
||
492 | |||
493 | /** |
||
494 | * Get object manager. |
||
495 | * |
||
496 | * @return \Doctrine\Common\Persistence\ObjectManager |
||
497 | */ |
||
498 | abstract protected function getManager(); |
||
499 | |||
500 | /** |
||
501 | * Get class metadata. |
||
502 | * |
||
503 | * @return \Doctrine\Common\Persistence\Mapping\ClassMetadata |
||
504 | */ |
||
505 | abstract protected function getClassMetadata(); |
||
506 | } |
||
507 |
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.