GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

DiMaria   B
last analyzed

Complexity

Total Complexity 52

Size/Duplication

Total Lines 270
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%
Metric Value
wmc 52
lcom 1
cbo 2
dl 0
loc 270
ccs 115
cts 115
cp 1
rs 7.9487

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A setAlias() 0 8 1
A setInjection() 0 5 1
A setParams() 0 5 1
A setPreference() 0 5 1
A setShared() 0 5 1
A set() 0 5 1
A setFactory() 0 5 1
B has() 0 4 5
A getClassName() 0 10 3
A getObject() 0 16 4
A getCallback() 0 18 4
A generateCallback() 0 15 3
B getMethodInfo() 0 16 7
B getParameters() 0 19 6
A get() 0 14 4
A create() 0 7 3
B determineParameter() 0 16 5

How to fix   Complexity   

Complex Class

Complex classes like DiMaria often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DiMaria, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace DD;
3
4
use DD\DiMaria\Exception\ContainerException;
5
use DD\DiMaria\Exception\NotFoundException;
6
use Interop\Container\ContainerInterface;
7
8
/**
9
 * DiMaria Dependency injector
10
 */
11
class DiMaria implements ContainerInterface
12
{
13
    protected $aliases = [];
14
    protected $factory = [];
15
    protected $injections = [];
16
    protected $params = [];
17
    protected $preferences = [];
18
    protected $shared = [];
19
    protected $sharedInstance = [];
20
21 81
    public function __construct()
22
    {
23 81
        $this->sharedInstance[__CLASS__] = $this;
24 81
    }
25
26
    /**
27
     * Alias a class/interface/alias to a string
28
     * @param  string $alias   the name of the alias
29
     * @param  string $class   the name of the class
30
     * @param  array  $params  a key/value array of parameter names and values
31
     * @return self
32
     */
33 24
    public function setAlias(string $alias, string $class, array $params = []): self
34
    {
35 24
        $this->aliases[$alias] = [
36 24
            'class' => $class,
37 24
            'params' => $params
38
        ];
39 24
        return $this;
40
    }
41
42
    /**
43
     * Set rule to call a method after constructing a class
44
     * @param  string $class   the name of the class
45
     * @param  string $method  the name of the method
46
     * @param  array  $params  a key/value array of parameter names and values
47
     * @return self
48
     */
49 18
    public function setInjection(string $class, string $method, array $params = []): self
50
    {
51 18
        $this->injections[$class][$method][] = $params;
52 18
        return $this;
53
    }
54
55
    /**
56
     * Set parameters of a class
57
     * @param  string $class   the name of the class
58
     * @param  array  $params  a key/value array of parameter names and values
59
     * @return self
60
     */
61 22
    public function setParams(string $class, array $params): self
62
    {
63 22
        $this->params[$class] = $params + ($this->params[$class] ?? []);
64 22
        return $this;
65
    }
66
67
    /**
68
     * Set a preferred implementation of a class/interface
69
     * @param  string $alias  the name of the alias
70
     * @param  string $class  the name of the class/interface
71
     * @return self
72
     */
73 3
    public function setPreference(string $alias, string $class): self
74
    {
75 3
        $this->preferences[$alias] = $class;
76 3
        return $this;
77
    }
78
79
    /**
80
     * Set an instance to always return a shared or new instance, regardless of method used
81
     * @param  string $class     the name of class/alias
82
     * @param  bool   $isShared  true will always return a shared instance. false will always return a new instance
83
     * @return self
84
     */
85 6
    public function setShared(string $class, bool $isShared = true): self
86
    {
87 6
        $this->shared[$class] = $isShared;
88 6
        return $this;
89
    }
90
91
    /**
92
     * Set a value. Retrievable with the get method
93
     * @param  string $key    a name to retrieve the value by
94
     * @param  mixed  $value  the content to store against the key
95
     * @return self
96
     */
97 4
    public function set(string $key, $value): self
98
    {
99 4
        $this->sharedInstance[$key] = $value;
100 4
        return $this;
101
    }
102
103
    /**
104
     * Set a factory. Retrievable with the create method. The get method will fetch but also cache the response
105
     * @param  string   $key      a name to retrieve the content by
106
     * @param  callable $factory  a callable to be invoked when fetched
107
     * @return self
108
     */
109 8
    public function setFactory(string $key, callable $factory): self
110
    {
111 8
        $this->factory[$key] = $factory;
112 8
        return $this;
113
    }
114
115
    /**
116
     * Returns true if DiMaria can return an entry for the given string. Returns false otherwise.
117
     * @param  string $class  identifier of the entry to look for
118
     * @return boolean
119
     */
120 75
    public function has($class): bool
121
    {
122 75
        return class_exists($class) ?: isset($this->aliases[$class]) ?: isset($this->preferences[$class]) ?: isset($this->sharedInstance[$class]) ?: isset($this->factory[$class]);
123
    }
124
125
    /**
126
     * Get an instance of a class
127
     * @param  string $class       the name of class/alias to create
128
     * @param  array $params       a key/value array of parameter names and values
129
     * @throws NotFoundException   when no entry was found
130
     * @throws ContainerException  if class could not be constructed
131
     * @return mixed               an instance of the class requested
132
     */
133 72
    public function get($class, array $params = [])
134
    {
135 72
        if (isset($this->shared[$class]) && !$this->shared[$class]) {
136 1
            return $this->create($class, $params);
137
        }
138 71
        if (isset($this->sharedInstance[$class])) {
139 7
            return $this->sharedInstance[$class];
140
        }
141 67
        $class = $this->getClassName($class);
142
143 65
        $object = $this->getObject($class, $params);
144 61
        $this->sharedInstance[$class] = $object;
145 61
        return $object;
146
    }
147
148
    /**
149
     * Get a new instance of a class
150
     * @param  string $class       the name of class/alias to create
151
     * @param  array $params       a key/value array of parameter names and values
152
     * @throws NotFoundException   if no entry was found
153
     * @throws ContainerException  if class could not be constructed
154
     * @return mixed               an instance of the class requested
155
     */
156 33
    public function create(string $class, array $params = [])
157
    {
158 33
        if (isset($this->shared[$class]) && $this->shared[$class]) {
159 4
            return $this->get($class, $params);
160
        }
161 29
        return $this->getObject($this->getClassName($class), $params);
162
    }
163
164 72
    protected function getClassName(string $class): string
165
    {
166 72
        if (!$this->has($class)) {
167 2
            throw new NotFoundException('Class or alias ' . $class . ' does not exist');
168
        }
169 70
        while ($preference = $this->preferences[$class] ?? false) {
170 3
            $class = $preference;
171
        }
172 70
        return $class;
173
    }
174
175 70
    protected function getObject(string $class, array $params)
176
    {
177 70
        $originalClass = $class;
178 70
        while ($alias = $this->aliases[$class] ?? false) {
179 22
            $params = $params + $alias['params'];
180 22
            $class = $alias['class'];
181
        }
182
        try {
183 70
            $callback = $this->factory[$originalClass] ?? $this->factory[$originalClass] = $this->getCallback($class, $originalClass);
184 69
            return $callback($params);
185 5
        } catch (\Exception $e) {
186 4
            throw new ContainerException($e->getMessage(), $e->getCode(), $e);
187 1
        } catch (\Error $e) {
188 1
            throw new ContainerException($e->getMessage(), $e->getCode(), $e);
189
        }
190
    }
191
192 67
    protected function getCallback(string $class, string $originalClass): callable
193
    {
194 67
        $callback = $this->generateCallback($class);
195 66
        if (isset($this->injections[$originalClass])) {
196 17
            foreach ($this->injections[$originalClass] as $method => $instance) {
197 17
                $methodInfo = $this->getMethodInfo(new \ReflectionMethod($class, $method));
198 17
                foreach ($instance as $methodParameters) {
199 17
                    $methodParams = $this->getParameters($methodInfo, $methodParameters);
200
                    $callback = function ($params) use ($callback, $method, $methodParams) {
201 17
                        $object = $callback($params);
202 17
                        $object->$method(...$methodParams);
203 17
                        return $object;
204 17
                    };
205
                };
206
            }
207
        }
208 66
        return $callback;
209
    }
210
211 67
    protected function generateCallback(string $class): callable
212
    {
213 67
        $constructor = (new \ReflectionClass($class))->getConstructor();
214 66
        if (!$constructor || !$constructor->getNumberOfParameters()) {
215
            return function () use ($class) {
216 33
                return new $class;
217 33
            };
218
        }
219 44
        $constructorInfo = $this->getMethodInfo($constructor);
220 44
        $predefinedParams = $this->params[$class] ?? [];
221
222 44
        return function ($params) use ($class, $constructorInfo, $predefinedParams) {
223 44
            return new $class(...$this->getParameters($constructorInfo, $params + $predefinedParams));
224 44
        };
225
    }
226
227 59
    protected function getMethodInfo(\ReflectionMethod $method): array
228
    {
229 59
        $paramInfo = [];
230 59
        foreach ($method->getParameters() as $param) {
231 56
            $paramType = $param->hasType() ? $param->getType() : null;
232 56
            $paramType = $paramType ? $paramType->isBuiltin() ? null : $paramType->__toString() : null;
233 56
            $paramInfo[$param->name] = [
234 56
                'name' => $param->name,
235 56
                'optional' => $param->isOptional(),
236 56
                'default' => $param->isOptional() ? ($param->isVariadic() ? null : $param->getDefaultValue()) : null,
237 56
                'variadic' => $param->isVariadic(),
238 56
                'type' => $paramType,
239
            ];
240
        }
241 59
        return $paramInfo;
242
    }
243
244 59
    protected function getParameters(array $methodInfo, array $params): array
245
    {
246 59
        $parameters = [];
247 59
        foreach ($methodInfo as $param) {
248 56
            if (isset($params[$param['name']])) {
249 42
                array_push($parameters, ...$this->determineParameter($params[$param['name']], $param['variadic']));
250 30
            } elseif ($param['optional']) {
251 26
                if ($param['variadic']) {
252 4
                    break;
253
                }
254 22
                $parameters[] = $param['default'];
255 15
            } elseif ($param['type']) {
256 12
                $parameters[] = $this->create($param['type']);
257
            } else {
258 54
                throw new ContainerException('Required parameter $' . $param['name'] . ' is missing');
259
            }
260
        }
261 56
        return $parameters;
262
    }
263
264 42
    protected function determineParameter($param, bool $isVariadic): array
265
    {
266 42
        if (!is_array($param)) {
267 37
            return [$param];
268
        }
269 18
        if (isset($param['instanceOf'])) {
270 14
            return [$this->create($param['instanceOf'], $param['params'] ?? [])];
271
        }
272 9
        foreach ($param as $key => $val) {
273 9
            $param[$key] = $this->determineParameter($val, false)[0];
274
        }
275 9
        if (!$isVariadic) {
276 2
            return [$param];
277
        }
278 7
        return $param;
279
    }
280
}
281