PluginProbe
Packeta / 2.0.9
Packeta v2.0.9
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
packeta / deps / nette / di / src / DI / Extensions / InjectExtension.php

InjectExtension.php in Packeta 2.0.9, at deps/nette/di/src/DI/Extensions/InjectExtension.php

141 lines 6.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * This file is part of the Nette Framework (https://nette.org)
5 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6 */
7 declare (strict_types=1);
8 namespace Packetery\Nette\DI\Extensions;
9
10 use Packetery\Nette;
11 use Packetery\Nette\DI;
12 use Packetery\Nette\DI\Definitions;
13 use Packetery\Nette\Utils\Reflection;
14 /**
15 * Calls inject methods and fills @inject properties.
16 */
17 final class InjectExtension extends DI\CompilerExtension
18 {
19 public const TagInject = 'nette.inject';
20 /** @deprecated use InjectExtension::TagInject */
21 public const TAG_INJECT = self::TagInject;
22 public function getConfigSchema() : \Packetery\Nette\Schema\Schema
23 {
24 return \Packetery\Nette\Schema\Expect::structure([]);
25 }
26 public function beforeCompile()
27 {
28 foreach ($this->getContainerBuilder()->getDefinitions() as $def) {
29 if ($def->getTag(self::TagInject)) {
30 $def = $def instanceof Definitions\FactoryDefinition ? $def->getResultDefinition() : $def;
31 if ($def instanceof Definitions\ServiceDefinition) {
32 $this->updateDefinition($def);
33 }
34 }
35 }
36 }
37 private function updateDefinition(Definitions\ServiceDefinition $def) : void
38 {
39 $resolvedType = (new DI\Resolver($this->getContainerBuilder()))->resolveEntityType($def->getCreator());
40 $class = \is_subclass_of($resolvedType, $def->getType()) ? $resolvedType : $def->getType();
41 $setups = $def->getSetup();
42 foreach (self::getInjectProperties($class) as $property => $type) {
43 $builder = $this->getContainerBuilder();
44 $inject = new Definitions\Statement(['@self', '$' . $property], [Definitions\Reference::fromType((string) $type)]);
45 foreach ($setups as $key => $setup) {
46 if ($setup->getEntity() == $inject->getEntity()) {
47 // intentionally ==
48 $inject = $setup;
49 $builder = null;
50 unset($setups[$key]);
51 }
52 }
53 if ($builder) {
54 self::checkType($class, $property, $type, $builder);
55 }
56 \array_unshift($setups, $inject);
57 }
58 foreach (\array_reverse(self::getInjectMethods($class)) as $method) {
59 $inject = new Definitions\Statement(['@self', $method]);
60 foreach ($setups as $key => $setup) {
61 if ($setup->getEntity() == $inject->getEntity()) {
62 // intentionally ==
63 $inject = $setup;
64 unset($setups[$key]);
65 }
66 }
67 \array_unshift($setups, $inject);
68 }
69 $def->setSetup($setups);
70 }
71 /**
72 * Generates list of inject methods.
73 * @internal
74 */
75 public static function getInjectMethods(string $class) : array
76 {
77 $classes = [];
78 foreach (\get_class_methods($class) as $name) {
79 if (\substr($name, 0, 6) === 'inject') {
80 $classes[$name] = (new \ReflectionMethod($class, $name))->getDeclaringClass()->name;
81 }
82 }
83 $methods = \array_keys($classes);
84 \uksort($classes, function (string $a, string $b) use($classes, $methods) : int {
85 return $classes[$a] === $classes[$b] ? \array_search($a, $methods, \true) <=> \array_search($b, $methods, \true) : (\is_a($classes[$a], $classes[$b], \true) ? 1 : -1);
86 });
87 return \array_keys($classes);
88 }
89 /**
90 * Generates list of properties with annotation @inject.
91 * @internal
92 */
93 public static function getInjectProperties(string $class) : array
94 {
95 $res = [];
96 foreach ((new \ReflectionClass($class))->getProperties() as $rp) {
97 $hasAttr = \PHP_VERSION_ID >= 80000 && $rp->getAttributes(DI\Attributes\Inject::class);
98 if ($hasAttr || DI\Helpers::parseAnnotation($rp, 'inject') !== null) {
99 if (!$rp->isPublic() || $rp->isStatic()) {
100 \trigger_error(\sprintf('Property %s for injection must be public and non-static.', Reflection::toString($rp)), \E_USER_WARNING);
101 continue;
102 }
103 if (\PHP_VERSION_ID >= 80100 && $rp->isReadOnly()) {
104 throw new \Packetery\Nette\InvalidStateException(\sprintf('Property %s for injection must not be readonly.', Reflection::toString($rp)));
105 }
106 $type = \Packetery\Nette\Utils\Type::fromReflection($rp);
107 if (!$type && !$hasAttr && ($annotation = DI\Helpers::parseAnnotation($rp, 'var'))) {
108 $annotation = Reflection::expandClassName($annotation, Reflection::getPropertyDeclaringClass($rp));
109 $type = \Packetery\Nette\Utils\Type::fromString($annotation);
110 }
111 $res[$rp->getName()] = DI\Helpers::ensureClassType($type, 'type of property ' . Reflection::toString($rp));
112 }
113 }
114 \ksort($res);
115 return $res;
116 }
117 /**
118 * Calls all methods starting with "inject" using autowiring.
119 * @param object $service
120 */
121 public static function callInjects(DI\Container $container, $service) : void
122 {
123 if (!\is_object($service)) {
124 throw new \Packetery\Nette\InvalidArgumentException(\sprintf('Service must be object, %s given.', \gettype($service)));
125 }
126 foreach (self::getInjectMethods(\get_class($service)) as $method) {
127 $container->callMethod([$service, $method]);
128 }
129 foreach (self::getInjectProperties(\get_class($service)) as $property => $type) {
130 self::checkType($service, $property, $type, $container);
131 $service->{$property} = $container->getByType($type);
132 }
133 }
134 private static function checkType($class, string $name, ?string $type, $container) : void
135 {
136 if (!$container->getByType($type, \false)) {
137 throw new \Packetery\Nette\DI\MissingServiceException(\sprintf('Service of type %s required by %s not found. Did you add it to configuration file?', $type, Reflection::toString(new \ReflectionProperty($class, $name))));
138 }
139 }
140 }
141