PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.8.1
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.8.1
4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 All 200 releases
betterdocs / includes / Dependencies / DI / Definition / Resolver / ObjectCreator.php

ObjectCreator.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 3.8.1, at includes/Dependencies/DI/Definition/Resolver/ObjectCreator.php

226 lines 7.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace WPDeveloper\BetterDocs\Dependencies\DI\Definition\Resolver;
6
7 use WPDeveloper\BetterDocs\Dependencies\DI\Definition\Definition;
8 use WPDeveloper\BetterDocs\Dependencies\DI\Definition\Exception\InvalidDefinition;
9 use WPDeveloper\BetterDocs\Dependencies\DI\Definition\ObjectDefinition;
10 use WPDeveloper\BetterDocs\Dependencies\DI\Definition\ObjectDefinition\PropertyInjection;
11 use WPDeveloper\BetterDocs\Dependencies\DI\DependencyException;
12 use WPDeveloper\BetterDocs\Dependencies\DI\Proxy\ProxyFactory;
13 use Exception;
14 use ProxyManager\Proxy\LazyLoadingInterface;
15 use WPDeveloper\BetterDocs\Dependencies\Psr\Container\NotFoundExceptionInterface;
16 use ReflectionClass;
17 use ReflectionProperty;
18
19 /**
20 * Create objects based on an object definition.
21 *
22 * @since 4.0
23 * @author Matthieu Napoli <matthieu@mnapoli.fr>
24 */
25 class ObjectCreator implements DefinitionResolver
26 {
27 /**
28 * @var ProxyFactory
29 */
30 private $proxyFactory;
31
32 /**
33 * @var ParameterResolver
34 */
35 private $parameterResolver;
36
37 /**
38 * @var DefinitionResolver
39 */
40 private $definitionResolver;
41
42 /**
43 * @param DefinitionResolver $definitionResolver Used to resolve nested definitions.
44 * @param ProxyFactory $proxyFactory Used to create proxies for lazy injections.
45 */
46 public function __construct(
47 DefinitionResolver $definitionResolver,
48 ProxyFactory $proxyFactory
49 ) {
50 $this->definitionResolver = $definitionResolver;
51 $this->proxyFactory = $proxyFactory;
52 $this->parameterResolver = new ParameterResolver($definitionResolver);
53 }
54
55 /**
56 * Resolve a class definition to a value.
57 *
58 * This will create a new instance of the class using the injections points defined.
59 *
60 * {@inheritdoc}
61 *
62 * @param ObjectDefinition $definition
63 *
64 * @return object|null
65 */
66 public function resolve(Definition $definition, array $parameters = [])
67 {
68 // Lazy?
69 if ($definition->isLazy()) {
70 return $this->createProxy($definition, $parameters);
71 }
72
73 return $this->createInstance($definition, $parameters);
74 }
75
76 /**
77 * The definition is not resolvable if the class is not instantiable (interface or abstract)
78 * or if the class doesn't exist.
79 *
80 * @param ObjectDefinition $definition
81 *
82 * {@inheritdoc}
83 */
84 public function isResolvable(Definition $definition, array $parameters = []) : bool
85 {
86 return $definition->isInstantiable();
87 }
88
89 /**
90 * Returns a proxy instance.
91 */
92 private function createProxy(ObjectDefinition $definition, array $parameters) : LazyLoadingInterface
93 {
94 /** @noinspection PhpUnusedParameterInspection */
95 $proxy = $this->proxyFactory->createProxy(
96 $definition->getClassName(),
97 function (& $wrappedObject, $proxy, $method, $params, & $initializer) use ($definition, $parameters) {
98 $wrappedObject = $this->createInstance($definition, $parameters);
99 $initializer = null; // turning off further lazy initialization
100 return true;
101 }
102 );
103
104 return $proxy;
105 }
106
107 /**
108 * Creates an instance of the class and injects dependencies..
109 *
110 * @param array $parameters Optional parameters to use to create the instance.
111 *
112 * @throws InvalidDefinition
113 * @throws DependencyException
114 * @return object
115 */
116 private function createInstance(ObjectDefinition $definition, array $parameters)
117 {
118 // Check that the class is instantiable
119 if (! $definition->isInstantiable()) {
120 // Check that the class exists
121 if (! $definition->classExists()) {
122 throw InvalidDefinition::create($definition, sprintf(
123 'Entry "%s" cannot be resolved: the class doesn\'t exist',
124 $definition->getName()
125 ));
126 }
127
128 throw InvalidDefinition::create($definition, sprintf(
129 'Entry "%s" cannot be resolved: the class is not instantiable',
130 $definition->getName()
131 ));
132 }
133
134 $classname = $definition->getClassName();
135 $classReflection = new ReflectionClass($classname);
136
137 $constructorInjection = $definition->getConstructorInjection();
138
139 try {
140 $args = $this->parameterResolver->resolveParameters(
141 $constructorInjection,
142 $classReflection->getConstructor(),
143 $parameters
144 );
145
146 $object = new $classname(...$args);
147
148 $this->injectMethodsAndProperties($object, $definition);
149 } catch (NotFoundExceptionInterface $e) {
150 throw new DependencyException(sprintf(
151 'Error while injecting dependencies into %s: %s',
152 $classReflection->getName(),
153 $e->getMessage()
154 ), 0, $e);
155 } catch (InvalidDefinition $e) {
156 throw InvalidDefinition::create($definition, sprintf(
157 'Entry "%s" cannot be resolved: %s',
158 $definition->getName(),
159 $e->getMessage()
160 ));
161 }
162
163 return $object;
164 }
165
166 protected function injectMethodsAndProperties($object, ObjectDefinition $objectDefinition)
167 {
168 // Property injections
169 foreach ($objectDefinition->getPropertyInjections() as $propertyInjection) {
170 $this->injectProperty($object, $propertyInjection);
171 }
172
173 // Method injections
174 foreach ($objectDefinition->getMethodInjections() as $methodInjection) {
175 $methodReflection = new \ReflectionMethod($object, $methodInjection->getMethodName());
176 $args = $this->parameterResolver->resolveParameters($methodInjection, $methodReflection);
177
178 $methodReflection->invokeArgs($object, $args);
179 }
180 }
181
182 /**
183 * Inject dependencies into properties.
184 *
185 * @param object $object Object to inject dependencies into
186 * @param PropertyInjection $propertyInjection Property injection definition
187 *
188 * @throws DependencyException
189 * @throws InvalidDefinition
190 */
191 private function injectProperty($object, PropertyInjection $propertyInjection)
192 {
193 $propertyName = $propertyInjection->getPropertyName();
194
195 $value = $propertyInjection->getValue();
196
197 if ($value instanceof Definition) {
198 try {
199 $value = $this->definitionResolver->resolve($value);
200 } catch (DependencyException $e) {
201 throw $e;
202 } catch (Exception $e) {
203 throw new DependencyException(sprintf(
204 'Error while injecting in %s::%s. %s',
205 get_class($object),
206 $propertyName,
207 $e->getMessage()
208 ), 0, $e);
209 }
210 }
211
212 self::setPrivatePropertyValue($propertyInjection->getClassName(), $object, $propertyName, $value);
213 }
214
215 public static function setPrivatePropertyValue(string $className = null, $object, string $propertyName, $propertyValue)
216 {
217 $className = $className ?: get_class($object);
218
219 $property = new ReflectionProperty($className, $propertyName);
220 if (! $property->isPublic()) {
221 $property->setAccessible(true);
222 }
223 $property->setValue($object, $propertyValue);
224 }
225 }
226