PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.5.4
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.5.4
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 3.5.2 All 199 releases
betterdocs / includes / Dependencies / DI / Compiler / ObjectCreationCompiler.php

ObjectCreationCompiler.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.5.4, at includes/Dependencies/DI/Compiler/ObjectCreationCompiler.php

205 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\Compiler;
6
7 use WPDeveloper\BetterDocs\Dependencies\DI\Definition\Exception\InvalidDefinition;
8 use WPDeveloper\BetterDocs\Dependencies\DI\Definition\ObjectDefinition;
9 use WPDeveloper\BetterDocs\Dependencies\DI\Definition\ObjectDefinition\MethodInjection;
10 use ReflectionClass;
11 use ReflectionMethod;
12 use ReflectionParameter;
13 use ReflectionProperty;
14
15 /**
16 * Compiles an object definition into native PHP code that, when executed, creates the object.
17 *
18 * @author Matthieu Napoli <matthieu@mnapoli.fr>
19 */
20 class ObjectCreationCompiler
21 {
22 /**
23 * @var Compiler
24 */
25 private $compiler;
26
27 public function __construct(Compiler $compiler)
28 {
29 $this->compiler = $compiler;
30 }
31
32 public function compile(ObjectDefinition $definition) : string
33 {
34 $this->assertClassIsNotAnonymous($definition);
35 $this->assertClassIsInstantiable($definition);
36
37 // Lazy?
38 if ($definition->isLazy()) {
39 return $this->compileLazyDefinition($definition);
40 }
41
42 try {
43 $classReflection = new ReflectionClass($definition->getClassName());
44 $constructorArguments = $this->resolveParameters($definition->getConstructorInjection(), $classReflection->getConstructor());
45 $dumpedConstructorArguments = array_map(function ($value) {
46 return $this->compiler->compileValue($value);
47 }, $constructorArguments);
48
49 $code = [];
50 $code[] = sprintf(
51 '$object = new %s(%s);',
52 $definition->getClassName(),
53 implode(', ', $dumpedConstructorArguments)
54 );
55
56 // Property injections
57 foreach ($definition->getPropertyInjections() as $propertyInjection) {
58 $value = $propertyInjection->getValue();
59 $value = $this->compiler->compileValue($value);
60
61 $className = $propertyInjection->getClassName() ?: $definition->getClassName();
62 $property = new ReflectionProperty($className, $propertyInjection->getPropertyName());
63 if ($property->isPublic()) {
64 $code[] = sprintf('$object->%s = %s;', $propertyInjection->getPropertyName(), $value);
65 } else {
66 // Private/protected property
67 $code[] = sprintf(
68 '\WPDeveloper\BetterDocs\Dependencies\DI\Definition\Resolver\ObjectCreator::setPrivatePropertyValue(%s, $object, \'%s\', %s);',
69 var_export($propertyInjection->getClassName(), true),
70 $propertyInjection->getPropertyName(),
71 $value
72 );
73 }
74 }
75
76 // Method injections
77 foreach ($definition->getMethodInjections() as $methodInjection) {
78 $methodReflection = new \ReflectionMethod($definition->getClassName(), $methodInjection->getMethodName());
79 $parameters = $this->resolveParameters($methodInjection, $methodReflection);
80
81 $dumpedParameters = array_map(function ($value) {
82 return $this->compiler->compileValue($value);
83 }, $parameters);
84
85 $code[] = sprintf(
86 '$object->%s(%s);',
87 $methodInjection->getMethodName(),
88 implode(', ', $dumpedParameters)
89 );
90 }
91 } catch (InvalidDefinition $e) {
92 throw InvalidDefinition::create($definition, sprintf(
93 'Entry "%s" cannot be compiled: %s',
94 $definition->getName(),
95 $e->getMessage()
96 ));
97 }
98
99 return implode("\n ", $code);
100 }
101
102 public function resolveParameters(?MethodInjection $definition = null, ?ReflectionMethod $method = null) : array
103 {
104 $args = [];
105
106 if (! $method) {
107 return $args;
108 }
109
110 $definitionParameters = $definition ? $definition->getParameters() : [];
111
112 foreach ($method->getParameters() as $index => $parameter) {
113 if (array_key_exists($index, $definitionParameters)) {
114 // Look in the definition
115 $value = &$definitionParameters[$index];
116 } elseif ($parameter->isOptional()) {
117 // If the parameter is optional and wasn't specified, we take its default value
118 $args[] = $this->getParameterDefaultValue($parameter, $method);
119 continue;
120 } else {
121 throw new InvalidDefinition(sprintf(
122 'Parameter $%s of %s has no value defined or guessable',
123 $parameter->getName(),
124 $this->getFunctionName($method)
125 ));
126 }
127
128 $args[] = &$value;
129 }
130
131 return $args;
132 }
133
134 private function compileLazyDefinition(ObjectDefinition $definition) : string
135 {
136 $subDefinition = clone $definition;
137 $subDefinition->setLazy(false);
138 $subDefinition = $this->compiler->compileValue($subDefinition);
139
140 return <<<PHP
141 \$object = \$this->proxyFactory->createProxy(
142 '{$definition->getClassName()}',
143 function (&\$wrappedObject, \$proxy, \$method, \$params, &\$initializer) {
144 \$wrappedObject = $subDefinition;
145 \$initializer = null; // turning off further lazy initialization
146 return true;
147 }
148 );
149 PHP;
150 }
151
152 /**
153 * Returns the default value of a function parameter.
154 *
155 * @throws InvalidDefinition Can't get default values from PHP internal classes and functions
156 * @return mixed
157 */
158 private function getParameterDefaultValue(ReflectionParameter $parameter, ReflectionMethod $function)
159 {
160 try {
161 return $parameter->getDefaultValue();
162 } catch (\ReflectionException $e) {
163 throw new InvalidDefinition(sprintf(
164 'The parameter "%s" of %s has no type defined or guessable. It has a default value, '
165 . 'but the default value can\'t be read through Reflection because it is a PHP internal class.',
166 $parameter->getName(),
167 $this->getFunctionName($function)
168 ));
169 }
170 }
171
172 private function getFunctionName(ReflectionMethod $method) : string
173 {
174 return $method->getName() . '()';
175 }
176
177 private function assertClassIsNotAnonymous(ObjectDefinition $definition)
178 {
179 if (strpos($definition->getClassName(), '@') !== false) {
180 throw InvalidDefinition::create($definition, sprintf(
181 'Entry "%s" cannot be compiled: anonymous classes cannot be compiled',
182 $definition->getName()
183 ));
184 }
185 }
186
187 private function assertClassIsInstantiable(ObjectDefinition $definition)
188 {
189 if (! $definition->isInstantiable()) {
190 // Check that the class exists
191 if (! $definition->classExists()) {
192 throw InvalidDefinition::create($definition, sprintf(
193 'Entry "%s" cannot be compiled: the class doesn\'t exist',
194 $definition->getName()
195 ));
196 }
197
198 throw InvalidDefinition::create($definition, sprintf(
199 'Entry "%s" cannot be compiled: the class is not instantiable',
200 $definition->getName()
201 ));
202 }
203 }
204 }
205