1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
servicelocator.php
См. документацию.
1<?php
2
3namespace Bitrix\Main\DI;
4
5use Bitrix\Main\Config\Configuration;
6use Bitrix\Main\DI\Exception\ServiceNotFoundException;
7use Bitrix\Main\DI\Exception\CircularDependencyException;
8use Bitrix\Main\ObjectNotFoundException;
9use Bitrix\Main\SystemException;
10use Closure;
11use Psr\Container\ContainerExceptionInterface;
12use Psr\Container\ContainerInterface;
13use Psr\Container\NotFoundExceptionInterface;
14use ReflectionClass;
15use ReflectionException;
16use ReflectionNamedType;
17
18final class ServiceLocator implements ContainerInterface
19{
21 private array $services = [];
22 private array $instantiated = [];
23 private static ServiceLocator $instance;
24
25 private array $callStack = [];
26
27 private function __construct()
28 {}
29
30 private function __clone()
31 {}
32
33 public static function getInstance(): self
34 {
35 if (!isset(self::$instance))
36 {
37 self::$instance = new self();
38 }
39
40 return self::$instance;
41 }
42
48 public function addInstance(string $code, mixed $service): void
49 {
50 $this->instantiated[$code] = $service;
51 }
52
60 public function addInstanceLazy(string $id, $configuration): void
61 {
62 if (!isset($configuration['className']) && !isset($configuration['constructor']))
63 {
64 throw $this->buildBadRegistrationExceptions($id);
65 }
66
67 $furtherClassMetadata = $configuration['className'] ?? $configuration['constructor'];
68
69 $this->services[$id] = [$furtherClassMetadata, $configuration['constructorParams'] ?? []];
70 }
71
77 public function registerByModuleSettings(string $moduleName): void
78 {
79 $configuration = Configuration::getInstance($moduleName);
80 $services = $configuration['services'] ?? [];
81 foreach ($services as $code => $config)
82 {
83 if ($this->has($code))
84 {
85 //It means that there is overridden service in global .setting.php or extra settings.
86 //Or probably service was registered manually.
87 continue;
88 }
89
91 }
92 }
93
98 public function registerByGlobalSettings(): void
99 {
100 $configuration = Configuration::getInstance();
101 $services = $configuration['services'] ?? [];
102 foreach ($services as $code => $config)
103 {
105 }
106 }
107
113 public function has(string $id): bool
114 {
115 return isset($this->services[$id]) || isset($this->instantiated[$id]);
116 }
117
126 public function get(string $id): mixed
127 {
128 if (isset($this->instantiated[$id]))
129 {
130 return $this->instantiated[$id];
131 }
132
133 if ($this->isInterfaceKey($id) || $this->isAbstractClassKey($id))
134 {
135 $object = $this->resolveInterfaceOrAbstractClass($id);
136 }
137 elseif ($this->isClassKey($id))
138 {
139 $object = $this->resolveClass($id);
140 }
141 else
142 {
143 $object = $this->createItemByServiceName($id);
144 }
145
146 $this->instantiated[$id] = $object;
147
148 return $object;
149 }
150
151 private function buildNotFoundException(string $msg): ObjectNotFoundException|NotFoundExceptionInterface
152 {
153 return new class($msg) extends ObjectNotFoundException
154 implements NotFoundExceptionInterface {}
155 ;
156 }
157
158 private function buildBadRegistrationExceptions(string $id): SystemException|ContainerExceptionInterface
159 {
160 $message =
161 "Could not register service {{$id}}." .
162 "There is no {className} to find class or {constructor} to build instance."
163 ;
164
165 return new class($message) extends SystemException implements ContainerExceptionInterface {};
166 }
167
168 private function resolveInterfaceOrAbstractClass(string $id): object
169 {
170 [$classOrClosure, $args] = $this->services[$id];
171 if ($classOrClosure instanceof Closure)
172 {
173 return $classOrClosure(...(is_array($args) ? $args : []));
174 }
175
176 return $this->createItemByClassName($classOrClosure);
177 }
178
179 private function resolveClass(string $id): object
180 {
181 if (!class_exists($id))
182 {
183 throw $this->buildNotFoundException("Could not find service by code {$id}.");
184 }
185 return $this->createItemByClassName($id);
186 }
187
188 private function createItemByClassName(string $className): object
189 {
190 try
191 {
192 return $this->createObjectWithFullConstruct($className);
193 }
194 catch (ReflectionException $exception)
195 {
196 throw new ServiceNotFoundException(
197 $exception->getMessage()
198 );
199 }
200 }
201
205 private function createItemByServiceName(string $serviceName): object
206 {
207 [$class, $args] = $this->services[$serviceName];
208
209 if ($class instanceof Closure)
210 {
211 return $class();
212 }
213
214 if ($args instanceof Closure)
215 {
216 $args = $args();
217 }
218
219 return new $class(...array_values($args));
220 }
221
222 private function checkCircularDependency(string $className): void
223 {
224 if ($this->isCallStacked($className))
225 {
226 $path = implode(' -> ', $this->callStack) . " -> $className";
227
228 throw new CircularDependencyException(
229 'Cyclic dependency detected for service: ' . $path
230 );
231 }
232
233 $this->addCallStack($className);
234 }
235
236 private function getConstructorParams(ReflectionClass $class): array
237 {
238 $constructor = $class->getConstructor();
239 if ($constructor !== null && !$constructor->isPublic())
240 {
241 throw new ServiceNotFoundException(
242 $class->getName() . ' constructor must be is public'
243 );
244 }
245
246 return $constructor?->getParameters() ?? [];
247 }
248
249 private function resolveConstructorDependencies(array $params, string $className): array
250 {
251 if (empty($params))
252 {
253 return [];
254 }
255
256 $paramsForClass = [];
257 foreach ($params as $param)
258 {
259 $type = $param->getType();
260 if (!$type instanceof ReflectionNamedType)
261 {
262 throw new ServiceNotFoundException(
263 $className . ' All parameters in the constructor must have real class type'
264 );
265 }
266
267 $classNameInParams = $type->getName();
268 if (!class_exists($classNameInParams) && !interface_exists($classNameInParams))
269 {
270 throw new ServiceNotFoundException(
271 "For {$className} error in params: {$classNameInParams} must be an existing class, interface or abstract class"
272 );
273 }
274
275 $paramsForClass[] = $this->get($classNameInParams);
276 }
277
278 return $paramsForClass;
279 }
280
281 private function createInstance(ReflectionClass $class, array $paramsForClass, string $className): object
282 {
283 $object = $class->newInstanceArgs($paramsForClass);
284 if (empty($object))
285 {
286 throw new ServiceNotFoundException(
287 'Failed to create component ' . $className
288 );
289 }
290
291 return $object;
292 }
293
294 private function createObjectWithFullConstruct(string $className): object
295 {
296 try
297 {
298 $this->checkCircularDependency($className);
299
300 $class = new ReflectionClass($className);
301 $params = $this->getConstructorParams($class);
302 if (empty($params))
303 {
304 return new $className();
305 }
306
307 $paramsForClass = $this->resolveConstructorDependencies($params, $className);
308
309 return $this->createInstance($class, $paramsForClass, $className);
310 }
311 finally
312 {
313 $this->popCallStack();
314 }
315 }
316
317 private function addCallStack(string $className): void
318 {
319 $this->callStack[] = $className;
320 }
321
322 private function popCallStack(): void
323 {
324 array_pop($this->callStack);
325 }
326
327 private function isCallStacked(string $className): bool
328 {
329 return in_array($className, $this->callStack, true);
330 }
331
332 private function isInterfaceKey(string $id): bool
333 {
334 return interface_exists($id) && isset($this->services[$id]);
335 }
336
337 private function isAbstractClassKey(string $id): bool
338 {
339 return isset($this->services[$id]) && class_exists($id) && (new ReflectionClass($id))->isAbstract();
340 }
341
342 private function isClassKey(string $id): bool
343 {
344 return !isset($this->services[$id]);
345 }
346}
$path
Определения access_edit.php:21
$type
Определения options.php:106
addInstance(string $code, mixed $service)
Определения servicelocator.php:48
has(string $id)
Определения servicelocator.php:113
static getInstance()
Определения servicelocator.php:33
registerByModuleSettings(string $moduleName)
Определения servicelocator.php:77
addInstanceLazy(string $id, $configuration)
Определения servicelocator.php:60
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
Определения options.php:195
$service
Определения payment.php:18
$message
Определения payment.php:8
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$config
Определения quickway.php:69
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799