-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathContainer.php
210 lines (180 loc) · 5.56 KB
/
Container.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
<?php
/**
* This file is part of the League\Di library.
*
* (c) Don Gilbert <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Di;
/**
* Container class
*
* @author Don Gilbert <[email protected]>
*/
class Container
{
/**
* Array of container bindings.
*
* @var $array
*/
protected $bindings = array();
/**
* The parent Container object.
*
* @var Container
*/
protected $parent;
/**
* Constructor
*
* @param Container $parent Container
*/
public function __construct(Container $parent = null)
{
$this->parent = $parent;
}
/**
* Create a child Container with a new property scope that
* that has the ability to access the parent scope when resolving.
*
* @return Container
*/
public function createChild()
{
return new static($this);
}
/**
* Method to bind a concrete class to an abstract class or interface.
*
* @param string $abstract Class to bind.
* @param \Closure|string $concrete Concrete definition to bind to $abstract.
*
* @return Definition|\Closure The concrete class for adding method calls / constructor arguments if desired.
*/
public function bind($abstract, $concrete = null)
{
if (is_null($concrete)) {
$concrete = $abstract;
}
if (is_string($concrete)) {
$concrete = new Definition($this, $concrete);
}
$this->bindings[$abstract] = $concrete;
return $concrete;
}
/**
* Checks to see if a binding key has been bound in current Container.
* If not bound in the current Container, it will recursively search
* parent Container's until it finds the $binding.
*
* @param string $binding The binding to check.
* @return bool
*/
public function bound($binding)
{
return isset($this->bindings[$binding]) || (isset($this->parent) && $this->parent->bound($binding));
}
/**
* Build a concrete instance of a class.
*
* @param string $concrete The name of the class to buld.
*
* @return mixed The instantiated class.
* @throws \InvalidArgumentException
*/
public function build($concrete)
{
$reflection = new \ReflectionClass($concrete);
if (! $reflection->isInstantiable()) {
throw new \InvalidArgumentException(sprintf('Class %s is not instantiable.', $concrete));
}
$constructor = $reflection->getConstructor();
if (is_null($constructor)) {
return new $concrete;
}
$dependencies = $this->getDependencies($constructor);
return $reflection->newInstanceArgs($dependencies);
}
/**
* Extend an existing binding.
*
* @param string $binding The name of the binding to extend.
* @param \Closure $closure The function to use to extend the existing binding.
*
* @return void
* @throws \InvalidArgumentException
*/
public function extend($binding, \Closure $closure)
{
$rawObject = $this->getRaw($binding);
if (is_null($rawObject)) {
throw new \InvalidArgumentException(sprintf('Cannot extend %s because it has not yet been bound.', $binding));
}
$this->bind($binding, function ($container) use ($closure, $rawObject) {
return $closure($container, $rawObject($container));
});
}
/**
* Recursively build the dependency list for the provided method.
*
* @param \ReflectionMethod $method The method for which to obtain dependencies.
*
* @return array An array containing the method dependencies.
* @throws \InvalidArgumentException
*/
protected function getDependencies(\ReflectionMethod $method)
{
$dependencies = array();
foreach ($method->getParameters() as $param) {
$dependency = $param->getClass();
if (is_null($dependency)) {
if ($param->isOptional()) {
$dependencies[] = $param->getDefaultValue();
continue;
}
} else {
$dependencies[] = $this->resolve($dependency->name);
continue;
}
throw new \InvalidArgumentException('Could not resolve ' . $param->getName());
}
return $dependencies;
}
/**
* Get the raw object prior to resolution.
*
* @param string $binding The $binding key to get the raw value from.
*
* @return Definition|\Closure Value of the $binding.
*/
public function getRaw($binding)
{
if (isset($this->bindings[$binding])) {
return $this->bindings[$binding];
} elseif (isset($this->parent)) {
return $this->parent->getRaw($binding);
}
return null;
}
/**
* Resolve the given binding.
*
* @param string $binding The binding to resolve.
*
* @return mixed The results of invoking the binding callback.
*/
public function resolve($binding)
{
$rawObject = $this->getRaw($binding);
// If the abstract is not registered, do it now for easy resolution.
if (is_null($rawObject)) {
// Pass $binding to both so it doesn't need to check if null again.
$this->bind($binding, $binding);
$rawObject = $this->getRaw($binding);
}
return $rawObject($this);
}
}