Completed
Push — master ( b1054d...eba5f2 )
by Christian
07:28 queued 11s
created

getHttpMethodAndResourcesFromMethod()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 7

Importance

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