Completed
Push — 2.x ( 77cf8b...f2a999 )
by Christian
03:13 queued 11s
created

RestActionReader::getRoutePrefix()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
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 62
    public function __construct(Reader $annotationReader, ParamReaderInterface $paramReader, InflectorInterface $inflector, $includeFormat, array $formats = [], $hasMethodPrefix = true)
130
    {
131 62
        $this->annotationReader = $annotationReader;
132 62
        $this->paramReader = $paramReader;
133 62
        $this->inflector = $inflector;
134 62
        $this->includeFormat = $includeFormat;
135 62
        $this->formats = $formats;
136 62
        $this->hasMethodPrefix = $hasMethodPrefix;
137 62
    }
138
139
    /**
140
     * Sets routes prefix.
141
     *
142
     * @param string $prefix Routes prefix
143
     */
144 43
    public function setRoutePrefix($prefix = null)
145
    {
146 43
        $this->routePrefix = $prefix;
147 43
    }
148
149
    /**
150
     * Returns route prefix.
151
     *
152
     * @return string
153
     */
154 43
    public function getRoutePrefix()
155
    {
156 43
        return $this->routePrefix;
157
    }
158
159
    /**
160
     * Sets route names prefix.
161
     *
162
     * @param string $prefix Route names prefix
163
     */
164 43
    public function setNamePrefix($prefix = null)
165
    {
166 43
        $this->namePrefix = $prefix;
167 43
    }
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 43
    public function setVersions($versions = null)
185
    {
186 43
        $this->versions = (array) $versions;
187 43
    }
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 43
    public function setPluralize($pluralize)
205
    {
206 43
        $this->pluralize = $pluralize;
207 43
    }
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 43
    public function setParents(array $parents)
225
    {
226 43
        $this->parents = $parents;
227 43
    }
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 43
    public function read(RestRouteCollection $collection, \ReflectionMethod $method, $resource)
251
    {
252
        // check that every route parent has non-empty singular name
253 43
        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 43
        if (!$this->isMethodReadable($method)) {
261 15
            return;
262
        }
263
264
        // if we can't get http-method and resources from method name - skip
265 43
        $httpMethodAndResources = $this->getHttpMethodAndResourcesFromMethod($method, $resource);
266 43
        if (!$httpMethodAndResources) {
267 40
            return;
268
        }
269
270 43
        list($httpMethod, $resources, $isCollection, $isInflectable) = $httpMethodAndResources;
271 43
        $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 43
        if (1 === count($resources) && 1 === count($arguments) - count($this->parents)) {
276 36
            $collection->setSingularName($resources[0]);
277
        }
278
279
        // if we have parents passed - merge them with own resource names
280 43
        if (count($this->parents)) {
281 5
            $resources = array_merge($this->parents, $resources);
282
        }
283
284 43
        if (empty($resources)) {
285 14
            $resources[] = null;
286
        }
287
288 43
        $routeName = $httpMethod.$this->generateRouteName($resources);
289 43
        $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 43
        if (!in_array($httpMethod, $this->availableHTTPMethods)) {
295 32
            $urlParts[] = $httpMethod;
296 32
            $httpMethod = $this->getCustomHttpMethod($httpMethod, $resources, $arguments);
297
        }
298
299
        // generated parameters
300 43
        $routeName = strtolower($routeName);
301 43
        $path = implode('/', $urlParts);
302 43
        $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 43
        $requirements = [];
304 43
        $options = [];
305 43
        $host = '';
306 43
        $versionCondition = $this->getVersionCondition();
307 43
        $versionRequirement = $this->getVersionRequirement();
308
309 43
        $annotations = $this->readRouteAnnotation($method);
310 43
        if (!empty($annotations)) {
311 23
            foreach ($annotations as $annotation) {
312 23
                $path = implode('/', $urlParts);
313 23
                $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 23
                $requirements = [];
315 23
                $options = [];
316 23
                $methods = explode('|', $httpMethod);
317
318 23
                $annoRequirements = $annotation->getRequirements();
319 23
                $annoMethods = $annotation->getMethods();
320
321 23
                if (!empty($annoMethods)) {
322 23
                    $methods = $annoMethods;
323
                }
324
325 23
                $path = null !== $annotation->getPath() ? $this->routePrefix.$annotation->getPath() : $path;
326 23
                $requirements = array_merge($requirements, $annoRequirements);
327 23
                $options = array_merge($options, $annotation->getOptions());
328 23
                $defaults = array_merge($defaults, $annotation->getDefaults());
329 23
                $host = $annotation->getHost();
330 23
                $schemes = $annotation->getSchemes();
331
332 23
                if ($this->hasVersionPlaceholder($path)) {
333 15
                    $combinedCondition = $annotation->getCondition();
334 15
                    $requirements = array_merge($versionRequirement, $requirements);
335
                } else {
336 23
                    $combinedCondition = $this->combineConditions($versionCondition, $annotation->getCondition());
337
                }
338
339 23
                $this->includeFormatIfNeeded($path, $requirements);
340
341
                // add route to collection
342 23
                $route = new Route(
343 23
                    $path,
344
                    $defaults,
345
                    $requirements,
346
                    $options,
347
                    $host,
348
                    $schemes,
349
                    $methods,
350
                    $combinedCondition
351
                );
352 23
                $this->addRoute($collection, $routeName, $route, $isCollection, $isInflectable, $annotation);
353
            }
354
        } else {
355 43
            if ($this->hasVersionPlaceholder($path)) {
356
                $versionCondition = null;
357
                $requirements = $versionRequirement;
358
            }
359
360 43
            $this->includeFormatIfNeeded($path, $requirements);
361
362 43
            $methods = explode('|', strtoupper($httpMethod));
363
364
            // add route to collection
365 43
            $route = new Route(
366 43
                $path,
367
                $defaults,
368
                $requirements,
369
                $options,
370
                $host,
371 43
                [],
372
                $methods,
373
                $versionCondition
374
            );
375 43
            $this->addRoute($collection, $routeName, $route, $isCollection, $isInflectable);
376
        }
377 43
    }
378
379
    /**
380
     * @return string|null
381
     */
382 43
    private function getVersionCondition()
383
    {
384 43
        if (empty($this->versions)) {
385 42
            return;
386
        }
387
388 15
        return sprintf("request.attributes.get('version') in ['%s']", implode("', '", $this->versions));
389
    }
390
391
    /**
392
     * @param string|null $conditionOne
393
     * @param string|null $conditionTwo
394
     *
395
     * @return string|null
396
     */
397 23
    private function combineConditions($conditionOne, $conditionTwo)
398
    {
399 23
        if (null === $conditionOne) {
400 8
            return $conditionTwo;
401
        }
402
403 15
        if (null === $conditionTwo) {
404 15
            return $conditionOne;
405
        }
406
407 1
        return sprintf('(%s) and (%s)', $conditionOne, $conditionTwo);
408
    }
409
410
    /**
411
     * @return array
412
     */
413 43
    private function getVersionRequirement()
414
    {
415 43
        if (empty($this->versions)) {
416 42
            return [];
417
        }
418
419 15
        return ['version' => implode('|', $this->versions)];
420
    }
421
422
    /**
423
     * Checks whether provided path contains {version} placeholder.
424
     *
425
     * @param string $path
426
     *
427
     * @return bool
428
     */
429 43
    private function hasVersionPlaceholder($path)
430
    {
431 43
        return false !== strpos($path, '{version}');
432
    }
433
434
    /**
435
     * Include the format in the path and requirements if its enabled.
436
     *
437
     * @param string $path
438
     * @param array  $requirements
439
     */
440 43
    private function includeFormatIfNeeded(&$path, &$requirements)
441
    {
442 43 View Code Duplication
        if (true === $this->includeFormat) {
443 43
            $path .= '.{_format}';
444
445 43
            if (!isset($requirements['_format']) && !empty($this->formats)) {
446 16
                $requirements['_format'] = implode('|', array_keys($this->formats));
447
            }
448
        }
449 43
    }
450
451
    /**
452
     * Checks whether provided method is readable.
453
     *
454
     * @param \ReflectionMethod $method
455
     *
456
     * @return bool
457
     */
458 43
    private function isMethodReadable(\ReflectionMethod $method)
459
    {
460
        // if method starts with _ - skip
461 43
        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...
462 10
            return false;
463
        }
464
465 43
        $hasNoRouteMethod = (bool) $this->readMethodAnnotation($method, 'NoRoute');
466 43
        $hasNoRouteClass = (bool) $this->readClassAnnotation($method->getDeclaringClass(), 'NoRoute');
467
468 43
        $hasNoRoute = $hasNoRouteMethod || $hasNoRouteClass;
469
        // since NoRoute extends Route we need to exclude all the method NoRoute annotations
470 43
        $hasRoute = (bool) $this->readMethodAnnotation($method, 'Route') && !$hasNoRouteMethod;
471
472
        // if method has NoRoute annotation and does not have Route annotation - skip
473 43
        if ($hasNoRoute && !$hasRoute) {
474 5
            return false;
475
        }
476
477 43
        return true;
478
    }
479
480
    /**
481
     * Returns HTTP method and resources list from method signature.
482
     *
483
     * @param \ReflectionMethod $method
484
     * @param string[]          $resource
485
     *
486
     * @return bool|array
487
     */
488 43
    private function getHttpMethodAndResourcesFromMethod(\ReflectionMethod $method, $resource)
489
    {
490
        // if method doesn't match regex - skip
491 43
        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...
492 40
            return false;
493
        }
494
495 43
        $httpMethod = strtolower($matches[1]);
496 43
        $resources = preg_split(
497 43
            '/([A-Z][^A-Z]*)/',
498 43
            $matches[2],
499 43
            -1,
500 43
            PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE
501
        );
502 43
        $isCollection = false;
503 43
        $isInflectable = true;
504
505 43
        if (0 === strpos($httpMethod, self::COLLECTION_ROUTE_PREFIX)
506 43
            && in_array(substr($httpMethod, 1), $this->availableHTTPMethods)
507
        ) {
508 20
            $isCollection = true;
509 20
            $httpMethod = substr($httpMethod, 1);
510 41
        } elseif ('options' === $httpMethod) {
511 15
            $isCollection = true;
512
        }
513
514 43
        if ($isCollection && !empty($resource)) {
515 20
            $resourcePluralized = $this->generateResourceName(end($resource));
516 20
            $isInflectable = ($resourcePluralized != $resource[count($resource) - 1]);
517 20
            $resource[count($resource) - 1] = $resourcePluralized;
518
        }
519
520 43
        $resources = array_merge($resource, $resources);
521
522 43
        return [$httpMethod, $resources, $isCollection, $isInflectable];
523
    }
524
525
    /**
526
     * Returns readable arguments from method.
527
     *
528
     * @param \ReflectionMethod $method
529
     *
530
     * @return \ReflectionParameter[]
531
     */
532 43
    private function getMethodArguments(\ReflectionMethod $method)
533
    {
534
        // ignore all query params
535 43
        $params = $this->paramReader->getParamsFromMethod($method);
536
537
        // check if a parameter is coming from the request body
538 43
        $ignoreParameters = [];
539 43
        if (class_exists(ParamConverter::class)) {
540
            $ignoreParameters = array_map(function ($annotation) {
541
                return
542 24
                    $annotation instanceof ParamConverter &&
543 24
                    'fos_rest.request_body' === $annotation->getConverter()
544 24
                        ? $annotation->getName() : null;
545 43
            }, $this->annotationReader->getMethodAnnotations($method));
546
        }
547
548
        // ignore several type hinted arguments
549
        $ignoreClasses = [
550 43
            ConstraintViolationListInterface::class,
551
            MessageInterface::class,
552
            ParamConverter::class,
553
            ParamFetcherInterface::class,
554
            Request::class,
555
            SessionInterface::class,
556
            UserInterface::class,
557
        ];
558
559 43
        $arguments = [];
560 43
        foreach ($method->getParameters() as $argument) {
561 40
            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...
562
                continue;
563
            }
564
565 40
            $argumentClass = $argument->getClass();
566 40
            if ($argumentClass) {
567 27
                $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...
568 27
                foreach ($ignoreClasses as $class) {
569 27
                    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...
570 25
                        continue 2;
571
                    }
572
                }
573
            }
574
575 40
            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...
576 1
                continue;
577
            }
578
579 39
            $arguments[] = $argument;
580
        }
581
582 43
        return $arguments;
583
    }
584
585
    /**
586
     * Generates final resource name.
587
     *
588
     * @param string|bool $resource
589
     *
590
     * @return string
591
     */
592 43
    private function generateResourceName($resource)
593
    {
594 43
        if (false === $this->pluralize) {
595 1
            return $resource;
596
        }
597
598 42
        return $this->inflector->pluralize($resource);
0 ignored issues
show
Bug introduced by
It seems like $resource defined by parameter $resource on line 592 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...
599
    }
600
601
    /**
602
     * Generates route name from resources list.
603
     *
604
     * @param string[] $resources
605
     *
606
     * @return string
607
     */
608 43
    private function generateRouteName(array $resources)
609
    {
610 43
        $routeName = '';
611 43
        foreach ($resources as $resource) {
612 43
            if (null !== $resource) {
613 43
                $routeName .= '_'.basename($resource);
614
            }
615
        }
616
617 43
        return $routeName;
618
    }
619
620
    /**
621
     * Generates URL parts for route from resources list.
622
     *
623
     * @param string[]               $resources
624
     * @param \ReflectionParameter[] $arguments
625
     * @param string                 $httpMethod
626
     *
627
     * @return array
628
     */
629 43
    private function generateUrlParts(array $resources, array $arguments, $httpMethod)
630
    {
631 43
        $urlParts = [];
632 43
        foreach ($resources as $i => $resource) {
633
            // if we already added all parent routes paths to URL & we have
634
            // prefix - add it
635 43
            if (!empty($this->routePrefix) && $i === count($this->parents)) {
636 3
                $urlParts[] = $this->routePrefix;
637
            }
638
639
            // if we have argument for current resource, then it's object.
640
            // otherwise - it's collection
641 43
            if (isset($arguments[$i])) {
642 39
                if (null !== $resource) {
643 39
                    $urlParts[] =
644 39
                        strtolower($this->generateResourceName($resource))
645 39
                        .'/{'.$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...
646
                } else {
647 39
                    $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...
648
                }
649 42
            } elseif (null !== $resource) {
650 42
                if ((0 === count($arguments) && !in_array($httpMethod, $this->availableHTTPMethods))
651 42
                    || 'new' === $httpMethod
652 42
                    || 'post' === $httpMethod
653
                ) {
654 35
                    $urlParts[] = $this->generateResourceName(strtolower($resource));
655
                } else {
656 41
                    $urlParts[] = strtolower($resource);
657
                }
658
            }
659
        }
660
661 43
        return $urlParts;
662
    }
663
664
    /**
665
     * Returns custom HTTP method for provided list of resources, arguments, method.
666
     *
667
     * @param string                 $httpMethod current HTTP method
668
     * @param string[]               $resources  resources list
669
     * @param \ReflectionParameter[] $arguments  list of method arguments
670
     *
671
     * @return string
672
     */
673 32
    private function getCustomHttpMethod($httpMethod, array $resources, array $arguments)
674
    {
675 32
        if (in_array($httpMethod, $this->availableConventionalActions)) {
676
            // allow hypertext as the engine of application state
677
            // through conventional GET actions
678 12
            return 'get';
679
        }
680
681 30
        if (count($arguments) < count($resources)) {
682
            // resource collection
683 16
            return 'get';
684
        }
685
686
        // custom object
687 29
        return 'patch';
688
    }
689
690
    /**
691
     * Returns first route annotation for method.
692
     *
693
     * @param \ReflectionMethod $reflectionMethod
694
     *
695
     * @return RouteAnnotation[]
696
     */
697 43
    private function readRouteAnnotation(\ReflectionMethod $reflectionMethod)
698
    {
699 43
        $annotations = [];
700
701 43
        if ($newAnnotations = $this->readMethodAnnotations($reflectionMethod, 'Route')) {
702 23
            $annotations = array_merge($annotations, $newAnnotations);
703
        }
704
705 43
        return $annotations;
706
    }
707
708
    /**
709
     * Reads class annotations.
710
     *
711
     * @param \ReflectionClass $reflectionClass
712
     * @param string           $annotationName
713
     *
714
     * @return RouteAnnotation|null
715
     */
716 43
    private function readClassAnnotation(\ReflectionClass $reflectionClass, $annotationName)
717
    {
718 43
        $annotationClass = "FOS\\RestBundle\\Controller\\Annotations\\$annotationName";
719
720 43
        if ($annotation = $this->annotationReader->getClassAnnotation($reflectionClass, $annotationClass)) {
721
            return $annotation;
722
        }
723 43
    }
724
725
    /**
726
     * Reads method annotations.
727
     *
728
     * @param \ReflectionMethod $reflectionMethod
729
     * @param string            $annotationName
730
     *
731
     * @return RouteAnnotation|null
732
     */
733 43
    private function readMethodAnnotation(\ReflectionMethod $reflectionMethod, $annotationName)
734
    {
735 43
        $annotationClass = "FOS\\RestBundle\\Controller\\Annotations\\$annotationName";
736
737 43
        if ($annotation = $this->annotationReader->getMethodAnnotation($reflectionMethod, $annotationClass)) {
738 23
            return $annotation;
739
        }
740 43
    }
741
742
    /**
743
     * Reads method annotations.
744
     *
745
     * @param \ReflectionMethod $reflectionMethod
746
     * @param string            $annotationName
747
     *
748
     * @return RouteAnnotation[]
749
     */
750 43
    private function readMethodAnnotations(\ReflectionMethod $reflectionMethod, $annotationName)
751
    {
752 43
        $annotations = [];
753 43
        $annotationClass = "FOS\\RestBundle\\Controller\\Annotations\\$annotationName";
754
755 43
        if ($annotations_new = $this->annotationReader->getMethodAnnotations($reflectionMethod)) {
756 24
            foreach ($annotations_new as $annotation) {
757 24
                if ($annotation instanceof $annotationClass) {
758 23
                    $annotations[] = $annotation;
759
                }
760
            }
761
        }
762
763 43
        return $annotations;
764
    }
765
766
    /**
767
     * @param RestRouteCollection $collection
768
     * @param string              $routeName
769
     * @param Route               $route
770
     * @param bool                $isCollection
771
     * @param bool                $isInflectable
772
     * @param RouteAnnotation     $annotation
773
     */
774 43
    private function addRoute(RestRouteCollection $collection, $routeName, $route, $isCollection, $isInflectable, RouteAnnotation $annotation = null)
775
    {
776 43
        if ($annotation && null !== $annotation->getName()) {
777 5
            $options = $annotation->getOptions();
778
779 5
            if (false === $this->hasMethodPrefix || (isset($options['method_prefix']) && false === $options['method_prefix'])) {
780 4
                $routeName = $annotation->getName();
781
            } else {
782 4
                $routeName .= $annotation->getName();
783
            }
784
        }
785
786 43
        $fullRouteName = $this->namePrefix.$routeName;
787
788 43
        if ($isCollection && !$isInflectable) {
789 4
            $collection->add($this->namePrefix.self::COLLECTION_ROUTE_PREFIX.$routeName, $route);
790 4
            if (!$collection->get($fullRouteName)) {
791 4
                $collection->add($fullRouteName, clone $route);
792
            }
793
        } else {
794 41
            $collection->add($fullRouteName, $route);
795
        }
796 43
    }
797
}
798