Passed
Pull Request — master (#9)
by Pavel
12:36
created

AnnotationClassLoader   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 116
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 87.5%

Importance

Changes 0
Metric Value
dl 0
loc 116
c 0
b 0
f 0
wmc 21
lcom 1
cbo 5
ccs 56
cts 64
cp 0.875
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
C load() 0 34 7
A supports() 0 6 4
A getResolver() 0 3 1
A setResolver() 0 3 1
B getParentAnnotations() 0 26 5
A addRoute() 0 19 2
1
<?php
2
3
namespace Bankiru\Api\Rpc\Routing\Loader;
4
5
use Bankiru\Api\Rpc\Routing\Annotation\Method;
6
use Bankiru\Api\Rpc\Routing\LoaderInterface;
7
use Bankiru\Api\Rpc\Routing\MethodCollection;
8
use Bankiru\Api\Rpc\Routing\Route;
9
use Doctrine\Common\Annotations\Reader;
10
use Symfony\Component\Config\Resource\FileResource;
11
12
class AnnotationClassLoader implements LoaderInterface
13
{
14
    /** @var  Reader */
15
    private $reader;
16
17
    /**
18
     * Constructor.
19
     *
20
     * @param Reader $reader
21
     */
22 11
    public function __construct(Reader $reader)
23
    {
24 11
        $this->reader = $reader;
25 11
    }
26
27
    /** {@inheritdoc} */
28 2
    public function load($class, $type = null)
29
    {
30 2
        if (!class_exists($class)) {
31
            throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
32
        }
33
34 2
        $class = new \ReflectionClass($class);
35 2
        if ($class->isAbstract()) {
36
            throw new \InvalidArgumentException(
37
                sprintf('Annotations from class "%s" cannot be read as it is abstract.', $class->getName())
0 ignored issues
show
Bug introduced by
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
38
            );
39
        }
40
41 2
        $parents = $this->getParentAnnotations($class);
42
43 2
        $collection = new MethodCollection();
44 2
        $collection->addResource(new FileResource($class->getFileName()));
45
46 2
        foreach ($class->getMethods() as $method) {
47 2
            if (!$method->isPublic()) {
48 2
                continue;
49
            }
50
51 2
            foreach ($this->reader->getMethodAnnotations($method) as $annot) {
52 2
                if ($annot instanceof Method) {
53 2
                    $this->addRoute($collection, $annot, $parents, $class, $method);
54 2
                }
55 2
            }
56 2
        }
57
58 2
        $collection->addPrefix($parents['method']);
59
60 2
        return $collection;
61
    }
62
63
    /** {@inheritdoc} */
64 2
    public function supports($resource, $type = null)
65
    {
66 2
        return is_string($resource) &&
67 2
               preg_match('/^(?:\\\\?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)+$/', $resource) &&
68 2
               (!$type || 'annotation' === $type);
0 ignored issues
show
Bug Best Practice introduced by
The expression $type of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
69
    }
70
71
    /** {@inheritdoc} */
72
    public function getResolver()
73
    {
74
    }
75
76
    /** {@inheritdoc} */
77
    public function setResolver($resolver)
78
    {
79
    }
80
81 2
    protected function getParentAnnotations(\ReflectionClass $class)
82
    {
83
        $parents = [
84 2
            'method'          => '',
85 2
            'context'         => [],
86 2
            'default_context' => true,
87 2
            'options'         => [],
88 2
        ];
89
90
        /** @var Method $annot */
91 2
        if ($annot = $this->reader->getClassAnnotation($class, Method::class)) {
92 2
            if (null !== $annot->getMethod()) {
93 2
                $parents['method'] = $annot->getMethod();
94 2
            }
95
96 2
            if (null !== $annot->getContext()) {
97 2
                $parents['context'] = $annot->getContext();
98 2
            }
99
100 2
            if (null !== $annot->getOptions()) {
101 2
                $parents['options'] = $annot->getOptions();
102 2
            }
103 2
        }
104
105 2
        return $parents;
106
    }
107
108 2
    protected function addRoute(
109
        MethodCollection $collection,
110
        Method $annot,
111
        array $parents,
112
        \ReflectionClass $class,
113
        \ReflectionMethod $method
114
    ) {
115 2
        $collection->add(
116 2
            $annot->getMethod(),
117 2
            new Route(
118 2
                $annot->getMethod(),
119 2
                $class->getName() . '::' . $method->getName(),
0 ignored issues
show
Bug introduced by
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
120 2
                array_merge($parents['context'], $annot->getContext()),
121 2
                $parents['default_context'] && $annot->isDefaultContext(),
122 2
                $annot->isInherit(),
123 2
                array_merge_recursive($parents['options'], $annot->getOptions())
0 ignored issues
show
Unused Code introduced by
The call to Route::__construct() has too many arguments starting with array_merge_recursive($p..., $annot->getOptions()).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
124 2
            )
125 2
        );
126 2
    }
127
}
128