Context   B
last analyzed

Complexity

Total Complexity 42

Size/Duplication

Total Lines 220
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
dl 0
loc 220
rs 8.295
c 0
b 0
f 0
wmc 42
lcom 1
cbo 5

19 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A willUse() 0 11 3
A forVariable() 0 4 1
A whenCreating() 0 8 2
A forType() 0 8 2
A wrapWith() 0 4 1
A pickFactory() 0 17 4
A hasWrapper() 0 11 3
A invokeSetters() 0 13 2
A settersFor() 0 6 2
A wrappersFor() 0 4 1
A createDependencies() 0 16 4
B instantiateParameter() 0 21 5
A determineContext() 0 10 3
A invoke() 0 4 1
A preferFrom() 0 10 3
A cons() 0 6 1
A repository() 0 4 1
A create() 0 21 2

How to fix   Complexity   

Complex Class

Complex classes like Context 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 Context, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Koch Framework
5
 * Jens-André Koch © 2005 - onwards.
6
 *
7
 * This file is part of "Koch Framework".
8
 *
9
 * License: GNU/GPL v2 or any later version, see LICENSE file.
10
 *
11
 * This program is free software; you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by
13
 * the Free Software Foundation; either version 2 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23
 */
24
25
namespace Koch\DI\Engine;
26
27
use Koch\DI\AbstractLifecycle;
28
use Koch\DI\Exception\CannotFindImplementation;
29
use Koch\DI\Lifecycle\Factory;
30
use Koch\DI\Lifecycle\Value;
31
32
class Context
33
{
34
    private $parent;
35
    /*private $repository;*/
36
    private $registry  = [];
37
    private $variables = [];
38
    private $contexts  = [];
39
    private $types     = [];
40
    private $wrappers  = [];
41
42
    public function __construct($parent)
43
    {
44
        $this->parent = $parent;
45
    }
46
47
    public function willUse($preference)
48
    {
49
        if ($preference instanceof AbstractLifecycle) {
50
            $lifecycle = $preference;
51
        } elseif (true === is_object($preference)) {
52
            $lifecycle = new Value($preference);
53
        } else {
54
            $lifecycle = new Factory($preference);
55
        }
56
        array_unshift($this->registry, $lifecycle);
57
    }
58
59
    /**
60
     * @param string $name
61
     */
62
    public function forVariable($name)
63
    {
64
        return $this->variables[$name] = new Variable($this);
65
    }
66
67
    /**
68
     * @param string $type
69
     */
70
    public function whenCreating($type)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
71
    {
72
        if (false === isset($this->contexts[$type])) {
73
            $this->contexts[$type] = new self($this);
74
        }
75
76
        return $this->contexts[$type];
77
    }
78
79
    /**
80
     * @param string $type
81
     */
82
    public function forType($type)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
83
    {
84
        if (false === isset($this->types[$type])) {
85
            $this->types[$type] = new Type();
86
        }
87
88
        return $this->types[$type];
89
    }
90
91
    public function wrapWith($type)
92
    {
93
        array_push($this->wrappers, $type);
94
    }
95
96
    public function create($type, $nesting = [])
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
97
    {
98
        $lifecycle = $this->pickFactory($type, $this->repository()->candidatesFor($type));
99
        $context   = $this->determineContext($lifecycle->class);
100
        $wrapper   = $context->hasWrapper($type, $nesting);
101
102
        if ($wrapper) {
103
            return $this->create($wrapper, $this->cons($wrapper, $nesting));
104
        }
105
106
        $instance = $lifecycle->instantiate(
107
            $context->createDependencies(
108
                $this->repository()->getConstructorParameters($lifecycle->class),
109
                $this->cons($lifecycle->class, $nesting)
110
            )
111
        );
112
113
        $this->invokeSetters($context, $nesting, $lifecycle->class, $instance);
114
115
        return $instance;
116
    }
117
118
    public function pickFactory($type, $candidates)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
119
    {
120
        if (count($candidates) === 0) {
121
            throw new CannotFindImplementation($type);
122
        }
123
124
        $preference = $this->preferFrom($candidates);
125
        if ($preference) {
126
            return $preference;
127
        }
128
129
        if (count($candidates) === 1) {
130
            return new Factory($candidates[0]);
131
        }
132
133
        return $this->parent->pickFactory($type, $candidates);
134
    }
135
136
    public function hasWrapper($type, $already_applied)
0 ignored issues
show
Coding Style introduced by
$already_applied does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
137
    {
138
        $wrappers = $this->wrappersFor($type);
139
        foreach ($wrappers as $wrapper) {
140
            if (false === in_array($wrapper, $already_applied, true)) {
0 ignored issues
show
Coding Style introduced by
$already_applied does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
141
                return $wrapper;
142
            }
143
        }
144
145
        return false;
146
    }
147
148
    private function invokeSetters($context, $nesting, $class, $instance)
149
    {
150
        foreach ($context->settersFor($class) as $setter) {
151
            $context->invoke(
152
                $instance,
153
                $setter,
154
                $context->createDependencies(
155
                    $this->repository()->getParameters($class, $setter),
156
                    $this->cons($class, $nesting)
157
                )
158
            );
159
        }
160
    }
161
162
    private function settersFor($class)
163
    {
164
        $setters = isset($this->types[$class]) ? $this->types[$class]->setters : [];
165
166
        return array_values(array_keys(array_flip(array_merge($setters, $this->parent->settersFor($class)))));
167
    }
168
169
    public function wrappersFor($type)
170
    {
171
        return array_values(array_merge($this->wrappers, $this->parent->wrappersFor($type)));
172
    }
173
174
    public function createDependencies($parameters, $nesting)
175
    {
176
        $values = [];
177
        foreach ($parameters as $parameter) {
178
            try {
179
                $values[] = $this->instantiateParameter($parameter, $nesting);
180
            } catch (\Exception $e) {
181
                if ($parameter->isOptional()) {
182
                    break;
183
                }
184
                throw $e;
185
            }
186
        }
187
188
        return $values;
189
    }
190
191
    private function instantiateParameter($parameter, $nesting)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
192
    {
193
        $hint = $parameter->getClass();
194
        if ($hint) {
195
            return $this->create($hint->getName(), $nesting);
196
        }
197
198
        if (true === isset($this->variables[$parameter->getName()])) {
199
            if ($this->variables[$parameter->getName()]->preference instanceof AbstractLifecycle) {
200
                return $this->variables[$parameter->getName()]->preference->instantiate([]);
201
            }
202
203
            if (false === is_string($this->variables[$parameter->getName()]->preference)) {
204
                return $this->variables[$parameter->getName()]->preference;
205
            }
206
207
            return $this->create($this->variables[$parameter->getName()]->preference, $nesting);
208
        }
209
210
        return $this->parent->instantiateParameter($parameter, $nesting);
211
    }
212
213
    private function determineContext($class)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
214
    {
215
        foreach ($this->contexts as $type => $context) {
216
            if (true === $this->repository()->isSupertype($class, $type)) {
217
                return $context;
218
            }
219
        }
220
221
        return $this;
222
    }
223
224
    private function invoke($instance, $method, $arguments)
225
    {
226
        call_user_func_array([$instance, $method], $arguments);
227
    }
228
229
    private function preferFrom($candidates)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
230
    {
231
        foreach ($this->registry as $preference) {
232
            if (true === $preference->isOneOf($candidates)) {
233
                return $preference;
234
            }
235
        }
236
237
        return false;
238
    }
239
240
    private function cons($head, $tail)
241
    {
242
        array_unshift($tail, $head);
243
244
        return $tail;
245
    }
246
247
    public function repository()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
248
    {
249
        return $this->parent->repository();
250
    }
251
}
252