1
|
|
|
<?php |
2
|
|
|
namespace Michaels\Manager\Traits; |
3
|
|
|
|
4
|
|
|
use Michaels\Manager\Contracts\IocContainerInterface; |
5
|
|
|
use Michaels\Manager\Exceptions\ItemNotFoundException; |
6
|
|
|
use Michaels\Manager\Messages\NoItemFoundMessage; |
7
|
|
|
|
8
|
|
|
/** |
9
|
|
|
* Manages complex, nested data |
10
|
|
|
* |
11
|
|
|
* @implements Michaels\Manager\Contracts\ManagesItemsInterface |
12
|
|
|
* @package Michaels\Manager |
13
|
|
|
*/ |
14
|
|
|
trait ManagesIocTrait |
15
|
|
|
{ |
16
|
|
|
use DependsOnManagesItemsTrait; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Returns the request object with all dependencies |
20
|
|
|
* |
21
|
|
|
* Overrides the `get()` method on ManagesItemsTrait |
22
|
|
|
* Use getRaw() to return the raw value |
23
|
|
|
* |
24
|
|
|
* string Full class name for a new object each time |
25
|
|
|
* callable Factory to create new object (passed manager) |
26
|
|
|
* object The exact object to be returned |
27
|
|
|
* |
28
|
|
|
* @param string $alias |
29
|
|
|
* @param string|mixed $fallback |
30
|
|
|
* @return mixed |
31
|
|
|
* @throws \Exception |
32
|
|
|
*/ |
33
|
|
|
public function get($alias, $fallback = '_michaels_no_fallback') |
34
|
|
|
{ |
35
|
|
|
// If this is a link, just go back to the master |
36
|
|
|
$link = $this->getIfExists("$alias"); |
37
|
|
|
if (is_string($link) && strpos($link, '_michaels_link_') !== false) { |
38
|
|
|
return $this->get(str_replace('_michaels_link_', '', $link)); |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
// Otherwise, continue |
42
|
|
|
$shared = $this->getIfExists("_singletons.$alias"); |
43
|
|
|
|
44
|
|
|
if ($shared instanceof NoItemFoundMessage) { |
45
|
|
|
// This is not a shared item. We want a new one each time |
46
|
|
|
return $this->produceDependency($alias, $fallback); |
47
|
|
|
} else { |
48
|
|
|
// This is shared, and object has already been cached |
49
|
|
|
if (is_object($shared)) { |
50
|
|
|
return $shared; |
51
|
|
|
|
52
|
|
|
// This is shared, but we must produce and cache it |
53
|
|
|
} else { |
54
|
|
|
$object = $this->produceDependency($alias, $fallback); |
55
|
|
|
$this->set("_singletons.$alias", $object); |
56
|
|
|
return $object; |
57
|
|
|
} |
58
|
|
|
} |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Alias of get() for backwards comparability |
63
|
|
|
* |
64
|
|
|
* @param string $alias |
65
|
|
|
* @param string|mixed $fallback |
66
|
|
|
* @return mixed |
67
|
|
|
* @throws \Exception |
68
|
|
|
*/ |
69
|
|
|
public function fetch($alias, $fallback = '_michaels_no_fallback') |
70
|
|
|
{ |
71
|
|
|
return $this->get($alias, $fallback); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* Adds a dependency to the manager |
76
|
|
|
* |
77
|
|
|
* $factory can be a: |
78
|
|
|
* string Full class name for a new object each time |
79
|
|
|
* callable Factory to create new object (passed manager) |
80
|
|
|
* object The exact object to be returned |
81
|
|
|
* |
82
|
|
|
* @param string $alias |
83
|
|
|
* @param callable|string|object $factory |
84
|
|
|
* @param array $declared |
85
|
|
|
* @return $this |
86
|
|
|
*/ |
87
|
|
|
public function add($alias, $factory = null, array $declared = null) |
88
|
|
|
{ |
89
|
|
|
// Setup links, if necessary |
90
|
|
|
if (is_array($alias)) { |
91
|
|
|
$links = $alias; |
92
|
|
|
$alias = $alias[0]; |
93
|
|
|
unset($links[0]); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
$this->set("$alias", $factory); |
|
|
|
|
97
|
|
|
|
98
|
|
|
// Setup any declared dependencies |
99
|
|
|
if ($declared) { |
100
|
|
|
$this->set("_declarations.$alias", $declared); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
// Add Links |
104
|
|
|
if (!empty($links)) { |
105
|
|
|
foreach ($links as $link) { |
106
|
|
|
$this->set("$link", "_michaels_link_$alias"); |
107
|
|
|
} |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
return $this; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Turns a dependency into a singleton. |
115
|
|
|
* @param $alias |
116
|
|
|
* @return mixed |
117
|
|
|
*/ |
118
|
|
|
public function share($alias) |
119
|
|
|
{ |
120
|
|
|
$this->set("_singletons.$alias", true); |
121
|
|
|
return $this; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Add a pipeline to to the que |
126
|
|
|
* @param $alias |
127
|
|
|
* @param $pipeline |
128
|
|
|
* @return $this |
129
|
|
|
*/ |
130
|
|
|
public function setup($alias, $pipeline) |
131
|
|
|
{ |
132
|
|
|
$this->set("_pipelines.$alias", $pipeline); |
133
|
|
|
return $this; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* Produces the object from an alias |
138
|
|
|
* @param string $alias |
139
|
|
|
* @param mixed|string $fallback |
140
|
|
|
* @return mixed |
141
|
|
|
* @throws ItemNotFoundException |
142
|
|
|
* @throws \Exception |
143
|
|
|
*/ |
144
|
|
|
protected function produceDependency($alias, $fallback = '_michaels_no_fallback') |
145
|
|
|
{ |
146
|
|
|
/* Get the registered factory (string, closure, object, container, NoItemFoundMessage) */ |
147
|
|
|
$factory = $this->getIfExists("$alias"); |
148
|
|
|
|
149
|
|
|
/* Manage not founds and fallback */ |
150
|
|
|
if ($factory instanceof NoItemFoundMessage) { |
151
|
|
|
if ($fallback !== '_michaels_no_fallback') { |
152
|
|
|
return $fallback; |
153
|
|
|
} elseif (class_exists($alias)) { |
154
|
|
|
return new $alias; |
155
|
|
|
} else { |
156
|
|
|
throw new ItemNotFoundException("$alias not found"); |
157
|
|
|
} |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/* Get any declared dependencies */ |
161
|
|
|
$declared = $this->getIfExists("_declarations.$alias"); |
162
|
|
|
$dependencies = []; |
163
|
|
|
|
164
|
|
|
// Now setup those dependencies into an array |
165
|
|
|
if (!$declared instanceof NoItemFoundMessage) { |
166
|
|
|
$dependencies = array_map(function(&$value) use ($alias) { |
167
|
|
|
if (is_string($value) && $this->exists("$alias")) { |
168
|
|
|
return $this->get($value); |
169
|
|
|
} |
170
|
|
|
return $value; |
171
|
|
|
}, $declared); |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
/* Produce the object itself */ |
175
|
|
|
if ($factory instanceof IocContainerInterface) { |
176
|
|
|
$object = $factory->get($alias); |
177
|
|
|
|
178
|
|
|
} elseif (is_string($factory)) { |
179
|
|
|
$class = new \ReflectionClass($factory); |
180
|
|
|
$object = $class->newInstanceArgs($dependencies); |
181
|
|
|
|
182
|
|
|
} elseif (is_callable($factory)) { |
183
|
|
|
array_unshift($dependencies, $this); |
184
|
|
|
$object = call_user_func_array($factory, $dependencies); |
185
|
|
|
|
186
|
|
|
} elseif (is_object($factory)) { |
187
|
|
|
$object = $factory; |
188
|
|
|
|
189
|
|
|
if (method_exists($object, "needs")) { |
190
|
|
|
call_user_func_array([$object, 'needs'], $dependencies); |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
} else { |
194
|
|
|
throw new \Exception("`get()` can only return from strings, callables, or objects"); |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
/* Run the object through the pipeline, if desired */ |
198
|
|
|
$pipeline = $this->getIfExists("_pipelines.$alias"); |
199
|
|
|
|
200
|
|
|
if (!$pipeline instanceof NoItemFoundMessage) { |
201
|
|
|
/** @var \Closure $pipeline */ |
202
|
|
|
$object = $pipeline($object, $this); |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
/* Return the final object */ |
206
|
|
|
return $object; |
207
|
|
|
} |
208
|
|
|
} |
209
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.