Completed
Pull Request — master (#4)
by Joao
01:21
created

DependencyInjection::getClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
4
namespace ByJG\Config;
5
6
use ByJG\Config\Exception\DependencyInjectionException;
7
use Psr\Container\ContainerInterface;
8
use ReflectionClass;
9
use ReflectionException;
10
use ReflectionMethod;
11
12
class DependencyInjection
13
{
14
    /**
15
     * @var ContainerInterface
16
     */
17
    protected $containerInterface;
18
19
    protected $class;
20
21
    protected $args = [];
22
23
    protected $instance;
24
25
    protected $singleton = false;
26
27
    protected $factory = null;
28
29
    protected $methodCall = [];
30
31
    /**
32
     * @param $containerInterface ContainerInterface
33
     * @return DependencyInjection
34
     */
35
    public function injectContainer($containerInterface)
36
    {
37
        $this->containerInterface = $containerInterface;
38
        return $this;
39
    }
40
41
    /**
42
     * @return mixed
43
     */
44
    protected function getClass()
45
    {
46
        return $this->class;
47
    }
48
49
    /**
50
     * @param mixed $class
51
     * @throws DependencyInjectionException
52
     */
53
    protected function setClass($class)
54
    {
55
        if (!class_exists($class)) {
56
            throw new DependencyInjectionException("Class $class does not exists");
57
        }
58
        $this->class = $class;
59
    }
60
61
    /**
62
     * @return mixed
63
     */
64
    protected function getArgs()
65
    {
66
        return array_map(function ($value) {
67
            if ($value instanceof Param) {
68
                return $this->containerInterface->get($value->getParam());
69
            }
70
            return $value;
71
        }, $this->args);
72
    }
73
74
    /**
75
     * @param mixed $args
76
     * @return DependencyInjection
77
     * @throws DependencyInjectionException
78
     */
79 View Code Duplication
    public function withConstructorArgs($args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
80
    {
81
        if (!is_null($args) && !is_array($args)) {
82
            throw new DependencyInjectionException("Arguments should be an array");
83
        }
84
        $this->args = $args;
0 ignored issues
show
Documentation Bug introduced by
It seems like $args can be null. However, the property $args is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

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

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
85
86
        return $this;
87
    }
88
89
    /**
90
     * @param mixed $args
91
     * @return DependencyInjection
92
     * @throws DependencyInjectionException
93
     */
94 View Code Duplication
    public function withFactoryMethod($method, $args = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
95
    {
96
        if (!is_null($args) && !is_array($args)) {
97
            throw new DependencyInjectionException("Arguments should be an array");
98
        }
99
        $this->args = $args;
0 ignored issues
show
Documentation Bug introduced by
It seems like $args can be null. However, the property $args is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

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

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
100
101
        $this->factory = $method;
102
103
        return $this;
104
    }
105
106
    /**
107
     * DependencyInjection constructor.
108
     * @param $class
109
     * @throws DependencyInjectionException
110
     */
111
    protected function __construct($class)
112
    {
113
        $this->setClass($class);
114
    }
115
116
    /**
117
     * @param $class
118
     * @return DependencyInjection
119
     * @throws DependencyInjectionException
120
     */
121
    public static function bind($class)
122
    {
123
        return new DependencyInjection($class);
124
    }
125
126
    /**
127
     * @return DependencyInjection
128
     * @throws DependencyInjectionException
129
     * @throws ReflectionException
130
     */
131
    public function withInjectedConstructor()
132
    {
133
        $reflection = new ReflectionMethod($this->getClass(), "__construct");
134
135
        $docComments = str_replace("\n", " ", $reflection->getDocComment());
136
137
        $params = [];
138
        $result = preg_match_all('/@param\s+([\d\w_\\\\]+)\s+\$[\w_\d]+/', $docComments, $params);
139
140
        if ($result) {
141
            $args = [];
142
            foreach ($params[1] as $param) {
143
                $args[] = Param::get(ltrim($param, "\\"));
144
            }
145
            return $this->withConstructorArgs($args);
146
        }
147
148
        return $this->withNoConstructor();
149
    }
150
151
    /**
152
     * @return DependencyInjection
153
     */
154
    public function withNoConstructor()
155
    {
156
        $this->args = 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 $args.

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...
157
        return $this;
158
    }
159
160
    public function withMethodCall($method, $args = [])
161
    {
162
        $this->methodCall[$method] = $args;
163
        return $this;
164
    }
165
166
    /**
167
     * @return DependencyInjection
168
     */
169
    public function toSingleton()
170
    {
171
        $this->singleton = true;
172
        return $this;
173
    }
174
175
    /**
176
     * @return DependencyInjection
177
     */
178
    public function toInstance()
179
    {
180
        $this->singleton = false;
181
        return $this;
182
    }
183
184
    /**
185
     * @return object
186
     * @throws DependencyInjectionException
187
     * @throws ReflectionException
188
     */
189
    public function getInstance()
190
    {
191
        $instance = $this->getInternalInstance();
192
193
        if (is_null($instance)) {
194
            throw new DependencyInjectionException("Could not get a instance of " . $this->getClass());
195
        }
196
197
        return $instance;
198
    }
199
200
    /**
201
     * @return object
202
     * @throws ReflectionException
203
     */
204
    protected function getInternalInstance()
205
    {
206
        if ($this->singleton) {
207
            return $this->getSingletonInstace();
208
        }
209
210
        return $this->getNewInstance();
211
    }
212
213
    /**
214
     * @return object
215
     * @throws ReflectionException
216
     */
217
    protected function getNewInstance()
218
    {
219
        if (!empty($this->factory)) {
220
            return $this->callMethods(call_user_func_array([$this->getClass(), $this->factory], $this->getArgs()));
221
        }
222
223
        $reflectionClass = new ReflectionClass($this->getClass());
224
225
        if (is_null($this->args)) {
226
            return $this->callMethods($reflectionClass->newInstanceWithoutConstructor());
227
        }
228
229
        return $this->callMethods($reflectionClass->newInstanceArgs($this->getArgs()));
230
    }
231
232
    /**
233
     * @param $instance
234
     * @return mixed
235
     */
236
    protected function callMethods($instance)
237
    {
238
        foreach ($this->methodCall as $methodName => $args) {
239
            if (is_null($args)) {
240
                call_user_func([$instance, $methodName]);
241
            } else {
242
                call_user_func_array([$instance, $methodName], $args);
243
            }
244
        }
245
246
        return $instance;
247
    }
248
249
    /**
250
     * @return object
251
     * @throws ReflectionException
252
     */
253
    protected function getSingletonInstace()
254
    {
255
        if (empty($this->instance)) {
256
            $this->instance = $this->getNewInstance();
257
        }
258
        return $this->instance;
259
    }
260
}
261