|
7 | 7 | use ArrayIterator;
|
8 | 8 | use Closure;
|
9 | 9 | use ReflectionFunction;
|
| 10 | +use Tempest\Container\Exceptions\DecoratorDIdNotImplementInterface; |
10 | 11 | use Tempest\Container\Exceptions\DependencyCouldNotBeAutowired;
|
11 | 12 | use Tempest\Container\Exceptions\DependencyCouldNotBeInstantiated;
|
12 | 13 | use Tempest\Container\Exceptions\InvokedCallableWasInvalid;
|
@@ -35,6 +36,9 @@ public function __construct(
|
35 | 36 |
|
36 | 37 | /** @var ArrayIterator<array-key, class-string> $dynamicInitializers */
|
37 | 38 | private ArrayIterator $dynamicInitializers = new ArrayIterator(),
|
| 39 | + |
| 40 | + /** @var ArrayIterator<array-key, class-string[]> $decorators */ |
| 41 | + private ArrayIterator $decorators = new ArrayIterator(), |
38 | 42 | private ?DependencyChain $chain = null,
|
39 | 43 | ) {}
|
40 | 44 |
|
@@ -66,6 +70,13 @@ public function setDynamicInitializers(array $dynamicInitializers): self
|
66 | 70 | return $this;
|
67 | 71 | }
|
68 | 72 |
|
| 73 | + public function setDecorators(array $decorators): self |
| 74 | + { |
| 75 | + $this->decorators = new ArrayIterator($decorators); |
| 76 | + |
| 77 | + return $this; |
| 78 | + } |
| 79 | + |
69 | 80 | public function getDefinitions(): array
|
70 | 81 | {
|
71 | 82 | return $this->definitions->getArrayCopy();
|
@@ -99,6 +110,11 @@ public function getDynamicInitializers(): array
|
99 | 110 | return $this->dynamicInitializers->getArrayCopy();
|
100 | 111 | }
|
101 | 112 |
|
| 113 | + public function getDecorators(): array |
| 114 | + { |
| 115 | + return $this->decorators->getArrayCopy(); |
| 116 | + } |
| 117 | + |
102 | 118 | public function register(string $className, callable $definition): self
|
103 | 119 | {
|
104 | 120 | $this->definitions[$className] = $definition;
|
@@ -299,7 +315,28 @@ public function removeInitializer(ClassReflector|string $initializerClass): Cont
|
299 | 315 | return $this;
|
300 | 316 | }
|
301 | 317 |
|
| 318 | + public function addDecorator(ClassReflector|string $decoratorClass, ClassReflector|string $decoratedClass): Container |
| 319 | + { |
| 320 | + $decoratorClass = is_string($decoratorClass) ? $decoratorClass : $decoratorClass->getName(); |
| 321 | + $decoratedClass = is_string($decoratedClass) ? $decoratedClass : $decoratedClass->getName(); |
| 322 | + |
| 323 | + $this->decorators[$decoratedClass][] = $decoratorClass; |
| 324 | + |
| 325 | + return $this; |
| 326 | + } |
| 327 | + |
302 | 328 | private function resolve(string $className, null|string|UnitEnum $tag = null, mixed ...$params): ?object
|
| 329 | + { |
| 330 | + $instance = $this->resolveDependency($className, $tag, ...$params); |
| 331 | + |
| 332 | + if ($this->decorators[$className] ?? null) { |
| 333 | + $instance = $this->resolveDecorator($className, $instance, $tag, ...$params); |
| 334 | + } |
| 335 | + |
| 336 | + return $instance; |
| 337 | + } |
| 338 | + |
| 339 | + private function resolveDependency(string $className, null|string|UnitEnum $tag = null, mixed ...$params): ?object |
303 | 340 | {
|
304 | 341 | $class = new ClassReflector($className);
|
305 | 342 |
|
@@ -602,4 +639,35 @@ private function resolveTaggedName(string $className, null|string|UnitEnum $tag)
|
602 | 639 | ? "{$className}#{$tag}"
|
603 | 640 | : $className;
|
604 | 641 | }
|
| 642 | + |
| 643 | + private function resolveDecorator(string $className, mixed $instance, null|string|UnitEnum $tag = null, mixed ...$params): ?object |
| 644 | + { |
| 645 | + foreach ($this->decorators[$className] ?? [] as $decoratorClass) { |
| 646 | + $decoratorClassReflector = new ClassReflector($decoratorClass); |
| 647 | + $constructor = $decoratorClassReflector->getConstructor(); |
| 648 | + $parameters = $constructor?->getParameters(); |
| 649 | + |
| 650 | + // we look for parameter holding decorated instance |
| 651 | + foreach ($parameters ?? [] as $parameter) { |
| 652 | + if ($parameter->getType()->matches($className) === false) { |
| 653 | + continue; |
| 654 | + } |
| 655 | + |
| 656 | + // we bind the decorated instance to the parameter, so container won't try to resolve it (it would end up as circular dependency) |
| 657 | + $params[$parameter->getName()] = $instance; |
| 658 | + |
| 659 | + break; |
| 660 | + } |
| 661 | + |
| 662 | + $decorator = $this->resolveDependency($decoratorClass, $tag, ...$params); |
| 663 | + |
| 664 | + if (! ($decorator instanceof $className)) { |
| 665 | + throw new DecoratorDIdNotImplementInterface($className, $decoratorClass, $className); |
| 666 | + } |
| 667 | + |
| 668 | + $instance = $decorator; |
| 669 | + } |
| 670 | + |
| 671 | + return $instance; |
| 672 | + } |
605 | 673 | }
|
0 commit comments