1: <?php declare(strict_types=1);
2:
3: namespace Salient\Contract\Container;
4:
5: use Psr\Container\ContainerInterface as PsrContainerInterface;
6: use Salient\Contract\Core\Chainable;
7: use Salient\Contract\Core\Instantiable;
8: use Salient\Contract\Core\Unloadable;
9:
10: /**
11: * A service container with contextual bindings
12: *
13: * If a service resolves to a new instance of a class that implements
14: * `ContainerAwareInterface`, the container is passed to its
15: * {@see ContainerAwareInterface::setContainer()} method.
16: *
17: * Then, if the resolved instance implements {@see ServiceAwareInterface}, its
18: * {@see ServiceAwareInterface::setService()} method is called.
19: *
20: * A service provider registered via {@see provider()} or {@see providers()} may
21: * also implement any combination of the following interfaces:
22: *
23: * - {@see SingletonInterface} to be instantiated once per container
24: * - {@see HasServices} to specify which of its interfaces are services to
25: * register with the container
26: * - {@see HasBindings} to bind additional services to the container
27: * - {@see HasContextualBindings} to bind services to the container that only
28: * apply in the context of the provider
29: *
30: * {@see SingletonInterface} is ignored if a lifetime other than
31: * {@see ServiceLifetime::INHERIT} is given when the service provider is
32: * registered.
33: *
34: * @api
35: */
36: interface ContainerInterface extends
37: PsrContainerInterface,
38: Chainable,
39: Instantiable,
40: Unloadable
41: {
42: /**
43: * Creates a new service container
44: */
45: public function __construct();
46:
47: /**
48: * Check if the global container is set
49: */
50: public static function hasGlobalContainer(): bool;
51:
52: /**
53: * Get the global container, creating it if necessary
54: */
55: public static function getGlobalContainer(): ContainerInterface;
56:
57: /**
58: * Set or unset the global container
59: */
60: public static function setGlobalContainer(?ContainerInterface $container): void;
61:
62: /**
63: * Apply the contextual bindings of a service to a copy of the container
64: *
65: * @param class-string $id
66: * @return static
67: */
68: public function inContextOf(string $id): ContainerInterface;
69:
70: /**
71: * Resolve a service from the container
72: *
73: * Values in `$args` are matched with service dependencies using one or more
74: * of the following strategies:
75: *
76: * - match objects to parameters by hinted type
77: * - match other values to parameters by name
78: * - match remaining values by type, then position
79: *
80: * @template T
81: *
82: * @param class-string<T> $id
83: * @param mixed[] $args
84: * @return T&object
85: * @throws ArgumentsNotUsedExceptionInterface if `$args` are given and `$id`
86: * resolves to a shared instance.
87: */
88: public function get(string $id, array $args = []): object;
89:
90: /**
91: * Resolve a partially-resolved service from the container
92: *
93: * This method resolves `$id` normally but passes `$service` to
94: * {@see ServiceAwareInterface::setService()} instead of `$id`.
95: *
96: * @template T
97: * @template TService
98: *
99: * @param class-string<T> $id
100: * @param class-string<TService> $service
101: * @param mixed[] $args
102: * @return T&TService&object
103: * @throws ArgumentsNotUsedExceptionInterface if `$args` are given and `$id`
104: * resolves to a shared instance.
105: */
106: public function getAs(string $id, string $service, array $args = []): object;
107:
108: /**
109: * Resolve a service to a concrete class name
110: *
111: * @template T
112: *
113: * @param class-string<T> $id
114: * @return class-string<T>
115: */
116: public function getName(string $id): string;
117:
118: /**
119: * Check if a service is bound to the container
120: *
121: * @param class-string $id
122: */
123: public function has(string $id): bool;
124:
125: /**
126: * Check if a shared service is bound to the container
127: *
128: * @param class-string $id
129: */
130: public function hasSingleton(string $id): bool;
131:
132: /**
133: * Check if a service resolves to a shared instance
134: *
135: * @param class-string $id
136: */
137: public function hasInstance(string $id): bool;
138:
139: /**
140: * Check if a service provider is registered with the container
141: *
142: * @param class-string $id
143: */
144: public function hasProvider(string $id): bool;
145:
146: /**
147: * Bind a service to the container
148: *
149: * Subsequent requests for `$id` resolve to `$class`.
150: *
151: * If `$class` is `null`, `$id` resolves to itself.
152: *
153: * `$args` are merged with `$args` passed to {@see get()} or {@see getAs()}
154: * when `$id` resolves to a new instance of `$class`.
155: *
156: * @template TService
157: * @template T of TService
158: *
159: * @param class-string<TService> $id
160: * @param class-string<T>|null $class
161: * @param mixed[] $args
162: * @return $this
163: */
164: public function bind(
165: string $id,
166: ?string $class = null,
167: array $args = []
168: ): ContainerInterface;
169:
170: /**
171: * Bind a service to the container if it isn't already bound
172: *
173: * @template TService
174: * @template T of TService
175: *
176: * @param class-string<TService> $id
177: * @param class-string<T>|null $class
178: * @param mixed[] $args
179: * @return $this
180: */
181: public function bindIf(
182: string $id,
183: ?string $class = null,
184: array $args = []
185: ): ContainerInterface;
186:
187: /**
188: * Bind a shared service to the container
189: *
190: * Subsequent requests for `$id` resolve to the instance of `$class` created
191: * when `$id` is first requested.
192: *
193: * @template TService
194: * @template T of TService
195: *
196: * @param class-string<TService> $id
197: * @param class-string<T>|null $class
198: * @param mixed[] $args
199: * @return $this
200: */
201: public function singleton(
202: string $id,
203: ?string $class = null,
204: array $args = []
205: ): ContainerInterface;
206:
207: /**
208: * Bind a shared service to the container if it isn't already bound
209: *
210: * @template TService
211: * @template T of TService
212: *
213: * @param class-string<TService> $id
214: * @param class-string<T>|null $class
215: * @param mixed[] $args
216: * @return $this
217: */
218: public function singletonIf(
219: string $id,
220: ?string $class = null,
221: array $args = []
222: ): ContainerInterface;
223:
224: /**
225: * Bind a shared instance to the container
226: *
227: * @template TService
228: * @template T of TService
229: *
230: * @param class-string<TService> $id
231: * @param T&object $instance
232: * @return $this
233: */
234: public function instance(string $id, $instance): ContainerInterface;
235:
236: /**
237: * Register a contextual binding with the container
238: *
239: * Subsequent requests from `$context` for `$dependency` resolve to
240: * `$value`.
241: *
242: * If `$dependency` starts with `'$'`, it is matched with dependencies of
243: * `$context` by constructor parameter name, otherwise it is matched by
244: * type.
245: *
246: * If `$value` is a callback, its return value is used.
247: *
248: * @template TValue
249: *
250: * @param class-string[]|class-string $context
251: * @param class-string<TValue>|string $dependency
252: * @param (callable(ContainerInterface): TValue)|class-string<TValue>|TValue $value
253: * @return $this
254: */
255: public function addContextualBinding($context, string $dependency, $value): ContainerInterface;
256:
257: /**
258: * Register a service provider with the container, optionally specifying
259: * which of its services to bind or ignore
260: *
261: * For performance reasons, classes bound to the container with
262: * {@see bind()} or {@see singleton()} are not loaded until they are
263: * resolved. Classes registered with {@see provider()} are loaded
264: * immediately, but are not instantiated until they are resolved.
265: *
266: * @param class-string $id
267: * @param class-string[]|null $services Services to bind, or `null` to
268: * include all services.
269: * @param class-string[] $exceptServices Services to ignore.
270: * @param ServiceLifetime::* $lifetime
271: * @return $this
272: */
273: public function provider(
274: string $id,
275: ?array $services = null,
276: array $exceptServices = [],
277: int $lifetime = ServiceLifetime::INHERIT
278: ): ContainerInterface;
279:
280: /**
281: * Register a service map with the container
282: *
283: * A service map is an array with entries that map a service (usually an
284: * interface) to a service provider (a class that extends or implements the
285: * service).
286: *
287: * Multiple services may be mapped to the same service provider, and service
288: * providers may be mapped to themselves. For example, to make `BarClass`
289: * discoverable via {@see ContainerInterface::getProviders()} whether it's
290: * bound to a service or not:
291: *
292: * ```php
293: * <?php
294: * $container->providers([
295: * FooInterface::class => Env::get('foo_provider'),
296: * BarClass::class => BarClass::class,
297: * ]);
298: * ```
299: *
300: * @param array<class-string,class-string> $serviceMap
301: * @param ServiceLifetime::* $lifetime
302: * @return $this
303: */
304: public function providers(
305: array $serviceMap,
306: int $lifetime = ServiceLifetime::INHERIT
307: ): ContainerInterface;
308:
309: /**
310: * Get a list of service providers registered with the container
311: *
312: * @return array<class-string>
313: */
314: public function getProviders(): array;
315:
316: /**
317: * Remove a binding from the container
318: *
319: * @param class-string $id
320: * @return $this
321: */
322: public function unbind(string $id): ContainerInterface;
323:
324: /**
325: * Remove a shared instance from the container
326: *
327: * @param class-string $id
328: * @return $this
329: */
330: public function removeInstance(string $id): ContainerInterface;
331: }
332: