-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDIContainer.php
More file actions
140 lines (124 loc) · 3.6 KB
/
DIContainer.php
File metadata and controls
140 lines (124 loc) · 3.6 KB
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
<?php
declare(strict_types=1);
namespace Snicco\Component\Kernel;
use ArrayAccess;
use Closure;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface as PsrContainer;
use Psr\Container\NotFoundExceptionInterface;
use ReturnTypeWillChange;
use Snicco\Component\Kernel\Exception\FrozenService;
use Webmozart\Assert\Assert;
/**
* The DependencyInjection(DI) container takes care of lazily constructing and
* loading services for your application. The snicco core itself DOES NOT
* require your implementation to be capable of auto-wiring. However, you are
* free to use a container that supports auto-wiring in your application code.
*/
abstract class DIContainer implements ArrayAccess, PsrContainer
{
/**
* After the lock method is called and method call that would change the
* container must throw.
*
* {@see FrozenService}.
*
* @throws FrozenService
*
* @psalm-internal Snicco
*/
abstract public function lock(): void;
/**
* Register a lazy callable in the container. This method MUST return a new
* object every time the id is resolved from the container.
*
* @template T of object
*
* @param class-string<T> $id
* @param callable():T $callable
*
* @throws FrozenService when trying to overwrite an already resolved shared service
*/
abstract public function factory(string $id, callable $callable): void;
/**
* Register a lazy callable in the container that will only be called ONCE.
* After resolving the service once the same object instance MUST be
* returned every time its resolved.
*
* @template T of object
*
* @param class-string<T> $id
* @param callable():T $callable
*
* @throws FrozenService when trying to overwrite an already resolved shared service
*/
abstract public function shared(string $id, callable $callable): void;
/**
* Stores the passed instance as a shared service.
*
* @template T of object
*
* @param class-string<T> $id
* @param T $service
*
* @throws FrozenService when trying to overwrite an already resolved singleton
*/
final public function instance(string $id, object $service): void
{
$this->shared($id, fn (): object => $service);
}
/**
* @template T of object
*
* @param class-string<T> $offset
*
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*
* @return T
*/
#[ReturnTypeWillChange]
final public function offsetGet($offset): object
{
Assert::stringNotEmpty($offset);
return $this->make($offset);
}
/**
* @template T of object
*
* @param class-string<T> $offset
* @param callable():T|T $value
*/
final public function offsetSet($offset, $value): void
{
Assert::stringNotEmpty($offset);
Assert::object($value);
if ($value instanceof Closure) {
/**
* @var Closure():object $value
*/
$this->shared($offset, $value);
return;
}
$this->instance($offset, $value);
}
/**
* @template T of object
*
* @param class-string<T> $id
*
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*
* @psalm-return T
*/
final public function make(string $id): object
{
/**
* @var mixed $res
*/
$res = $this->get($id);
Assert::isInstanceOf($res, $id);
return $res;
}
}