PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.7.0
Defender Security – Malware Scanner, Login Security & Firewall v5.7.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / lib / packages / DI / Compiler / ObjectCreationCompiler.php

ObjectCreationCompiler.php in Defender Security – Malware Scanner, Login Security & Firewall 5.7.0, at lib/packages/DI/Compiler/ObjectCreationCompiler.php

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