1 | <?php |
||
12 | class ContainerFactory |
||
13 | { |
||
14 | /** |
||
15 | * @var mixed[] map where component name => value |
||
16 | */ |
||
17 | protected $values = []; |
||
18 | |||
19 | /** |
||
20 | * @var callable[] map where component name => factory function |
||
21 | */ |
||
22 | protected $factory = []; |
||
23 | |||
24 | /** |
||
25 | * @var array map where component name => mixed list/map of parameter names |
||
26 | */ |
||
27 | protected $factory_map = []; |
||
28 | |||
29 | /** |
||
30 | * @var (callable[])[] map where component name => list of configuration functions |
||
31 | */ |
||
32 | protected $config = []; |
||
33 | |||
34 | /** |
||
35 | * @var array map where component name => mixed list/map of parameter names |
||
36 | */ |
||
37 | protected $config_map = []; |
||
38 | |||
39 | 1 | public function __construct() |
|
41 | |||
42 | /** |
||
43 | * Register a component for dependency injection. |
||
44 | * |
||
45 | * There are numerous valid ways to register components. |
||
46 | * |
||
47 | * * `register(Foo::class)` registers a component by it's class-name, and will try to |
||
48 | * automatically resolve all of it's constructor arguments. |
||
49 | * |
||
50 | * * `register(Foo::class, ['bar'])` registers a component by it's class-name, and will |
||
51 | * use `'bar'` as the first constructor argument, and try to resolve the rest. |
||
52 | * |
||
53 | * * `register(Foo::class, [$container->ref(Bar::class)])` creates a boxed reference to |
||
54 | * a registered component `Bar` and provides that as the first argument. |
||
55 | * |
||
56 | * * `register(Foo::class, ['bat' => 'zap'])` registers a component by it's class-name |
||
57 | * and will use `'zap'` for the constructor argument named `$bat`, and try to resolve |
||
58 | * any other arguments. |
||
59 | * |
||
60 | * * `register(Bar::class, Foo::class)` registers a component `Foo` under another name |
||
61 | * `Bar`, which might be an interface or an abstract class. |
||
62 | * |
||
63 | * * `register(Bar::class, Foo::class, ['bar'])` same as above, but uses `'bar'` as the |
||
64 | * first argument. |
||
65 | * |
||
66 | * * `register(Bar::class, Foo::class, ['bat' => 'zap'])` same as above, but, well, guess. |
||
67 | * |
||
68 | * * `register(Bar::class, function (Foo $foo) { return new Bar(...); })` registers a |
||
69 | * component with a custom creation function. |
||
70 | * |
||
71 | * * `register(Bar::class, function ($name) { ... }, [$container->ref('db.name')]);` |
||
72 | * registers a component creation function with a reference to a component "db.name" |
||
73 | * as the first argument. |
||
74 | * |
||
75 | * In effect, you can think of `$func` as being an optional argument. |
||
76 | * |
||
77 | * The provided parameter values may include any `BoxedValueInterface`, such as the boxed |
||
78 | * component referenced created by {@see Container::ref()} - these will be unboxed as late |
||
79 | * as possible. |
||
80 | * |
||
81 | * @param string $name component name |
||
82 | * @param callable|mixed|mixed[]|null $func_or_map_or_type creation function or class-name, or, if the first |
||
83 | * argument is a class-name, a map of constructor arguments |
||
84 | * @param mixed|mixed[] $map mixed list/map of parameter values (and/or boxed values) |
||
85 | * |
||
86 | * @return void |
||
87 | * |
||
88 | * @throws ContainerException |
||
89 | */ |
||
90 | 1 | public function register($name, $func_or_map_or_type = null, $map = []) |
|
121 | |||
122 | /** |
||
123 | * Directly inject a component into the container - use this to register components that |
||
124 | * have already been created for some reason; for example, the Composer ClassLoader. |
||
125 | * |
||
126 | * @param string $name component name |
||
127 | * @param mixed $value |
||
128 | * |
||
129 | * @return void |
||
130 | * |
||
131 | * @throws ContainerException |
||
132 | */ |
||
133 | 1 | public function set($name, $value) |
|
139 | |||
140 | /** |
||
141 | * Register a component as an alias of another registered component. |
||
142 | * |
||
143 | * @param string $new_name new component name |
||
144 | * @param string $ref_name referenced existing component name |
||
145 | */ |
||
146 | public function alias($new_name, $ref_name) |
||
152 | |||
153 | /** |
||
154 | * Register a configuration function, which will be applied as late as possible, e.g. |
||
155 | * on first use of the component. For example: |
||
156 | * |
||
157 | * $factory->configure('stack', function (MiddlewareStack $stack) { |
||
158 | * $stack->push(new MoreAwesomeMiddleware()); |
||
159 | * }); |
||
160 | * |
||
161 | * The given configuration function should include the configured component as the |
||
162 | * first parameter to the closure, but may include any number of parameters, which |
||
163 | * will be resolved and injected. |
||
164 | * |
||
165 | * The first argument (component name) is optional - that is, the name can be inferred |
||
166 | * from a type-hint on the first parameter of the closure, so the following will work: |
||
167 | * |
||
168 | * $factory->register(PageLayout::class); |
||
169 | * |
||
170 | * $factory->configure(function (PageLayout $layout) { |
||
171 | * $layout->title = "Welcome"; |
||
172 | * }); |
||
173 | * |
||
174 | * In some cases, you may wish to fetch additional dependencies, by using additional |
||
175 | * arguments, and specifying how these should be resolved, e.g. using |
||
176 | * {@see Container::ref()} - for example: |
||
177 | * |
||
178 | * $factory->register("cache", FileCache::class); |
||
179 | * |
||
180 | * $factory->configure( |
||
181 | * "cache", |
||
182 | * function (FileCache $cache, $path) { |
||
183 | * $cache->setPath($path); |
||
184 | * }, |
||
185 | * ['path' => $container->ref('cache.path')] |
||
186 | * ); |
||
187 | * |
||
188 | * You can also use `configure()` to decorate objects, or manipulate (or replace) values: |
||
189 | * |
||
190 | * $factory->configure('num_kittens', function ($num_kittens) { |
||
191 | * return $num_kittens + 6; // add another litter |
||
192 | * }); |
||
193 | * |
||
194 | * In other words, if your closure returns something, the component will be replaced. |
||
195 | * |
||
196 | * @param string|callable $name_or_func component name |
||
197 | * (or callable, if name is left out) |
||
198 | * @param callable|mixed|mixed[] $func_or_map `function (Type $component, ...) : void` |
||
199 | * (or parameter values, if name is left out) |
||
200 | * @param mixed|mixed[] $map mixed list/map of parameter values and/or boxed values |
||
201 | * (or unused, if name is left out) |
||
202 | * |
||
203 | * @return void |
||
204 | * |
||
205 | * @throws ContainerException |
||
206 | */ |
||
207 | 1 | public function configure($name_or_func, $func_or_map = null, $map = []) |
|
238 | |||
239 | /** |
||
240 | * Creates a boxed reference to a component with a given name. |
||
241 | * |
||
242 | * You can use this in conjunction with `register()` to provide a component reference |
||
243 | * without expanding that reference until first use - for example: |
||
244 | * |
||
245 | * $factory->register(UserRepo::class, [$factory->ref('cache')]); |
||
246 | * |
||
247 | * This will reference the "cache" component and provide it as the first argument to the |
||
248 | * constructor of `UserRepo` - compared with using `$container->get('cache')`, this has |
||
249 | * the advantage of not actually activating the "cache" component until `UserRepo` is |
||
250 | * used for the first time. |
||
251 | * |
||
252 | * Another reason (besides performance) to use references, is to defer the reference: |
||
253 | * |
||
254 | * $factory->register(FileCache::class, ['root_path' => $factory->ref('cache.path')]); |
||
255 | * |
||
256 | * In this example, the component "cache.path" will be fetched from the container on |
||
257 | * first use of `FileCache`, giving you a chance to configure "cache.path" later. |
||
258 | * |
||
259 | * @param string $name component name |
||
260 | * |
||
261 | * @return BoxedReference component reference |
||
262 | */ |
||
263 | 1 | public function ref($name) |
|
267 | |||
268 | /** |
||
269 | * Add a packaged configuration (a "provider") to this container. |
||
270 | * |
||
271 | * @see ProviderInterface |
||
272 | * |
||
273 | * @param ProviderInterface $provider |
||
274 | * |
||
275 | * @return void |
||
276 | */ |
||
277 | 1 | public function add(ProviderInterface $provider) |
|
281 | |||
282 | /** |
||
283 | * Create and bootstrap a new `Container` instance |
||
284 | * |
||
285 | * @return Container |
||
286 | */ |
||
287 | 1 | public function createContainer() |
|
297 | } |
||
298 |