1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Zewa; |
4
|
|
|
|
5
|
|
|
class DIContainer |
6
|
|
|
{ |
7
|
|
|
//So.. I'm storing dependencies so they can be retrived for later collection |
8
|
|
|
//without re-instantiating.. we should make this optional some way? |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
* @var array $dependencies |
12
|
|
|
* @var Config $dependencies['Config'] |
13
|
|
|
*/ |
14
|
|
|
private $dependencies = []; |
15
|
|
|
private $callbacks = []; |
16
|
|
|
|
17
|
|
|
public function __construct(Config $config) |
18
|
|
|
{ |
19
|
|
|
$this->dependencies['\Zewa\DIContainer'] = $this; |
20
|
|
|
$this->dependencies['\Zewa\Config'] = $config; |
21
|
|
|
} |
22
|
|
|
|
23
|
|
|
private function isDependencyLoaded(string $class) : bool |
24
|
|
|
{ |
25
|
|
|
if (isset($this->dependencies[$class])) { |
26
|
|
|
return true; |
27
|
|
|
} |
28
|
|
|
|
29
|
|
|
return false; |
30
|
|
|
} |
31
|
|
|
|
32
|
|
|
//@TODO change class & service resolution ? |
33
|
|
|
public function resolve($class, $share = false) |
34
|
|
|
{ |
35
|
|
|
if ($this->isDependencyLoaded($class)) { |
36
|
|
|
return $this->dependencies[$class]; |
37
|
|
|
} elseif (!empty($this->callbacks[$class])) { |
38
|
|
|
$share = $this->callbacks[$class]['share'] === true || $share === true ? true : false; |
39
|
|
|
//pass an instance of $this to the factory for dependency injection on cached classes |
40
|
|
|
$dependency = $this->callbacks[$class]['factory']($this); |
41
|
|
|
if ($share === true) { |
42
|
|
|
$this->dependencies[$class] = $dependency; |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
return $dependency; |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
// Reflect on the $class |
49
|
|
|
$reflectionClass = new \ReflectionClass($class); |
50
|
|
|
|
51
|
|
|
// Fetch the constructor (instance of ReflectionMethod) |
52
|
|
|
$constructor = $reflectionClass->getConstructor(); |
53
|
|
|
|
54
|
|
|
// If there is no constructor, there is no |
55
|
|
|
// dependencies, which means that our job is done. |
56
|
|
View Code Duplication |
if (! $constructor) { |
|
|
|
|
57
|
|
|
$dependency = new $class; |
58
|
|
|
if ($share === true) { |
59
|
|
|
$this->dependencies[$class] = $dependency; |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
return $dependency; |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
// Fetch the arguments from the constructor |
66
|
|
|
// (collection of ReflectionParameter instances) |
67
|
|
|
$params = $constructor->getParameters(); |
68
|
|
|
|
69
|
|
|
// If there is a constructor, but no dependencies, |
70
|
|
|
// our job is done. |
71
|
|
View Code Duplication |
if (count($params) === 0) { |
|
|
|
|
72
|
|
|
$dependency = new $class; |
73
|
|
|
if ($share === true) { |
74
|
|
|
$this->dependencies[$class] = $dependency; |
75
|
|
|
} |
76
|
|
|
return $dependency; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
// This is were we store the dependencies |
80
|
|
|
$newInstanceParams = []; |
81
|
|
|
|
82
|
|
|
// Loop over the constructor arguments |
83
|
|
View Code Duplication |
foreach ($params as $param) { |
|
|
|
|
84
|
|
|
// Here we should perform a bunch of checks, such as: |
85
|
|
|
// isArray(), isCallable(), isDefaultValueAvailable() |
|
|
|
|
86
|
|
|
// isOptional() etc. |
87
|
|
|
|
88
|
|
|
// For now, we just check to see if the argument is |
89
|
|
|
// a class, so we can instantiate it, |
90
|
|
|
// otherwise we just pass null. |
91
|
|
|
if (is_null($param->getClass())) { |
92
|
|
|
$newInstanceParams[] = null; |
93
|
|
|
continue; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
// This is where 'the magic happens'. We resolve each |
97
|
|
|
// of the dependencies, by recursively calling the |
98
|
|
|
// resolve() method. |
99
|
|
|
// At one point, we will reach the bottom of the |
100
|
|
|
// nested dependencies we need in order to instantiate |
101
|
|
|
// the class. |
102
|
|
|
$newInstanceParams[] = $this->resolve( |
103
|
|
|
"\\" . $param->getClass()->getName() |
104
|
|
|
); |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
// Return the reflected class, instantiated with all its |
108
|
|
|
// dependencies (this happens once for all the |
109
|
|
|
// nested dependencies). |
110
|
|
|
$dependency = $reflectionClass->newInstanceArgs( |
111
|
|
|
$newInstanceParams |
112
|
|
|
); |
113
|
|
|
|
114
|
|
|
if ($share === true) { |
115
|
|
|
$this->dependencies[$class] = $dependency; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
return $dependency; |
119
|
|
|
} |
120
|
|
|
} |
121
|
|
|
|
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.