elementor
/
vendor_prefixed
/
dependency-injection
/
php-di
/
php-di
/
src
/
Compiler
/
ObjectCreationCompiler.php
ObjectCreationCompiler.php in Elementor Website Builder – more than just a page builder 3.29.0-beta4, at vendor_prefixed/dependency-injection/php-di/php-di/src/Compiler/ObjectCreationCompiler.php
| 1 | <?php |
| 2 | |
| 3 | declare (strict_types=1); |
| 4 | namespace ElementorDeps\DI\Compiler; |
| 5 | |
| 6 | use ElementorDeps\DI\Definition\Exception\InvalidDefinition; |
| 7 | use ElementorDeps\DI\Definition\ObjectDefinition; |
| 8 | use ElementorDeps\DI\Definition\ObjectDefinition\MethodInjection; |
| 9 | use ReflectionClass; |
| 10 | use ReflectionMethod; |
| 11 | use ReflectionParameter; |
| 12 | use ReflectionProperty; |
| 13 | /** |
| 14 | * Compiles an object definition into native PHP code that, when executed, creates the object. |
| 15 | * |
| 16 | * @author Matthieu Napoli <[email protected]> |
| 17 | */ |
| 18 | class ObjectCreationCompiler |
| 19 | { |
| 20 | /** |
| 21 | * @var Compiler |
| 22 | */ |
| 23 | private $compiler; |
| 24 | public function __construct(Compiler $compiler) |
| 25 | { |
| 26 | $this->compiler = $compiler; |
| 27 | } |
| 28 | public function compile(ObjectDefinition $definition) : string |
| 29 | { |
| 30 | $this->assertClassIsNotAnonymous($definition); |
| 31 | $this->assertClassIsInstantiable($definition); |
| 32 | // Lazy? |
| 33 | if ($definition->isLazy()) { |
| 34 | return $this->compileLazyDefinition($definition); |
| 35 | } |
| 36 | try { |
| 37 | $classReflection = new ReflectionClass($definition->getClassName()); |
| 38 | $constructorArguments = $this->resolveParameters($definition->getConstructorInjection(), $classReflection->getConstructor()); |
| 39 | $dumpedConstructorArguments = \array_map(function ($value) { |
| 40 | return $this->compiler->compileValue($value); |
| 41 | }, $constructorArguments); |
| 42 | $code = []; |
| 43 | $code[] = \sprintf('$object = new %s(%s);', $definition->getClassName(), \implode(', ', $dumpedConstructorArguments)); |
| 44 | // Property injections |
| 45 | foreach ($definition->getPropertyInjections() as $propertyInjection) { |
| 46 | $value = $propertyInjection->getValue(); |
| 47 | $value = $this->compiler->compileValue($value); |
| 48 | $className = $propertyInjection->getClassName() ?: $definition->getClassName(); |
| 49 | $property = new ReflectionProperty($className, $propertyInjection->getPropertyName()); |
| 50 | if ($property->isPublic()) { |
| 51 | $code[] = \sprintf('$object->%s = %s;', $propertyInjection->getPropertyName(), $value); |
| 52 | } else { |
| 53 | // Private/protected property |
| 54 | $code[] = \sprintf('\\DI\\Definition\\Resolver\\ObjectCreator::setPrivatePropertyValue(%s, $object, \'%s\', %s);', \var_export($propertyInjection->getClassName(), \true), $propertyInjection->getPropertyName(), $value); |
| 55 | } |
| 56 | } |
| 57 | // Method injections |
| 58 | foreach ($definition->getMethodInjections() as $methodInjection) { |
| 59 | $methodReflection = new \ReflectionMethod($definition->getClassName(), $methodInjection->getMethodName()); |
| 60 | $parameters = $this->resolveParameters($methodInjection, $methodReflection); |
| 61 | $dumpedParameters = \array_map(function ($value) { |
| 62 | return $this->compiler->compileValue($value); |
| 63 | }, $parameters); |
| 64 | $code[] = \sprintf('$object->%s(%s);', $methodInjection->getMethodName(), \implode(', ', $dumpedParameters)); |
| 65 | } |
| 66 | } catch (InvalidDefinition $e) { |
| 67 | throw InvalidDefinition::create($definition, \sprintf('Entry "%s" cannot be compiled: %s', $definition->getName(), $e->getMessage())); |
| 68 | } |
| 69 | return \implode("\n ", $code); |
| 70 | } |
| 71 | public function resolveParameters(MethodInjection $definition = null, ReflectionMethod $method = null) : array |
| 72 | { |
| 73 | $args = []; |
| 74 | if (!$method) { |
| 75 | return $args; |
| 76 | } |
| 77 | $definitionParameters = $definition ? $definition->getParameters() : []; |
| 78 | foreach ($method->getParameters() as $index => $parameter) { |
| 79 | if (\array_key_exists($index, $definitionParameters)) { |
| 80 | // Look in the definition |
| 81 | $value =& $definitionParameters[$index]; |
| 82 | } elseif ($parameter->isOptional()) { |
| 83 | // If the parameter is optional and wasn't specified, we take its default value |
| 84 | $args[] = $this->getParameterDefaultValue($parameter, $method); |
| 85 | continue; |
| 86 | } else { |
| 87 | throw new InvalidDefinition(\sprintf('Parameter $%s of %s has no value defined or guessable', $parameter->getName(), $this->getFunctionName($method))); |
| 88 | } |
| 89 | $args[] =& $value; |
| 90 | } |
| 91 | return $args; |
| 92 | } |
| 93 | private function compileLazyDefinition(ObjectDefinition $definition) : string |
| 94 | { |
| 95 | $subDefinition = clone $definition; |
| 96 | $subDefinition->setLazy(\false); |
| 97 | $subDefinition = $this->compiler->compileValue($subDefinition); |
| 98 | $this->compiler->getProxyFactory()->generateProxyClass($definition->getClassName()); |
| 99 | return <<<PHP |
| 100 | \$object = \$this->proxyFactory->createProxy( |
| 101 | '{$definition->getClassName()}', |
| 102 | function (&\$wrappedObject, \$proxy, \$method, \$params, &\$initializer) { |
| 103 | \$wrappedObject = {$subDefinition}; |
| 104 | \$initializer = null; // turning off further lazy initialization |
| 105 | return true; |
| 106 | } |
| 107 | ); |
| 108 | PHP; |
| 109 | } |
| 110 | /** |
| 111 | * Returns the default value of a function parameter. |
| 112 | * |
| 113 | * @throws InvalidDefinition Can't get default values from PHP internal classes and functions |
| 114 | * @return mixed |
| 115 | */ |
| 116 | private function getParameterDefaultValue(ReflectionParameter $parameter, ReflectionMethod $function) |
| 117 | { |
| 118 | try { |
| 119 | return $parameter->getDefaultValue(); |
| 120 | } catch (\ReflectionException $e) { |
| 121 | throw new InvalidDefinition(\sprintf('The parameter "%s" of %s has no type defined or guessable. It has a default value, ' . 'but the default value can\'t be read through Reflection because it is a PHP internal class.', $parameter->getName(), $this->getFunctionName($function))); |
| 122 | } |
| 123 | } |
| 124 | private function getFunctionName(ReflectionMethod $method) : string |
| 125 | { |
| 126 | return $method->getName() . '()'; |
| 127 | } |
| 128 | private function assertClassIsNotAnonymous(ObjectDefinition $definition) |
| 129 | { |
| 130 | if (\strpos($definition->getClassName(), '@') !== \false) { |
| 131 | throw InvalidDefinition::create($definition, \sprintf('Entry "%s" cannot be compiled: anonymous classes cannot be compiled', $definition->getName())); |
| 132 | } |
| 133 | } |
| 134 | private function assertClassIsInstantiable(ObjectDefinition $definition) |
| 135 | { |
| 136 | if ($definition->isInstantiable()) { |
| 137 | return; |
| 138 | } |
| 139 | $message = !$definition->classExists() ? 'Entry "%s" cannot be compiled: the class doesn\'t exist' : 'Entry "%s" cannot be compiled: the class is not instantiable'; |
| 140 | throw InvalidDefinition::create($definition, \sprintf($message, $definition->getName())); |
| 141 | } |
| 142 | } |
| 143 |