Completed
Push — master ( 95a6e8...b8af6d )
by Filipe
07:12
created

MethodsInspector::checkParameters()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19
Code Lines 12

Duplication

Lines 14
Ratio 73.68 %

Code Coverage

Tests 13
CRAP Score 4

Importance

Changes 0
Metric Value
dl 14
loc 19
ccs 13
cts 13
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 12
nc 4
nop 1
crap 4
1
<?php
2
3
/**
4
 * This file is part of slick/di package
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
namespace Slick\Di\DependencyInspector;
11
12
use Slick\Common\Base;
13
use Slick\Common\Inspector;
14
use Slick\Di\Definition\Object as ObjectDefinition;
15
use Slick\Di\Exception\NotFoundException;
16
17
/**
18
 * Methods Inspector for dependency definition
19
 *
20
 * @package Slick\Di\DependencyInspector
21
 * @author  Filipe Silva <[email protected]>
22
 */
23
class MethodsInspector extends Base
24
{
25
26
    /**
27
     * @readwrite
28
     * @var ObjectDefinition
29
     */
30
    protected $definition;
31
32
    /**
33
     * @read
34
     * @var array
35
     */
36
    protected $methods;
37
38
    /**
39
     * Set the definition that will be inspected
40
     *
41
     * @param ObjectDefinition $definition
42
     * @return $this
43
     */
44 46
    public function setDefinition(ObjectDefinition $definition)
45
    {
46 46
        $this->definition = $definition;
47 46
        $this->methods = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $methods.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
48 46
        $this->updateDefinition();
49 46
        return $this;
50
    }
51
52
    /**
53
     * Updates definition with method calls from inspection
54
     */
55 46
    protected function updateDefinition()
56
    {
57
        /**
58
         * @var string    $name
59
         * @var Parameter[] $params
60
         */
61 46
        foreach ($this->getMethods() as $name => $params)
62
        {
63 22
            $isInjectable = $this->checkMethodAnnotation($name, '@inject');
64 22
            $params = $this->checkParameters($params);
65 22
            if (!$params && $isInjectable) {
66 2
                throw new NotFoundException(
67 2
                    "The method {$name} has annotation '@inject' but ".
68 2
                    "current container has no correspondent entry for ".
69
                    "at least one of its parameters."
70 2
                );
71
            }
72 22
            if ($params) {
73 34
                $this->definition->setMethod($name, $params);
74 22
            }
75 46
        }
76 46
    }
77
78
    /**
79
     * Check and returns a list of parameters
80
     *
81
     * @param Parameter[] $params
82
     * @return array|bool
83
     */
84 22
    protected function checkParameters(array $params)
85
    {
86 22
        $arguments = [];
87 22 View Code Duplication
        foreach ($params as $param) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
88 22
            if ($this->definition->getContainer()->has($param->getId())) {
89 18
                $arguments[$param->name] = $this->definition
90 18
                    ->getContainer()
91 18
                    ->get($param->getId());
92 18
                continue;
93
            }
94
95 22
            if (!$param->isOptional()) {
96 22
                return false;
97
            }
98
99 22
            $arguments[$param->name] = $param->default;
100 22
        }
101 22
        return $arguments;
102
    }
103
104
    /**
105
     * Gets a list of methods with their arguments
106
     *
107
     * @return array
108
     */
109 46
    public function getMethods()
110
    {
111 46
        if (is_null($this->methods)) {
112 46
            $this->methods = [];
113 46
            $methods = Inspector::forClass($this->definition->getClassName())
114 46
                ->getClassMethods();
115 46
            foreach ($methods as $method) {
116 34
                $this->checkMethod($method);
117 46
            }
118 46
        }
119 46
        return $this->methods;
120
    }
121
122
    /**
123
     * Check if a method could is a setter.
124
     *
125
     * Setters must begin with 'get' and have one parameter only.
126
     *
127
     * @param string $method
128
     */
129 34
    protected function checkMethod($method)
130
    {
131 34
        $isAccessible = Inspector::forClass($this->definition->getClassName())
132 34
            ->getReflection()
133 34
            ->getMethod($method)
134 34
            ->isPublic();
135 34
        $isIgnore = $this->checkMethodAnnotation($method, '@ignoreInject');
136 34
        $isInject = $this->checkMethodAnnotation($method, '@inject');
137 34
        $isPublicSetter = $isAccessible && preg_match('/^set(.*)/i', $method);
138
        if (
139 34
            (!$isIgnore && $isPublicSetter) ||
140
            $isInject
141 34
        ) {
142 22
            $arguments = (new MethodInspector(
143 22
                ['definition' => $this->definition, 'name' => $method])
144
            )
145 22
                ->getArguments();
146 22
            if (count($arguments) == 1) {
147 22
                $this->methods[$method] = $arguments;
148 22
            }
149 22
        }
150 34
    }
151
152
    /**
153
     * Check method has an annotation with provided name
154
     *
155
     * @param string $method
156
     * @param string $annotation
157
     *
158
     * @return bool True if annotation is present, false otherwise.
159
     */
160 34
    protected function checkMethodAnnotation($method, $annotation)
161
    {
162 34
        $methodAnnotations = Inspector::forClass(
163 34
            $this->definition->getClassName()
164 34
        )
165 34
            ->getMethodAnnotations($method);
166 34
        return $methodAnnotations->hasAnnotation($annotation);
167
    }
168
}