PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.4.0
Defender Security – Malware Scanner, Login Security & Firewall v5.4.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.4.0, at lib/packages/DI/Compiler/ObjectCreationCompiler.php

202 lines 7.2 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 /**
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 '\WPMU_DEV\Defender\Vendor\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 $this->compiler->getProxyFactory()->generateProxyClass($definition->getClassName());
141
142 return <<<PHP
143 \$object = \$this->proxyFactory->createProxy(
144 '{$definition->getClassName()}',
145 function (&\$wrappedObject, \$proxy, \$method, \$params, &\$initializer) {
146 \$wrappedObject = $subDefinition;
147 \$initializer = null; // turning off further lazy initialization
148 return true;
149 }
150 );
151 PHP;
152 }
153
154 /**
155 * Returns the default value of a function parameter.
156 *
157 * @throws InvalidDefinition Can't get default values from PHP internal classes and functions
158 * @return mixed
159 */
160 private function getParameterDefaultValue(ReflectionParameter $parameter, ReflectionMethod $function)
161 {
162 try {
163 return $parameter->getDefaultValue();
164 } catch (\ReflectionException $e) {
165 throw new InvalidDefinition(sprintf(
166 'The parameter "%s" of %s has no type defined or guessable. It has a default value, '
167 . 'but the default value can\'t be read through Reflection because it is a PHP internal class.',
168 $parameter->getName(),
169 $this->getFunctionName($function)
170 ));
171 }
172 }
173
174 private function getFunctionName(ReflectionMethod $method) : string
175 {
176 return $method->getName() . '()';
177 }
178
179 private function assertClassIsNotAnonymous(ObjectDefinition $definition)
180 {
181 if (strpos($definition->getClassName(), '@') !== false) {
182 throw InvalidDefinition::create($definition, sprintf(
183 'Entry "%s" cannot be compiled: anonymous classes cannot be compiled',
184 $definition->getName()
185 ));
186 }
187 }
188
189 private function assertClassIsInstantiable(ObjectDefinition $definition)
190 {
191 if ($definition->isInstantiable()) {
192 return;
193 }
194
195 $message = ! $definition->classExists()
196 ? 'Entry "%s" cannot be compiled: the class doesn\'t exist'
197 : 'Entry "%s" cannot be compiled: the class is not instantiable';
198
199 throw InvalidDefinition::create($definition, sprintf($message, $definition->getName()));
200 }
201 }
202