-
Notifications
You must be signed in to change notification settings - Fork 0
/
Container.php
executable file
·191 lines (174 loc) · 6.41 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
<?php
declare(strict_types=1);
namespace MaplePHP\Container;
use Closure;
use MaplePHP\Container\Interfaces\ContainerInterface;
use MaplePHP\Container\Interfaces\FactoryInterface;
use MaplePHP\DTO\Format\Arr;
//use MaplePHP\Container\Reflection;
use MaplePHP\Container\Exceptions\NotFoundException;
use MaplePHP\Container\Exceptions\ContainerException;
use ReflectionException;
class Container implements ContainerInterface, FactoryInterface
{
private array $services = [];
private array $args = [];
private array $getter = [];
/**
* @throws ReflectionException
*/
public function __call($method, $args)
{
return $this->get($method, $args);
}
/**
* Set a container OR factory
* @param string $identifier Uniq identifier
* @param mixed $value Example:
* TestClasses\Test::class,
* TestClasses\Test::class."::__construct",
* TestClasses\Test::class."::getStaticMethod",
* @param array|null $args Pass argumnets to constructor staticMethod if you choose.
* @param bool $overwrite Will throw exception if already been defined if not arg is set to TRUE.
*/
public function set(string $identifier, $value, ?array $args = null, bool $overwrite = false): ContainerInterface
{
if (!$overwrite && $this->has($identifier)) {
$type = ($this->isFactory($identifier)) ? "factory" : "container";
throw new ContainerException("The $type ($identifier) has already been defined. If you want to overwrite " .
"the $type then set overwrite argument to true.", 1);
}
if (isset($this->getter[$identifier])) {
unset($this->getter[$identifier]);
}
$this->services[$identifier] = $value;
$this->args[$identifier] = $args;
return $this;
}
/**
* Same as @set, BUT will only accept a factory
* @param string $identifier Uniq identifier
* @param callable $factory
* @param bool $overwrite Will throw exception if already been defined if not arg is set to TRUE.
* @return self
*/
public function factory(string $identifier, callable $factory, bool $overwrite = false): self
{
if (!$overwrite && $this->has($identifier)) {
if (!$this->isFactory($identifier)) {
throw new ContainerException("($identifier) Has already been defined, but has been defined as a " .
"container and not factory. If you want to overwrite the container as factory then set " .
"overwrite argument to true.", 1);
} else {
throw new ContainerException("The factory ($identifier) has already been defined. If you want to " .
"overwrite the factory then set overwrite argument to true.", 1);
}
}
if (isset($this->getter[$identifier])) {
unset($this->getter[$identifier]);
}
$this->services[$identifier] = $factory;
return $this;
}
/**
* Check if service exist
* @param string $identifier
* @return boolean
*/
public function has(string $identifier): bool
{
return (bool)($this->getService($identifier));
}
/**
* Check if is a factory
* @param string $identifier Uniq identifier
* @return boolean
*/
public function isFactory(string $identifier): bool
{
return ($this->getService($identifier) instanceof Closure);
}
/**
* Check if is a container
* @param string $identifier Uniq identifier
* @return boolean
*/
public function isContainer(string $identifier): bool
{
return (!$this->isFactory($identifier));
}
/**
* Get a container or factory
* @param string $identifier [description]
* @param array $args Is possible to overwrite/add __construct or method argumnets
* @return mixed
* @throws ReflectionException
*/
public function get(string $identifier, array $args = []): mixed
{
if ($service = $this->getService($identifier)) {
if (count($args) === 0) {
$args = $this->getArgs($identifier);
}
if ($this->isFactory($identifier)) {
$this->getter[$identifier] = $service(...$args);
} else {
if (empty($this->getter[$identifier])) {
if (is_string($service) && class_exists($service)) {
$reflect = new Reflection($service);
if (count($args) > 0) {
$reflect->setArgs($args);
}
$this->getter[$identifier] = $reflect->get();
} else {
$this->getter[$identifier] = $service;
}
}
}
return $this->getter[$identifier];
} else {
throw new NotFoundException("Trying to get a container ($identifier) that does not exists", 1);
}
}
/**
* Fetch is used to load multiple container and factories at once with the help of a wildcard search
* @param string $identifier
* @return array
* @throws ReflectionException
* @example @set("event.session", \name\space\session::class)
* ->set("event.traverse", \name\space\traverse::class)
* ->fetch("event.*");
*/
public function fetch(string $identifier): array
{
if (str_contains($identifier, "*")) {
$arr = Arr::value($this->services)->wildcardSearch($identifier)->get();
if (count($arr) > 0) {
$new = [];
foreach ($arr as $key => $_unusedValues) {
$new[$key] = $this->get($key);
}
return $new;
}
}
throw new NotFoundException("Error Processing Request", 1);
}
/**
* Get services
* @param string $identifier
* @return mixed
*/
private function getService(string $identifier): mixed
{
return ($this->services[$identifier] ?? null);
}
/**
* Get arguments
* @param string $identifier
* @return array
*/
private function getArgs(string $identifier): array
{
return ($this->args[$identifier] ?? []);
}
}