PluginProbe
The Innovative Form Builder – IvyForms / 1.5
The Innovative Form Builder – IvyForms v1.5
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / backend / scope-vendor / php-di / php-di / src / Compiler / ObjectCreationCompiler.php

ObjectCreationCompiler.php in The Innovative Form Builder – IvyForms 1.5, at backend/scope-vendor/php-di/php-di/src/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 IvyForms\Vendor\DI\Compiler;
6
7 use IvyForms\Vendor\DI\Definition\Exception\InvalidDefinition;
8 use IvyForms\Vendor\DI\Definition\ObjectDefinition;
9 use IvyForms\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 '\IvyForms\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