Completed
Push — master ( 03ec6c...0df031 )
by
unknown
12:45 queued 02:44
created

RestActionReader::getVersionRequirement()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 2
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\ParamFetcherInterface;
18
use FOS\RestBundle\Request\ParamReaderInterface;
19
use FOS\RestBundle\Routing\RestRouteCollection;
20
use Psr\Http\Message\MessageInterface;
21
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
22
use Symfony\Component\HttpFoundation\Request;
23
use Symfony\Component\Routing\Route;
24
use Symfony\Component\Validator\ConstraintViolationListInterface;
25
26
/**
27
 * REST controller actions reader.
28
 *
29
 * @author Konstantin Kudryashov <[email protected]>
30
 */
31
class RestActionReader
32
{
33
    const COLLECTION_ROUTE_PREFIX = 'c';
34
35
    /**
36
     * @var Reader
37
     */
38
    private $annotationReader;
39
40
    /**
41
     * @var ParamReaderInterface
42
     */
43
    private $paramReader;
44
45
    /**
46
     * @var InflectorInterface
47
     */
48
    private $inflector;
49
50
    /**
51
     * @var array
52
     */
53
    private $formats;
54
55
    /**
56
     * @var bool
57
     */
58
    private $includeFormat;
59
60
    /**
61
     * @var string|null
62
     */
63
    private $routePrefix;
64
65
    /**
66
     * @var string|null
67
     */
68
    private $namePrefix;
69
70
    /**
71
     * @var array|string|null
72
     */
73
    private $versions;
74
75
    /**
76
     * @var bool|null
77
     */
78
    private $pluralize;
79
80
    /**
81
     * @var array
82
     */
83
    private $parents = [];
84
85
    /**
86
     * @var array
87
     */
88
    private $availableHTTPMethods = [
89
        'get',
90
        'post',
91
        'put',
92
        'patch',
93
        'delete',
94
        'link',
95
        'unlink',
96
        'head',
97
        'options',
98
        'mkcol',
99
        'propfind',
100
        'proppatch',
101
        'move',
102
        'copy',
103
        'lock',
104
        'unlock',
105
    ];
106
107
    /**
108
     * @var array
109
     */
110
    private $availableConventionalActions = ['new', 'edit', 'remove'];
111
112
    /**
113
     * @var bool
114
     */
115
    private $hasMethodPrefix;
116
117 56
    /**
118
     * Initializes controller reader.
119 56
     *
120 56
     * @param Reader               $annotationReader
121 56
     * @param ParamReaderInterface $paramReader
122 56
     * @param InflectorInterface   $inflector
123 56
     * @param bool                 $includeFormat
124 56
     * @param array                $formats
125
     * @param bool                 $hasMethodPrefix
126
     */
127
    public function __construct(Reader $annotationReader, ParamReaderInterface $paramReader, InflectorInterface $inflector, $includeFormat, array $formats = [], $hasMethodPrefix = true)
128
    {
129
        $this->annotationReader = $annotationReader;
130
        $this->paramReader = $paramReader;
131 37
        $this->inflector = $inflector;
132
        $this->includeFormat = $includeFormat;
133 37
        $this->formats = $formats;
134 37
        $this->hasMethodPrefix = $hasMethodPrefix;
135
    }
136
137
    /**
138
     * Sets routes prefix.
139
     *
140
     * @param string $prefix Routes prefix
141 37
     */
142
    public function setRoutePrefix($prefix = null)
143 37
    {
144
        $this->routePrefix = $prefix;
145
    }
146
147
    /**
148
     * Returns route prefix.
149
     *
150
     * @return string
151 37
     */
152
    public function getRoutePrefix()
153 37
    {
154 37
        return $this->routePrefix;
155
    }
156
157
    /**
158
     * Sets route names prefix.
159
     *
160
     * @param string $prefix Route names prefix
161
     */
162
    public function setNamePrefix($prefix = null)
163
    {
164
        $this->namePrefix = $prefix;
165
    }
166
167
    /**
168
     * Returns name prefix.
169
     *
170
     * @return string
171 37
     */
172
    public function getNamePrefix()
173 37
    {
174 37
        return $this->namePrefix;
175
    }
176
177
    /**
178
     * Sets route names versions.
179
     *
180
     * @param array|string|null $versions Route names versions
181
     */
182
    public function setVersions($versions = null)
183
    {
184
        $this->versions = (array) $versions;
185
    }
186
187
    /**
188
     * Returns versions.
189
     *
190
     * @return array|null
191 37
     */
192
    public function getVersions()
193 37
    {
194 37
        return $this->versions;
195
    }
196
197
    /**
198
     * Sets pluralize.
199
     *
200
     * @param bool|null $pluralize Specify if resource name must be pluralized
201
     */
202
    public function setPluralize($pluralize)
203
    {
204
        $this->pluralize = $pluralize;
205
    }
206
207
    /**
208
     * Returns pluralize.
209
     *
210
     * @return bool|null
211 37
     */
212
    public function getPluralize()
213 37
    {
214 37
        return $this->pluralize;
215
    }
216
217
    /**
218
     * Set parent routes.
219
     *
220
     * @param array $parents Array of parent resources names
221
     */
222
    public function setParents(array $parents)
223
    {
224
        $this->parents = $parents;
225
    }
226
227
    /**
228
     * Returns parents.
229
     *
230
     * @return array
231
     */
232
    public function getParents()
233
    {
234
        return $this->parents;
235
    }
236
237 37
    /**
238
     * Reads action route.
239
     *
240 37
     * @param RestRouteCollection $collection
241 5
     * @param \ReflectionMethod   $method
242
     * @param string[]            $resource
243
     *
244
     * @throws \InvalidArgumentException
245
     *
246
     * @return Route
247 37
     */
248
    public function read(RestRouteCollection $collection, \ReflectionMethod $method, $resource)
249
    {
250 37
        // check that every route parent has non-empty singular name
251 14
        foreach ($this->parents as $parent) {
252
            if (empty($parent) || '/' === substr($parent, -1)) {
253
                throw new \InvalidArgumentException('Every parent controller must have `get{SINGULAR}Action(\$id)` method where {SINGULAR} is a singular form of associated object');
254
            }
255 37
        }
256 37
257 35
        // if method is not readable - skip
258
        if (!$this->isMethodReadable($method)) {
259
            return;
260 37
        }
261 37
262
        // if we can't get http-method and resources from method name - skip
263
        $httpMethodAndResources = $this->getHttpMethodAndResourcesFromMethod($method, $resource);
264
        if (!$httpMethodAndResources) {
265 37
            return;
266 21
        }
267 21
268
        list($httpMethod, $resources, $isCollection, $isInflectable) = $httpMethodAndResources;
269
        $arguments = $this->getMethodArguments($method);
270 37
271 5
        // if we have only 1 resource & 1 argument passed, then it's object call, so
272 5
        // we can set collection singular name
273
        if (1 === count($resources) && 1 === count($arguments) - count($this->parents)) {
274 37
            $collection->setSingularName($resources[0]);
275 10
        }
276 10
277
        // if we have parents passed - merge them with own resource names
278 37
        if (count($this->parents)) {
279 37
            $resources = array_merge($this->parents, $resources);
280
        }
281
282
        if (empty($resources)) {
283
            $resources[] = null;
284 37
        }
285 27
286 27
        $routeName = $httpMethod.$this->generateRouteName($resources);
287 27
        $urlParts = $this->generateUrlParts($resources, $arguments, $httpMethod);
288
289
        // if passed method is not valid HTTP method then it's either
290 37
        // a hypertext driver, a custom object (PUT) or collection (GET)
291 37
        // method
292 37
        if (!in_array($httpMethod, $this->availableHTTPMethods)) {
293 37
            $urlParts[] = $httpMethod;
294 37
            $httpMethod = $this->getCustomHttpMethod($httpMethod, $resources, $arguments);
295 37
        }
296 37
297
        // generated parameters
298 37
        $routeName = strtolower($routeName);
299 37
        $path = implode('/', $urlParts);
300 18
        $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...
301 18
        $requirements = [];
302 18
        $options = [];
303 18
        $host = '';
304 18
        $versionCondition = $this->getVersionCondition();
305 18
        $versionRequirement = $this->getVersionRequirement();
306
307 18
        $annotations = $this->readRouteAnnotation($method);
308 18
        if (!empty($annotations)) {
309
            foreach ($annotations as $annotation) {
310 18
                $path = implode('/', $urlParts);
311 18
                $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...
312 18
                $requirements = [];
313
                $options = [];
314 18
                $methods = explode('|', $httpMethod);
315 18
316 18
                $annoRequirements = $annotation->getRequirements();
317 18
                $annoMethods = $annotation->getMethods();
318 18
319 18
                if (!empty($annoMethods)) {
320 18
                    $methods = $annoMethods;
321
                }
322 18
323
                $path = null !== $annotation->getPath() ? $this->routePrefix.$annotation->getPath() : $path;
324
                $requirements = array_merge($requirements, $annoRequirements);
325 18
                $options = array_merge($options, $annotation->getOptions());
326 18
                $defaults = array_merge($defaults, $annotation->getDefaults());
327 18
                $host = $annotation->getHost();
328 18
                $schemes = $annotation->getSchemes();
329 18
330 18
                if ($this->hasVersionPlaceholder($path)) {
331 37
                    $combinedCondition = $annotation->getCondition();
332
                    $requirements = array_merge($versionRequirement, $requirements);
333 37
                } else {
334
                    $combinedCondition = $this->combineConditions($versionCondition, $annotation->getCondition());
335
                }
336 37
337 37
                $this->includeFormatIfNeeded($path, $requirements);
338 37
339 37
                // add route to collection
340
                $route = new Route(
341 37
                    $path, $defaults, $requirements, $options, $host, $schemes, $methods, $combinedCondition
342
                );
343
                $this->addRoute($collection, $routeName, $route, $isCollection, $isInflectable, $annotation);
344
            }
345
        } else {
346 37
            if ($this->hasVersionPlaceholder($path)) {
347
                $versionCondition = null;
348 37
                $requirements = $versionRequirement;
349 36
            }
350
351
            $this->includeFormatIfNeeded($path, $requirements);
352 11
353
            $methods = explode('|', strtoupper($httpMethod));
354
355
            // add route to collection
356
            $route = new Route(
357
                $path, $defaults, $requirements, $options, $host, [], $methods, $versionCondition
358
            );
359
            $this->addRoute($collection, $routeName, $route, $isCollection, $isInflectable);
360
        }
361 18
    }
362
363 18
    /**
364 7
     * @return string|null
365
     */
366
    private function getVersionCondition()
367 11
    {
368 11
        if (empty($this->versions)) {
369
            return;
370
        }
371 1
372
        return sprintf("request.attributes.get('version') in ['%s']", implode("', '", $this->versions));
373
    }
374
375
    /**
376
     * @param string|null $conditionOne
377
     * @param string|null $conditionTwo
378
     *
379
     * @return string|null
380 37
     */
381
    private function combineConditions($conditionOne, $conditionTwo)
382 37
    {
383 37
        if (null === $conditionOne) {
384
            return $conditionTwo;
385 37
        }
386 12
387 12
        if (null === $conditionTwo) {
388 37
            return $conditionOne;
389 37
        }
390
391
        return sprintf('(%s) and (%s)', $conditionOne, $conditionTwo);
392
    }
393
394
    /**
395
     * @return array
396
     */
397
    private function getVersionRequirement()
398 37
    {
399
        if (empty($this->versions)) {
400
            return [];
401 37
        }
402 10
403
        return ['version' => implode('|', $this->versions)];
404
    }
405 37
406 37
    /**
407
     * Checks whether provided path contains {version} placeholder.
408 37
     *
409
     * @param string $path
410 37
     *
411
     * @return bool
412
     */
413 37
    private function hasVersionPlaceholder($path)
414 4
    {
415
        return false !== strpos($path, '{version}');
416
    }
417 37
418
    /**
419
     * Include the format in the path and requirements if its enabled.
420
     *
421
     * @param string $path
422
     * @param array  $requirements
423
     */
424
    private function includeFormatIfNeeded(&$path, &$requirements)
425
    {
426 View Code Duplication
        if (true === $this->includeFormat) {
427
            $path .= '.{_format}';
428 37
429
            if (!isset($requirements['_format']) && !empty($this->formats)) {
430
                $requirements['_format'] = implode('|', array_keys($this->formats));
431 37
            }
432 35
        }
433
    }
434
435 37
    /**
436 37
     * Checks whether provided method is readable.
437 37
     *
438 37
     * @param \ReflectionMethod $method
439 37
     *
440 37
     * @return bool
441
     */
442 37
    private function isMethodReadable(\ReflectionMethod $method)
443 37
    {
444 37
        // if method starts with _ - skip
445 16
        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...
446 16
            return false;
447 37
        }
448 14
449 14
        $hasNoRouteMethod = (bool) $this->readMethodAnnotation($method, 'NoRoute');
450
        $hasNoRouteClass = (bool) $this->readClassAnnotation($method->getDeclaringClass(), 'NoRoute');
451 37
452 16
        $hasNoRoute = $hasNoRouteMethod || $hasNoRouteClass;
453 16
        // since NoRoute extends Route we need to exclude all the method NoRoute annotations
454 16
        $hasRoute = (bool) $this->readMethodAnnotation($method, 'Route') && !$hasNoRouteMethod;
455 16
456
        // if method has NoRoute annotation and does not have Route annotation - skip
457 37
        if ($hasNoRoute && !$hasRoute) {
458
            return false;
459 37
        }
460
461
        return true;
462
    }
463
464
    /**
465
     * Returns HTTP method and resources list from method signature.
466
     *
467
     * @param \ReflectionMethod $method
468
     * @param string[]          $resource
469 37
     *
470
     * @return bool|array
471
     */
472 37
    private function getHttpMethodAndResourcesFromMethod(\ReflectionMethod $method, $resource)
473
    {
474
        // if method doesn't match regex - skip
475
        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...
476 37
            return false;
477 37
        }
478 37
479 37
        $httpMethod = strtolower($matches[1]);
480 37
        $resources = preg_split(
481 37
            '/([A-Z][^A-Z]*)/', $matches[2], -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE
482
        );
483 37
        $isCollection = false;
484 37
        $isInflectable = true;
485 34
486
        if (0 === strpos($httpMethod, self::COLLECTION_ROUTE_PREFIX)
487
            && in_array(substr($httpMethod, 1), $this->availableHTTPMethods)
488
        ) {
489 34
            $isCollection = true;
490 34
            $httpMethod = substr($httpMethod, 1);
491 22
        } elseif ('options' === $httpMethod) {
492 22
            $isCollection = true;
493 22
        }
494 21
495
        if ($isCollection && !empty($resource)) {
496 3
            $resourcePluralized = $this->generateResourceName(end($resource));
497 1
            $isInflectable = ($resourcePluralized != $resource[count($resource) - 1]);
498
            $resource[count($resource) - 1] = $resourcePluralized;
499 34
        }
500 37
501
        $resources = array_merge($resource, $resources);
502 37
503
        return [$httpMethod, $resources, $isCollection, $isInflectable];
504
    }
505
506
    /**
507
     * Returns readable arguments from method.
508
     *
509
     * @param \ReflectionMethod $method
510
     *
511
     * @return \ReflectionParameter[]
512 37
     */
513
    private function getMethodArguments(\ReflectionMethod $method)
514 37
    {
515 1
        // ignore all query params
516
        $params = $this->paramReader->getParamsFromMethod($method);
517
518 36
        // check if a parameter is coming from the request body
519
        $ignoreParameters = [];
520
        if (class_exists(ParamConverter::class)) {
521
            $ignoreParameters = array_map(function ($annotation) {
522
                return
523
                    $annotation instanceof ParamConverter &&
524
                    'fos_rest.request_body' === $annotation->getConverter()
525
                        ? $annotation->getName() : null;
526
            }, $this->annotationReader->getMethodAnnotations($method));
527
        }
528 37
529
        // ignore several type hinted arguments
530 37
        $ignoreClasses = [
531 37
            Request::class,
532 37
            ParamFetcherInterface::class,
533 37
            ConstraintViolationListInterface::class,
534 37
            ParamConverter::class,
535 37
            MessageInterface::class,
536
        ];
537 37
538
        $arguments = [];
539
        foreach ($method->getParameters() as $argument) {
540
            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...
541
                continue;
542
            }
543
544
            $argumentClass = $argument->getClass();
545
            if ($argumentClass) {
546
                $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...
547
                foreach ($ignoreClasses as $class) {
548
                    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...
549 37
                        continue 2;
550
                    }
551 37
                }
552 37
            }
553
554
            if (in_array($argument->getName(), $ignoreParameters, true)) {
0 ignored issues
show
Bug introduced by
Consider using $argument->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
555 37
                continue;
556 3
            }
557 3
558
            $arguments[] = $argument;
559
        }
560
561 37
        return $arguments;
562 34
    }
563 24
564 24
    /**
565 24
     * Generates final resource name.
566 24
     *
567 10
     * @param string|bool $resource
568
     *
569 37
     * @return string
570 36
     */
571
    private function generateResourceName($resource)
572 35
    {
573 36
        if (false === $this->pluralize) {
574 29
            return $resource;
575 29
        }
576 35
577
        return $this->inflector->pluralize($resource);
0 ignored issues
show
Bug introduced by
It seems like $resource defined by parameter $resource on line 571 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...
578 36
    }
579 37
580
    /**
581 37
     * Generates route name from resources list.
582
     *
583
     * @param string[] $resources
584
     *
585
     * @return string
586
     */
587
    private function generateRouteName(array $resources)
588
    {
589
        $routeName = '';
590
        foreach ($resources as $resource) {
591
            if (null !== $resource) {
592
                $routeName .= '_'.basename($resource);
593 27
            }
594
        }
595 27
596
        return $routeName;
597
    }
598 12
599
    /**
600
     * Generates URL parts for route from resources list.
601 25
     *
602
     * @param string[]               $resources
603 15
     * @param \ReflectionParameter[] $arguments
604
     * @param string                 $httpMethod
605
     *
606
     * @return array
607 24
     */
608
    private function generateUrlParts(array $resources, array $arguments, $httpMethod)
609
    {
610
        $urlParts = [];
611
        foreach ($resources as $i => $resource) {
612
            // if we already added all parent routes paths to URL & we have
613
            // prefix - add it
614
            if (!empty($this->routePrefix) && $i === count($this->parents)) {
615
                $urlParts[] = $this->routePrefix;
616
            }
617 37
618
            // if we have argument for current resource, then it's object.
619 37
            // otherwise - it's collection
620
            if (isset($arguments[$i])) {
621 37
                if (null !== $resource) {
622 18
                    $urlParts[] =
623 18
                        strtolower($this->generateResourceName($resource))
624
                        .'/{'.$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...
625 37
                } else {
626
                    $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...
627
                }
628
            } elseif (null !== $resource) {
629
                if ((0 === count($arguments) && !in_array($httpMethod, $this->availableHTTPMethods))
630
                    || 'new' === $httpMethod
631
                    || 'post' === $httpMethod
632
                ) {
633
                    $urlParts[] = $this->generateResourceName(strtolower($resource));
634
                } else {
635
                    $urlParts[] = strtolower($resource);
636 37
                }
637
            }
638 37
        }
639
640 37
        return $urlParts;
641
    }
642
643 37
    /**
644
     * Returns custom HTTP method for provided list of resources, arguments, method.
645
     *
646
     * @param string                 $httpMethod current HTTP method
647
     * @param string[]               $resources  resources list
648
     * @param \ReflectionParameter[] $arguments  list of method arguments
649
     *
650
     * @return string
651
     */
652
    private function getCustomHttpMethod($httpMethod, array $resources, array $arguments)
653 37
    {
654
        if (in_array($httpMethod, $this->availableConventionalActions)) {
655 37
            // allow hypertext as the engine of application state
656
            // through conventional GET actions
657 37
            return 'get';
658 18
        }
659
660 37
        if (count($arguments) < count($resources)) {
661
            // resource collection
662
            return 'get';
663
        }
664
665
        // custom object
666
        return 'patch';
667
    }
668
669
    /**
670 37
     * Returns first route annotation for method.
671
     *
672 37
     * @param \ReflectionMethod $reflectionMethod
673 37
     *
674
     * @return RouteAnnotation[]
675 37
     */
676 18
    private function readRouteAnnotation(\ReflectionMethod $reflectionMethod)
677 18
    {
678 18
        $annotations = [];
679 18
680 18
        if ($newAnnotations = $this->readMethodAnnotations($reflectionMethod, 'Route')) {
681 18
            $annotations = array_merge($annotations, $newAnnotations);
682
        }
683 37
684
        return $annotations;
685
    }
686
687
    /**
688
     * Reads class annotations.
689
     *
690
     * @param \ReflectionClass $reflectionClass
691
     * @param string           $annotationName
692
     *
693
     * @return RouteAnnotation|null
694 37
     */
695
    private function readClassAnnotation(\ReflectionClass $reflectionClass, $annotationName)
696 37
    {
697 4
        $annotationClass = "FOS\\RestBundle\\Controller\\Annotations\\$annotationName";
698
699 4
        if ($annotation = $this->annotationReader->getClassAnnotation($reflectionClass, $annotationClass)) {
700 3
            return $annotation;
701 3
        }
702 4
    }
703
704 4
    /**
705
     * Reads method annotations.
706 37
     *
707
     * @param \ReflectionMethod $reflectionMethod
708 37
     * @param string            $annotationName
709 4
     *
710 4
     * @return RouteAnnotation|null
711 4
     */
712 4
    private function readMethodAnnotation(\ReflectionMethod $reflectionMethod, $annotationName)
713 4
    {
714 35
        $annotationClass = "FOS\\RestBundle\\Controller\\Annotations\\$annotationName";
715
716 37
        if ($annotation = $this->annotationReader->getMethodAnnotation($reflectionMethod, $annotationClass)) {
717
            return $annotation;
718
        }
719
    }
720
721
    /**
722
     * Reads method annotations.
723
     *
724
     * @param \ReflectionMethod $reflectionMethod
725
     * @param string            $annotationName
726
     *
727
     * @return RouteAnnotation[]
728
     */
729
    private function readMethodAnnotations(\ReflectionMethod $reflectionMethod, $annotationName)
730
    {
731
        $annotations = [];
732
        $annotationClass = "FOS\\RestBundle\\Controller\\Annotations\\$annotationName";
733
734
        if ($annotations_new = $this->annotationReader->getMethodAnnotations($reflectionMethod)) {
735
            foreach ($annotations_new as $annotation) {
736
                if ($annotation instanceof $annotationClass) {
737
                    $annotations[] = $annotation;
738
                }
739
            }
740
        }
741
742
        return $annotations;
743
    }
744
745
    /**
746
     * @param RestRouteCollection $collection
747
     * @param string              $routeName
748
     * @param Route               $route
749
     * @param bool                $isCollection
750
     * @param bool                $isInflectable
751
     * @param RouteAnnotation     $annotation
752
     */
753
    private function addRoute(RestRouteCollection $collection, $routeName, $route, $isCollection, $isInflectable, RouteAnnotation $annotation = null)
754
    {
755
        if ($annotation && null !== $annotation->getName()) {
756
            $options = $annotation->getOptions();
757
758
            if (false === $this->hasMethodPrefix || (isset($options['method_prefix']) && false === $options['method_prefix'])) {
759
                $routeName = $annotation->getName();
760
            } else {
761
                $routeName .= $annotation->getName();
762
            }
763
        }
764
765
        $fullRouteName = $this->namePrefix.$routeName;
766
767
        if ($isCollection && !$isInflectable) {
768
            $collection->add($this->namePrefix.self::COLLECTION_ROUTE_PREFIX.$routeName, $route);
769
            if (!$collection->get($fullRouteName)) {
770
                $collection->add($fullRouteName, clone $route);
771
            }
772
        } else {
773
            $collection->add($fullRouteName, $route);
774
        }
775
    }
776
}
777