Completed
Push — master ( 30491c...98adac )
by Lukas Kahwe
05:05
created

RestActionReader::setNamePrefix()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
3
/*
4
 * This file is part of the FOSRestBundle package.
5
 *
6
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.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
12
namespace FOS\RestBundle\Routing\Loader\Reader;
13
14
use Doctrine\Common\Annotations\Reader;
15
use FOS\RestBundle\Controller\Annotations\Route as RouteAnnotation;
16
use FOS\RestBundle\Inflector\InflectorInterface;
17
use FOS\RestBundle\Request\ParamReaderInterface;
18
use FOS\RestBundle\Routing\RestRouteCollection;
19
use Symfony\Component\Routing\Route;
20
21
/**
22
 * REST controller actions reader.
23
 *
24
 * @author Konstantin Kudryashov <[email protected]>
25
 */
26
class RestActionReader
27
{
28
    const COLLECTION_ROUTE_PREFIX = 'c';
29
30
    /**
31
     * @var Reader
32
     */
33
    private $annotationReader;
34
35
    /**
36
     * @var ParamReaderInterface
37
     */
38
    private $paramReader;
39
40
    /**
41
     * @var InflectorInterface
42
     */
43
    private $inflector;
44
45
    /**
46
     * @var array
47
     */
48
    private $formats;
49
50
    /**
51
     * @var bool
52
     */
53
    private $includeFormat;
54
55
    /**
56
     * @var string|null
57
     */
58
    private $routePrefix;
59
60
    /**
61
     * @var string|null
62
     */
63
    private $namePrefix;
64
65
    /**
66
     * @var array|string|null
67
     */
68
    private $versions;
69
70
    /**
71
     * @var bool|null
72
     */
73
    private $pluralize;
74
75
    /**
76
     * @var array
77
     */
78
    private $parents = [];
79
80
    /**
81
     * @var array
82
     */
83
    private $availableHTTPMethods = [
84
        'get',
85
        'post',
86
        'put',
87
        'patch',
88
        'delete',
89
        'link',
90
        'unlink',
91
        'head',
92
        'options',
93
        'mkcol',
94
        'propfind',
95
        'proppatch',
96
        'move',
97
        'copy',
98
        'lock',
99
        'unlock',
100
    ];
101
102
    /**
103
     * @var array
104
     */
105
    private $availableConventionalActions = ['new', 'edit', 'remove'];
106
107
    /**
108
     * Initializes controller reader.
109
     *
110
     * @param Reader               $annotationReader
111
     * @param ParamReaderInterface $paramReader
112
     * @param InflectorInterface   $inflector
113
     * @param bool                 $includeFormat
114
     * @param array                $formats
115
     */
116 40
    public function __construct(Reader $annotationReader, ParamReaderInterface $paramReader, InflectorInterface $inflector, $includeFormat, array $formats = [])
117
    {
118 40
        $this->annotationReader = $annotationReader;
119 40
        $this->paramReader = $paramReader;
120 40
        $this->inflector = $inflector;
121 40
        $this->includeFormat = $includeFormat;
122 40
        $this->formats = $formats;
123 40
    }
124
125
    /**
126
     * Sets routes prefix.
127
     *
128
     * @param string $prefix Routes prefix
129
     */
130 30
    public function setRoutePrefix($prefix = null)
131
    {
132 30
        $this->routePrefix = $prefix;
133 30
    }
134
135
    /**
136
     * Returns route prefix.
137
     *
138
     * @return string
139
     */
140 30
    public function getRoutePrefix()
141
    {
142 30
        return $this->routePrefix;
143
    }
144
145
    /**
146
     * Sets route names prefix.
147
     *
148
     * @param string $prefix Route names prefix
149
     */
150 30
    public function setNamePrefix($prefix = null)
151
    {
152 30
        $this->namePrefix = $prefix;
153 30
    }
154
155
    /**
156
     * Returns name prefix.
157
     *
158
     * @return string
159
     */
160
    public function getNamePrefix()
161
    {
162
        return $this->namePrefix;
163
    }
164
165
    /**
166
     * Sets route names versions.
167
     *
168
     * @param array|string|null $versions Route names versions
169
     */
170 30
    public function setVersions($versions = null)
171
    {
172 30
        $this->versions = (array) $versions;
173 30
    }
174
175
    /**
176
     * Returns versions.
177
     *
178
     * @return array|null
179
     */
180
    public function getVersions()
181
    {
182
        return $this->versions;
183
    }
184
185
    /**
186
     * Sets pluralize.
187
     *
188
     * @param bool|null $pluralize Specify if resource name must be pluralized
189
     */
190 30
    public function setPluralize($pluralize)
191
    {
192 30
        $this->pluralize = $pluralize;
193 30
    }
194
195
    /**
196
     * Returns pluralize.
197
     *
198
     * @return bool|null
199
     */
200
    public function getPluralize()
201
    {
202
        return $this->pluralize;
203
    }
204
205
    /**
206
     * Set parent routes.
207
     *
208
     * @param array $parents Array of parent resources names
209
     */
210 30
    public function setParents(array $parents)
211
    {
212 30
        $this->parents = $parents;
213 30
    }
214
215
    /**
216
     * Returns parents.
217
     *
218
     * @return array
219
     */
220
    public function getParents()
221
    {
222
        return $this->parents;
223
    }
224
225
    /**
226
     * Reads action route.
227
     *
228
     * @param RestRouteCollection $collection
229
     * @param \ReflectionMethod   $method
230
     * @param string[]            $resource
231
     *
232
     * @throws \InvalidArgumentException
233
     *
234
     * @return Route
235
     */
236 30
    public function read(RestRouteCollection $collection, \ReflectionMethod $method, $resource)
237
    {
238
        // check that every route parent has non-empty singular name
239 30
        foreach ($this->parents as $parent) {
240 5
            if (empty($parent) || '/' === substr($parent, -1)) {
241
                throw new \InvalidArgumentException(
242
                    "Every parent controller must have `get{SINGULAR}Action(\$id)` method\n".
243
                    'where {SINGULAR} is a singular form of associated object'
244
                );
245
            }
246 30
        }
247
248
        // if method is not readable - skip
249 30
        if (!$this->isMethodReadable($method)) {
250 14
            return;
251
        }
252
253
        // if we can't get http-method and resources from method name - skip
254 30
        $httpMethodAndResources = $this->getHttpMethodAndResourcesFromMethod($method, $resource);
255 30
        if (!$httpMethodAndResources) {
256 30
            return;
257
        }
258
259 30
        list($httpMethod, $resources, $isCollection, $isInflectable) = $httpMethodAndResources;
260 30
        $arguments = $this->getMethodArguments($method);
261
262
        // if we have only 1 resource & 1 argument passed, then it's object call, so
263
        // we can set collection singular name
264 30
        if (1 === count($resources) && 1 === count($arguments) - count($this->parents)) {
265 18
            $collection->setSingularName($resources[0]);
266 18
        }
267
268
        // if we have parents passed - merge them with own resource names
269 30
        if (count($this->parents)) {
270 5
            $resources = array_merge($this->parents, $resources);
271 5
        }
272
273 30
        if (empty($resources)) {
274
            $resources[] = null;
275
        }
276
277 30
        $routeName = $httpMethod.$this->generateRouteName($resources);
278 30
        $urlParts = $this->generateUrlParts($resources, $arguments, $httpMethod);
279
280
        // if passed method is not valid HTTP method then it's either
281
        // a hypertext driver, a custom object (PUT) or collection (GET)
282
        // method
283 30
        if (!in_array($httpMethod, $this->availableHTTPMethods)) {
284 17
            $urlParts[] = $httpMethod;
285 17
            $httpMethod = $this->getCustomHttpMethod($httpMethod, $resources, $arguments);
286 17
        }
287
288
        // generated parameters
289 30
        $routeName = strtolower($routeName);
290 30
        $path = implode('/', $urlParts);
291 30
        $defaults = ['_controller' => $method->getName()];
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
292 30
        $requirements = [];
293 30
        $options = [];
294 30
        $host = '';
295 30
        $condition = null;
296
297 30
        $annotations = $this->readRouteAnnotation($method);
298 30
        if (!empty($annotations)) {
299 8
            foreach ($annotations as $annotation) {
300 8
                $path = implode('/', $urlParts);
301 8
                $defaults = ['_controller' => $method->getName()];
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
302 8
                $requirements = [];
303 8
                $options = [];
304 8
                $methods = explode('|', $httpMethod);
305
306 8
                $annoRequirements = $annotation->getRequirements();
307 8
                $annoMethods = $annotation->getMethods();
308
309 8
                if (!empty($annoMethods)) {
310 8
                    $methods = $annoMethods;
311 8
                }
312
313 8
                $path = $annotation->getPath() !== null ? $this->routePrefix.$annotation->getPath() : $path;
314 8
                $requirements = array_merge($requirements, $annoRequirements);
315 8
                $options = array_merge($options, $annotation->getOptions());
316 8
                $defaults = array_merge($defaults, $annotation->getDefaults());
317 8
                $host = $annotation->getHost();
318 8
                $schemes = $annotation->getSchemes();
319 8
                $condition = $this->getCondition($method, $annotation);
320
321 8
                $this->includeFormatIfNeeded($path, $requirements);
322
323
                // add route to collection
324 8
                $route = new Route(
325 8
                    $path, $defaults, $requirements, $options, $host, $schemes, $methods, $condition
326 8
                );
327 8
                $this->addRoute($collection, $routeName, $route, $isCollection, $isInflectable, $annotation);
328 8
            }
329 8
        } else {
330 29
            $this->includeFormatIfNeeded($path, $requirements);
331
332 29
            $methods = explode('|', strtoupper($httpMethod));
333
334
            // add route to collection
335 29
            $route = new Route(
336 29
                $path, $defaults, $requirements, $options, $host, [], $methods, $condition
337 29
            );
338 29
            $this->addRoute($collection, $routeName, $route, $isCollection, $isInflectable);
339
        }
340 30
    }
341
342
    /**
343
     * Determine the Route condition by combining Route annotations with Version annotation.
344
     *
345
     * @param \ReflectionMethod $method
346
     * @param RouteAnnotation   $annotation
347
     *
348
     * @return string
349
     */
350 8
    private function getCondition(\ReflectionMethod $method, RouteAnnotation $annotation)
0 ignored issues
show
Unused Code introduced by
The parameter $method is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
351
    {
352 8
        $condition = $annotation->getCondition();
353
354 8
        if (!empty($this->versions)) {
355 1
            $versionCondition = "request.attributes.get('version') == (";
356 1
            $first = true;
357 1
            foreach ($this->versions as $version) {
0 ignored issues
show
Bug introduced by
The expression $this->versions of type array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
358 1
                if (!$first) {
359 1
                    $versionCondition .= ' or ';
360 1
                }
361 1
                $versionCondition .= '\''.$version.'\'';
362 1
                $first = false;
363 1
            }
364 1
            $versionCondition .= ')';
365 1
            $condition = $condition ? '('.$condition.') and '.$versionCondition : $versionCondition;
366 1
        }
367
368 8
        return $condition;
369
    }
370
371
    /**
372
     * Include the format in the path and requirements if its enabled.
373
     *
374
     * @param string $path
375
     * @param array  $requirements
376
     */
377 30
    private function includeFormatIfNeeded(&$path, &$requirements)
378
    {
379 30 View Code Duplication
        if ($this->includeFormat === true) {
0 ignored issues
show
Duplication introduced by
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.

Loading history...
380 30
            $path .= '.{_format}';
381
382 30
            if (!isset($requirements['_format']) && !empty($this->formats)) {
383 7
                $requirements['_format'] = implode('|', array_keys($this->formats));
384 7
            }
385 30
        }
386 30
    }
387
388
    /**
389
     * Checks whether provided method is readable.
390
     *
391
     * @param \ReflectionMethod $method
392
     *
393
     * @return bool
394
     */
395 30
    private function isMethodReadable(\ReflectionMethod $method)
396
    {
397
        // if method starts with _ - skip
398 30
        if ('_' === substr($method->getName(), 0, 1)) {
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
399 10
            return false;
400
        }
401
402 30
        $hasNoRouteMethod = (bool) $this->readMethodAnnotation($method, 'NoRoute');
403 30
        $hasNoRouteClass = (bool) $this->readClassAnnotation($method->getDeclaringClass(), 'NoRoute');
404
405 30
        $hasNoRoute = $hasNoRouteMethod || $hasNoRouteClass;
406
        // since NoRoute extends Route we need to exclude all the method NoRoute annotations
407 30
        $hasRoute = (bool) $this->readMethodAnnotation($method, 'Route') && !$hasNoRouteMethod;
408
409
        // if method has NoRoute annotation and does not have Route annotation - skip
410 30
        if ($hasNoRoute && !$hasRoute) {
411 4
            return false;
412
        }
413
414 30
        return true;
415
    }
416
417
    /**
418
     * Returns HTTP method and resources list from method signature.
419
     *
420
     * @param \ReflectionMethod $method
421
     * @param string[]          $resource
422
     *
423
     * @return bool|array
424
     */
425 30
    private function getHttpMethodAndResourcesFromMethod(\ReflectionMethod $method, $resource)
426
    {
427
        // if method doesn't match regex - skip
428 30
        if (!preg_match('/([a-z][_a-z0-9]+)(.*)Action/', $method->getName(), $matches)) {
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
429 30
            return false;
430
        }
431
432 30
        $httpMethod = strtolower($matches[1]);
433 30
        $resources = preg_split(
434 30
            '/([A-Z][^A-Z]*)/', $matches[2], -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE
435 30
        );
436 30
        $isCollection = false;
437 30
        $isInflectable = true;
438
439 30
        if (0 === strpos($httpMethod, self::COLLECTION_ROUTE_PREFIX)
440 30
            && in_array(substr($httpMethod, 1), $this->availableHTTPMethods)
441 30
        ) {
442 11
            $isCollection = true;
443 11
            $httpMethod = substr($httpMethod, 1);
444 30
        } elseif ('options' === $httpMethod) {
445 14
            $isCollection = true;
446 14
        }
447
448 30
        if ($isCollection && !empty($resource)) {
449 11
            $resourcePluralized = $this->generateResourceName(end($resource));
450 11
            $isInflectable = ($resourcePluralized != $resource[count($resource) - 1]);
451 11
            $resource[count($resource) - 1] = $resourcePluralized;
452 11
        }
453
454 30
        $resources = array_merge($resource, $resources);
455
456 30
        return [$httpMethod, $resources, $isCollection, $isInflectable];
457
    }
458
459
    /**
460
     * Returns readable arguments from method.
461
     *
462
     * @param \ReflectionMethod $method
463
     *
464
     * @return \ReflectionParameter[]
465
     */
466 30
    private function getMethodArguments(\ReflectionMethod $method)
467
    {
468
        // ignore all query params
469 30
        $params = $this->paramReader->getParamsFromMethod($method);
470
471
        // ignore several type hinted arguments
472
        $ignoreClasses = [
473 30
            \Symfony\Component\HttpFoundation\Request::class,
474 30
            \FOS\RestBundle\Request\ParamFetcherInterface::class,
475 30
            \Symfony\Component\Validator\ConstraintViolationListInterface::class,
476 30
            \Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter::class,
477 30
        ];
478
479 30
        $arguments = [];
480 30
        foreach ($method->getParameters() as $argument) {
481 27
            if (isset($params[$argument->getName()])) {
0 ignored issues
show
Bug introduced by
Consider using $argument->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
482
                continue;
483
            }
484
485 27
            $argumentClass = $argument->getClass();
486 27
            if ($argumentClass) {
487 17
                $className = $argumentClass->getName();
0 ignored issues
show
Bug introduced by
Consider using $argumentClass->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
488 17
                foreach ($ignoreClasses as $class) {
489 17
                    if ($className === $class || is_subclass_of($className, $class)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if $class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
490 16
                        continue 2;
491
                    }
492 2
                }
493 1
            }
494
495 21
            $arguments[] = $argument;
496 30
        }
497
498 30
        return $arguments;
499
    }
500
501
    /**
502
     * Generates final resource name.
503
     *
504
     * @param string|bool $resource
505
     *
506
     * @return string
507
     */
508 30
    private function generateResourceName($resource)
509
    {
510 30
        if (false === $this->pluralize) {
511 1
            return $resource;
512
        }
513
514 29
        return $this->inflector->pluralize($resource);
0 ignored issues
show
Bug introduced by
It seems like $resource defined by parameter $resource on line 508 can also be of type boolean; however, FOS\RestBundle\Inflector...rInterface::pluralize() does only seem to accept string, 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.

Loading history...
515
    }
516
517
    /**
518
     * Generates route name from resources list.
519
     *
520
     * @param string[] $resources
521
     *
522
     * @return string
523
     */
524 30
    private function generateRouteName(array $resources)
525
    {
526 30
        $routeName = '';
527 30
        foreach ($resources as $resource) {
528 30
            if (null !== $resource) {
529 30
                $routeName .= '_'.basename($resource);
530 30
            }
531 30
        }
532
533 30
        return $routeName;
534
    }
535
536
    /**
537
     * Generates URL parts for route from resources list.
538
     *
539
     * @param string[]               $resources
540
     * @param \ReflectionParameter[] $arguments
541
     * @param string                 $httpMethod
542
     *
543
     * @return array
544
     */
545 30
    private function generateUrlParts(array $resources, array $arguments, $httpMethod)
546
    {
547 30
        $urlParts = [];
548 30
        foreach ($resources as $i => $resource) {
549
            // if we already added all parent routes paths to URL & we have
550
            // prefix - add it
551 30
            if (!empty($this->routePrefix) && $i === count($this->parents)) {
552 3
                $urlParts[] = $this->routePrefix;
553 3
            }
554
555
            // if we have argument for current resource, then it's object.
556
            // otherwise - it's collection
557 30
            if (isset($arguments[$i])) {
558 21
                if (null !== $resource) {
559 21
                    $urlParts[] =
560 21
                        strtolower($this->generateResourceName($resource))
561 21
                        .'/{'.$arguments[$i]->getName().'}';
0 ignored issues
show
Bug introduced by
Consider using $arguments[$i]->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
562 21
                } else {
563
                    $urlParts[] = '{'.$arguments[$i]->getName().'}';
0 ignored issues
show
Bug introduced by
Consider using $arguments[$i]->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
564
                }
565 30
            } elseif (null !== $resource) {
566 30
                if ((0 === count($arguments) && !in_array($httpMethod, $this->availableHTTPMethods))
567
                    || 'new' === $httpMethod
568 29
                    || 'post' === $httpMethod
569 30
                ) {
570 23
                    $urlParts[] = $this->generateResourceName(strtolower($resource));
571 23
                } else {
572 29
                    $urlParts[] = strtolower($resource);
573
                }
574 30
            }
575 30
        }
576
577 30
        return $urlParts;
578
    }
579
580
    /**
581
     * Returns custom HTTP method for provided list of resources, arguments, method.
582
     *
583
     * @param string                 $httpMethod current HTTP method
584
     * @param string[]               $resources  resources list
585
     * @param \ReflectionParameter[] $arguments  list of method arguments
586
     *
587
     * @return string
588
     */
589 17
    private function getCustomHttpMethod($httpMethod, array $resources, array $arguments)
590
    {
591 17
        if (in_array($httpMethod, $this->availableConventionalActions)) {
592
            // allow hypertext as the engine of application state
593
            // through conventional GET actions
594 12
            return 'get';
595
        }
596
597 15
        if (count($arguments) < count($resources)) {
598
            // resource collection
599 15
            return 'get';
600
        }
601
602
        // custom object
603 14
        return 'patch';
604
    }
605
606
    /**
607
     * Returns first route annotation for method.
608
     *
609
     * @param \ReflectionMethod $reflectionMethod
610
     *
611
     * @return RouteAnnotation[]
612
     */
613 30
    private function readRouteAnnotation(\ReflectionMethod $reflectionMethod)
614
    {
615 30
        $annotations = [];
616
617 30
        if ($newAnnotations = $this->readMethodAnnotations($reflectionMethod, 'Route')) {
618 8
            $annotations = array_merge($annotations, $newAnnotations);
619 8
        }
620
621 30
        return $annotations;
622
    }
623
624
    /**
625
     * Reads class annotations.
626
     *
627
     * @param \ReflectionClass $reflectionClass
628
     * @param string           $annotationName
629
     *
630
     * @return RouteAnnotation|null
631
     */
632 30
    private function readClassAnnotation(\ReflectionClass $reflectionClass, $annotationName)
633
    {
634 30
        $annotationClass = "FOS\\RestBundle\\Controller\\Annotations\\$annotationName";
635
636 30
        if ($annotation = $this->annotationReader->getClassAnnotation($reflectionClass, $annotationClass)) {
637
            return $annotation;
638
        }
639 30
    }
640
641
    /**
642
     * Reads method annotations.
643
     *
644
     * @param \ReflectionMethod $reflectionMethod
645
     * @param string            $annotationName
646
     *
647
     * @return RouteAnnotation|null
648
     */
649 30
    private function readMethodAnnotation(\ReflectionMethod $reflectionMethod, $annotationName)
650
    {
651 30
        $annotationClass = "FOS\\RestBundle\\Controller\\Annotations\\$annotationName";
652
653 30
        if ($annotation = $this->annotationReader->getMethodAnnotation($reflectionMethod, $annotationClass)) {
654 8
            return $annotation;
655
        }
656 30
    }
657
658
    /**
659
     * Reads method annotations.
660
     *
661
     * @param \ReflectionMethod $reflectionMethod
662
     * @param string            $annotationName
663
     *
664
     * @return RouteAnnotation[]
665
     */
666 30
    private function readMethodAnnotations(\ReflectionMethod $reflectionMethod, $annotationName)
667
    {
668 30
        $annotations = [];
669 30
        $annotationClass = "FOS\\RestBundle\\Controller\\Annotations\\$annotationName";
670
671 30
        if ($annotations_new = $this->annotationReader->getMethodAnnotations($reflectionMethod)) {
672 14
            foreach ($annotations_new as $annotation) {
673 14
                if ($annotation instanceof $annotationClass) {
674 8
                    $annotations[] = $annotation;
675 8
                }
676 14
            }
677 14
        }
678
679 30
        return $annotations;
680
    }
681
682
    /**
683
     * @param RestRouteCollection $collection
684
     * @param string              $routeName
685
     * @param Route               $route
686
     * @param bool                $isCollection
687
     * @param bool                $isInflectable
688
     * @param RouteAnnotation     $annotation
689
     */
690 30
    private function addRoute(RestRouteCollection $collection, $routeName, $route, $isCollection, $isInflectable, RouteAnnotation $annotation = null)
691
    {
692 30
        if ($annotation && null !== $annotation->getName()) {
693 4
            $options = $annotation->getOptions();
694
695 4
            if (isset($options['method_prefix']) && false === $options['method_prefix']) {
696 3
                $routeName = $annotation->getName();
697 3
            } else {
698 4
                $routeName = $routeName.$annotation->getName();
699
            }
700 4
        }
701
702 30
        $fullRouteName = $this->namePrefix.$routeName;
703
704 30
        if ($isCollection && !$isInflectable) {
705 4
            $collection->add($this->namePrefix.self::COLLECTION_ROUTE_PREFIX.$routeName, $route);
706 4
            if (!$collection->get($fullRouteName)) {
707 4
                $collection->add($fullRouteName, clone $route);
708 4
            }
709 4
        } else {
710 28
            $collection->add($fullRouteName, $route);
711
        }
712 30
    }
713
}
714