PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.2
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.2
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 4.9.2, at includes/Dependencies/DI/Definition/Resolver/ObjectCreator.php

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