Файловый менеджер - Редактировать - /var/www/xthruster/html/wp-content/uploads/flags/vendor_prefixed.tar
Назад
laravel/serializable-closure/src/Support/ReflectionClosure.php 0000644 00000130175 14717614651 0020740 0 ustar 00 <?php namespace ElementorProDeps\Laravel\SerializableClosure\Support; \defined('T_NAME_QUALIFIED') || \define('T_NAME_QUALIFIED', -4); \defined('T_NAME_FULLY_QUALIFIED') || \define('T_NAME_FULLY_QUALIFIED', -5); \defined('T_FN') || \define('T_FN', -6); \defined('T_NULLSAFE_OBJECT_OPERATOR') || \define('T_NULLSAFE_OBJECT_OPERATOR', -7); use Closure; use ReflectionFunction; class ReflectionClosure extends ReflectionFunction { protected $code; protected $tokens; protected $hashedName; protected $useVariables; protected $isStaticClosure; protected $isScopeRequired; protected $isBindingRequired; protected $isShortClosure; protected static $files = []; protected static $classes = []; protected static $functions = []; protected static $constants = []; protected static $structures = []; /** * Creates a new reflection closure instance. * * @param \Closure $closure * @param string|null $code * @return void */ public function __construct(Closure $closure, $code = null) { parent::__construct($closure); } /** * Checks if the closure is "static". * * @return bool */ public function isStatic() : bool { if ($this->isStaticClosure === null) { $this->isStaticClosure = \strtolower(\substr($this->getCode(), 0, 6)) === 'static'; } return $this->isStaticClosure; } /** * Checks if the closure is a "short closure". * * @return bool */ public function isShortClosure() { if ($this->isShortClosure === null) { $code = $this->getCode(); if ($this->isStatic()) { $code = \substr($code, 6); } $this->isShortClosure = \strtolower(\substr(\trim($code), 0, 2)) === 'fn'; } return $this->isShortClosure; } /** * Get the closure's code. * * @return string */ public function getCode() { if ($this->code !== null) { return $this->code; } $fileName = $this->getFileName(); $line = $this->getStartLine() - 1; $className = null; if (null !== ($className = $this->getClosureScopeClass())) { $className = '\\' . \trim($className->getName(), '\\'); } $builtin_types = self::getBuiltinTypes(); $class_keywords = ['self', 'static', 'parent']; $ns = $this->getClosureNamespaceName(); $nsf = $ns == '' ? '' : ($ns[0] == '\\' ? $ns : '\\' . $ns); $_file = \var_export($fileName, \true); $_dir = \var_export(\dirname($fileName), \true); $_namespace = \var_export($ns, \true); $_class = \var_export(\trim($className ?: '', '\\'), \true); $_function = $ns . ($ns == '' ? '' : '\\') . '{closure}'; $_method = ($className == '' ? '' : \trim($className, '\\') . '::') . $_function; $_function = \var_export($_function, \true); $_method = \var_export($_method, \true); $_trait = null; $tokens = $this->getTokens(); $state = $lastState = 'start'; $inside_structure = \false; $isFirstClassCallable = \false; $isShortClosure = \false; $inside_structure_mark = 0; $open = 0; $code = ''; $id_start = $id_start_ci = $id_name = $context = ''; $classes = $functions = $constants = null; $use = []; $lineAdd = 0; $isUsingScope = \false; $isUsingThisObject = \false; for ($i = 0, $l = \count($tokens); $i < $l; $i++) { $token = $tokens[$i]; switch ($state) { case 'start': if ($token[0] === \T_FUNCTION || $token[0] === \T_STATIC) { $code .= $token[1]; $state = $token[0] === \T_FUNCTION ? 'function' : 'static'; } elseif ($token[0] === \T_FN) { $isShortClosure = \true; $code .= $token[1]; $state = 'closure_args'; } elseif ($token[0] === \T_PUBLIC || $token[0] === \T_PROTECTED || $token[0] === \T_PRIVATE) { $code = ''; $isFirstClassCallable = \true; } break; case 'static': if ($token[0] === \T_WHITESPACE || $token[0] === \T_COMMENT || $token[0] === \T_FUNCTION) { $code .= $token[1]; if ($token[0] === \T_FUNCTION) { $state = 'function'; } } elseif ($token[0] === \T_FN) { $isShortClosure = \true; $code .= $token[1]; $state = 'closure_args'; } else { $code = ''; $state = 'start'; } break; case 'function': switch ($token[0]) { case \T_STRING: if ($isFirstClassCallable) { $state = 'closure_args'; break; } $code = ''; $state = 'named_function'; break; case '(': $code .= '('; $state = 'closure_args'; break; default: $code .= \is_array($token) ? $token[1] : $token; } break; case 'named_function': if ($token[0] === \T_FUNCTION || $token[0] === \T_STATIC) { $code = $token[1]; $state = $token[0] === \T_FUNCTION ? 'function' : 'static'; } elseif ($token[0] === \T_FN) { $isShortClosure = \true; $code .= $token[1]; $state = 'closure_args'; } break; case 'closure_args': switch ($token[0]) { case \T_NAME_QUALIFIED: [$id_start, $id_start_ci, $id_name] = $this->parseNameQualified($token[1]); $context = 'args'; $state = 'id_name'; $lastState = 'closure_args'; break; case \T_NS_SEPARATOR: case \T_STRING: $id_start = $token[1]; $id_start_ci = \strtolower($id_start); $id_name = ''; $context = 'args'; $state = 'id_name'; $lastState = 'closure_args'; break; case \T_USE: $code .= $token[1]; $state = 'use'; break; case \T_DOUBLE_ARROW: $code .= $token[1]; if ($isShortClosure) { $state = 'closure'; } break; case ':': $code .= ':'; $state = 'return'; break; case '{': $code .= '{'; $state = 'closure'; $open++; break; default: $code .= \is_array($token) ? $token[1] : $token; } break; case 'use': switch ($token[0]) { case \T_VARIABLE: $use[] = \substr($token[1], 1); $code .= $token[1]; break; case '{': $code .= '{'; $state = 'closure'; $open++; break; case ':': $code .= ':'; $state = 'return'; break; default: $code .= \is_array($token) ? $token[1] : $token; break; } break; case 'return': switch ($token[0]) { case \T_WHITESPACE: case \T_COMMENT: case \T_DOC_COMMENT: $code .= $token[1]; break; case \T_NS_SEPARATOR: case \T_STRING: $id_start = $token[1]; $id_start_ci = \strtolower($id_start); $id_name = ''; $context = 'return_type'; $state = 'id_name'; $lastState = 'return'; break 2; case \T_NAME_QUALIFIED: [$id_start, $id_start_ci, $id_name] = $this->parseNameQualified($token[1]); $context = 'return_type'; $state = 'id_name'; $lastState = 'return'; break 2; case \T_DOUBLE_ARROW: $code .= $token[1]; if ($isShortClosure) { $state = 'closure'; } break; case '{': $code .= '{'; $state = 'closure'; $open++; break; default: $code .= \is_array($token) ? $token[1] : $token; break; } break; case 'closure': switch ($token[0]) { case \T_CURLY_OPEN: case \T_DOLLAR_OPEN_CURLY_BRACES: case '{': $code .= \is_array($token) ? $token[1] : $token; $open++; break; case '}': $code .= '}'; if (--$open === 0 && !$isShortClosure) { break 3; } elseif ($inside_structure) { $inside_structure = !($open === $inside_structure_mark); } break; case '(': case '[': $code .= $token[0]; if ($isShortClosure) { $open++; } break; case ')': case ']': if ($isShortClosure) { if ($open === 0) { break 3; } $open--; } $code .= $token[0]; break; case ',': case ';': if ($isShortClosure && $open === 0) { break 3; } $code .= $token[0]; break; case \T_LINE: $code .= $token[2] - $line + $lineAdd; break; case \T_FILE: $code .= $_file; break; case \T_DIR: $code .= $_dir; break; case \T_NS_C: $code .= $_namespace; break; case \T_CLASS_C: $code .= $inside_structure ? $token[1] : $_class; break; case \T_FUNC_C: $code .= $inside_structure ? $token[1] : $_function; break; case \T_METHOD_C: $code .= $inside_structure ? $token[1] : $_method; break; case \T_COMMENT: if (\substr($token[1], 0, 8) === '#trackme') { $timestamp = \time(); $code .= '/**' . \PHP_EOL; $code .= '* Date : ' . \date(\DATE_W3C, $timestamp) . \PHP_EOL; $code .= '* Timestamp : ' . $timestamp . \PHP_EOL; $code .= '* Line : ' . ($line + 1) . \PHP_EOL; $code .= '* File : ' . $_file . \PHP_EOL . '*/' . \PHP_EOL; $lineAdd += 5; } else { $code .= $token[1]; } break; case \T_VARIABLE: if ($token[1] == '$this' && !$inside_structure) { $isUsingThisObject = \true; } $code .= $token[1]; break; case \T_STATIC: case \T_NS_SEPARATOR: case \T_STRING: $id_start = $token[1]; $id_start_ci = \strtolower($id_start); $id_name = ''; $context = 'root'; $state = 'id_name'; $lastState = 'closure'; break 2; case \T_NAME_QUALIFIED: [$id_start, $id_start_ci, $id_name] = $this->parseNameQualified($token[1]); $context = 'root'; $state = 'id_name'; $lastState = 'closure'; break 2; case \T_NEW: $code .= $token[1]; $context = 'new'; $state = 'id_start'; $lastState = 'closure'; break 2; case \T_USE: $code .= $token[1]; $context = 'use'; $state = 'id_start'; $lastState = 'closure'; break; case \T_INSTANCEOF: case \T_INSTEADOF: $code .= $token[1]; $context = 'instanceof'; $state = 'id_start'; $lastState = 'closure'; break; case \T_OBJECT_OPERATOR: case \T_NULLSAFE_OBJECT_OPERATOR: case \T_DOUBLE_COLON: $code .= $token[1]; $lastState = 'closure'; $state = 'ignore_next'; break; case \T_FUNCTION: $code .= $token[1]; $state = 'closure_args'; if (!$inside_structure) { $inside_structure = \true; $inside_structure_mark = $open; } break; case \T_TRAIT_C: if ($_trait === null) { $startLine = $this->getStartLine(); $endLine = $this->getEndLine(); $structures = $this->getStructures(); $_trait = ''; foreach ($structures as &$struct) { if ($struct['type'] === 'trait' && $struct['start'] <= $startLine && $struct['end'] >= $endLine) { $_trait = ($ns == '' ? '' : $ns . '\\') . $struct['name']; break; } } $_trait = \var_export($_trait, \true); } $code .= $_trait; break; default: $code .= \is_array($token) ? $token[1] : $token; } break; case 'ignore_next': switch ($token[0]) { case \T_WHITESPACE: case \T_COMMENT: case \T_DOC_COMMENT: $code .= $token[1]; break; case \T_CLASS: case \T_NEW: case \T_STATIC: case \T_VARIABLE: case \T_STRING: case \T_CLASS_C: case \T_FILE: case \T_DIR: case \T_METHOD_C: case \T_FUNC_C: case \T_FUNCTION: case \T_INSTANCEOF: case \T_LINE: case \T_NS_C: case \T_TRAIT_C: case \T_USE: $code .= $token[1]; $state = $lastState; break; default: $state = $lastState; $i--; } break; case 'id_start': switch ($token[0]) { case \T_WHITESPACE: case \T_COMMENT: case \T_DOC_COMMENT: $code .= $token[1]; break; case \T_NS_SEPARATOR: case \T_NAME_FULLY_QUALIFIED: case \T_STRING: case \T_STATIC: $id_start = $token[1]; $id_start_ci = \strtolower($id_start); $id_name = ''; $state = 'id_name'; break 2; case \T_NAME_QUALIFIED: [$id_start, $id_start_ci, $id_name] = $this->parseNameQualified($token[1]); $state = 'id_name'; break 2; case \T_VARIABLE: $code .= $token[1]; $state = $lastState; break; case \T_CLASS: $code .= $token[1]; $state = 'anonymous'; break; default: $i--; //reprocess last $state = 'id_name'; } break; case 'id_name': switch ($token[0]) { case $token[0] === ':' && $context !== 'instanceof': if ($lastState === 'closure' && $context === 'root') { $state = 'closure'; $code .= $id_start . $token; } break; case \T_NAME_QUALIFIED: case \T_NS_SEPARATOR: case \T_STRING: case \T_WHITESPACE: case \T_COMMENT: case \T_DOC_COMMENT: $id_name .= $token[1]; break; case '(': if ($isShortClosure) { $open++; } if ($context === 'new' || \false !== \strpos($id_name, '\\')) { if ($id_start_ci === 'self' || $id_start_ci === 'static') { if (!$inside_structure) { $isUsingScope = \true; } } elseif ($id_start !== '\\' && !\in_array($id_start_ci, $class_keywords)) { if ($classes === null) { $classes = $this->getClasses(); } if (isset($classes[$id_start_ci])) { $id_start = $classes[$id_start_ci]; } if ($id_start[0] !== '\\') { $id_start = $nsf . '\\' . $id_start; } } } else { if ($id_start !== '\\') { if ($functions === null) { $functions = $this->getFunctions(); } if (isset($functions[$id_start_ci])) { $id_start = $functions[$id_start_ci]; } elseif ($nsf !== '\\' && \function_exists($nsf . '\\' . $id_start)) { $id_start = $nsf . '\\' . $id_start; // Cache it to functions array $functions[$id_start_ci] = $id_start; } } } $code .= $id_start . $id_name . '('; $state = $lastState; break; case \T_VARIABLE: case \T_DOUBLE_COLON: if ($id_start !== '\\') { if ($id_start_ci === 'self' || $id_start_ci === 'parent') { if (!$inside_structure) { $isUsingScope = \true; } } elseif ($id_start_ci === 'static') { if (!$inside_structure) { $isUsingScope = $token[0] === \T_DOUBLE_COLON; } } elseif (!(\PHP_MAJOR_VERSION >= 7 && \in_array($id_start_ci, $builtin_types))) { if ($classes === null) { $classes = $this->getClasses(); } if (isset($classes[$id_start_ci])) { $id_start = $classes[$id_start_ci]; } if ($id_start[0] !== '\\') { $id_start = $nsf . '\\' . $id_start; } } } $code .= $id_start . $id_name . $token[1]; $state = $token[0] === \T_DOUBLE_COLON ? 'ignore_next' : $lastState; break; default: if ($id_start !== '\\' && !\defined($id_start)) { if ($constants === null) { $constants = $this->getConstants(); } if (isset($constants[$id_start])) { $id_start = $constants[$id_start]; } elseif ($context === 'new') { if (\in_array($id_start_ci, $class_keywords)) { if (!$inside_structure) { $isUsingScope = \true; } } else { if ($classes === null) { $classes = $this->getClasses(); } if (isset($classes[$id_start_ci])) { $id_start = $classes[$id_start_ci]; } if ($id_start[0] !== '\\') { $id_start = $nsf . '\\' . $id_start; } } } elseif ($context === 'use' || $context === 'instanceof' || $context === 'args' || $context === 'return_type' || $context === 'extends' || $context === 'root') { if (\in_array($id_start_ci, $class_keywords)) { if (!$inside_structure && !$id_start_ci === 'static') { $isUsingScope = \true; } } elseif (!(\PHP_MAJOR_VERSION >= 7 && \in_array($id_start_ci, $builtin_types))) { if ($classes === null) { $classes = $this->getClasses(); } if (isset($classes[$id_start_ci])) { $id_start = $classes[$id_start_ci]; } if ($id_start[0] !== '\\') { $id_start = $nsf . '\\' . $id_start; } } } } $code .= $id_start . $id_name; $state = $lastState; $i--; } break; case 'anonymous': switch ($token[0]) { case \T_NAME_QUALIFIED: [$id_start, $id_start_ci, $id_name] = $this->parseNameQualified($token[1]); $state = 'id_name'; $lastState = 'anonymous'; break 2; case \T_NS_SEPARATOR: case \T_STRING: $id_start = $token[1]; $id_start_ci = \strtolower($id_start); $id_name = ''; $state = 'id_name'; $context = 'extends'; $lastState = 'anonymous'; break; case '{': $state = 'closure'; if (!$inside_structure) { $inside_structure = \true; $inside_structure_mark = $open; } $i--; break; default: $code .= \is_array($token) ? $token[1] : $token; } break; } } if ($isShortClosure) { $this->useVariables = $this->getStaticVariables(); } else { $this->useVariables = empty($use) ? $use : \array_intersect_key($this->getStaticVariables(), \array_flip($use)); } $this->isShortClosure = $isShortClosure; $this->isBindingRequired = $isUsingThisObject; $this->isScopeRequired = $isUsingScope; if (\PHP_VERSION_ID >= 80100) { $attributesCode = \array_map(function ($attribute) { $arguments = $attribute->getArguments(); $name = $attribute->getName(); $arguments = \implode(', ', \array_map(function ($argument, $key) { $argument = \sprintf("'%s'", \str_replace("'", "\\'", $argument)); if (\is_string($key)) { $argument = \sprintf('%s: %s', $key, $argument); } return $argument; }, $arguments, \array_keys($arguments))); return "#[{$name}({$arguments})]"; }, $this->getAttributes()); if (!empty($attributesCode)) { $code = \implode("\n", \array_merge($attributesCode, [$code])); } } $this->code = $code; return $this->code; } /** * Get PHP native built in types. * * @return array */ protected static function getBuiltinTypes() { // PHP 8.1 if (\PHP_VERSION_ID >= 80100) { return ['array', 'callable', 'string', 'int', 'bool', 'float', 'iterable', 'void', 'object', 'mixed', 'false', 'null', 'never']; } // PHP 8 if (\PHP_MAJOR_VERSION === 8) { return ['array', 'callable', 'string', 'int', 'bool', 'float', 'iterable', 'void', 'object', 'mixed', 'false', 'null']; } // PHP 7 switch (\PHP_MINOR_VERSION) { case 0: return ['array', 'callable', 'string', 'int', 'bool', 'float']; case 1: return ['array', 'callable', 'string', 'int', 'bool', 'float', 'iterable', 'void']; default: return ['array', 'callable', 'string', 'int', 'bool', 'float', 'iterable', 'void', 'object']; } } /** * Gets the use variables by the closure. * * @return array */ public function getUseVariables() { if ($this->useVariables !== null) { return $this->useVariables; } $tokens = $this->getTokens(); $use = []; $state = 'start'; foreach ($tokens as &$token) { $is_array = \is_array($token); switch ($state) { case 'start': if ($is_array && $token[0] === \T_USE) { $state = 'use'; } break; case 'use': if ($is_array) { if ($token[0] === \T_VARIABLE) { $use[] = \substr($token[1], 1); } } elseif ($token == ')') { break 2; } break; } } $this->useVariables = empty($use) ? $use : \array_intersect_key($this->getStaticVariables(), \array_flip($use)); return $this->useVariables; } /** * Checks if binding is required. * * @return bool */ public function isBindingRequired() { if ($this->isBindingRequired === null) { $this->getCode(); } return $this->isBindingRequired; } /** * Checks if access to the scope is required. * * @return bool */ public function isScopeRequired() { if ($this->isScopeRequired === null) { $this->getCode(); } return $this->isScopeRequired; } /** * The hash of the current file name. * * @return string */ protected function getHashedFileName() { if ($this->hashedName === null) { $this->hashedName = \sha1($this->getFileName()); } return $this->hashedName; } /** * Get the file tokens. * * @return array */ protected function getFileTokens() { $key = $this->getHashedFileName(); if (!isset(static::$files[$key])) { static::$files[$key] = \token_get_all(\file_get_contents($this->getFileName())); } return static::$files[$key]; } /** * Get the tokens. * * @return array */ protected function getTokens() { if ($this->tokens === null) { $tokens = $this->getFileTokens(); $startLine = $this->getStartLine(); $endLine = $this->getEndLine(); $results = []; $start = \false; foreach ($tokens as &$token) { if (!\is_array($token)) { if ($start) { $results[] = $token; } continue; } $line = $token[2]; if ($line <= $endLine) { if ($line >= $startLine) { $start = \true; $results[] = $token; } continue; } break; } $this->tokens = $results; } return $this->tokens; } /** * Get the classes. * * @return array */ protected function getClasses() { $key = $this->getHashedFileName(); if (!isset(static::$classes[$key])) { $this->fetchItems(); } return static::$classes[$key]; } /** * Get the functions. * * @return array */ protected function getFunctions() { $key = $this->getHashedFileName(); if (!isset(static::$functions[$key])) { $this->fetchItems(); } return static::$functions[$key]; } /** * Gets the constants. * * @return array */ protected function getConstants() { $key = $this->getHashedFileName(); if (!isset(static::$constants[$key])) { $this->fetchItems(); } return static::$constants[$key]; } /** * Get the structures. * * @return array */ protected function getStructures() { $key = $this->getHashedFileName(); if (!isset(static::$structures[$key])) { $this->fetchItems(); } return static::$structures[$key]; } /** * Fetch the items. * * @return void. */ protected function fetchItems() { $key = $this->getHashedFileName(); $classes = []; $functions = []; $constants = []; $structures = []; $tokens = $this->getFileTokens(); $open = 0; $state = 'start'; $lastState = ''; $prefix = ''; $name = ''; $alias = ''; $isFunc = $isConst = \false; $startLine = $endLine = 0; $structType = $structName = ''; $structIgnore = \false; foreach ($tokens as $token) { switch ($state) { case 'start': switch ($token[0]) { case \T_CLASS: case \T_INTERFACE: case \T_TRAIT: $state = 'before_structure'; $startLine = $token[2]; $structType = $token[0] == \T_CLASS ? 'class' : ($token[0] == \T_INTERFACE ? 'interface' : 'trait'); break; case \T_USE: $state = 'use'; $prefix = $name = $alias = ''; $isFunc = $isConst = \false; break; case \T_FUNCTION: $state = 'structure'; $structIgnore = \true; break; case \T_NEW: $state = 'new'; break; case \T_OBJECT_OPERATOR: case \T_DOUBLE_COLON: $state = 'invoke'; break; } break; case 'use': switch ($token[0]) { case \T_FUNCTION: $isFunc = \true; break; case \T_CONST: $isConst = \true; break; case \T_NS_SEPARATOR: $name .= $token[1]; break; case \T_STRING: $name .= $token[1]; $alias = $token[1]; break; case \T_NAME_QUALIFIED: $name .= $token[1]; $pieces = \explode('\\', $token[1]); $alias = \end($pieces); break; case \T_AS: $lastState = 'use'; $state = 'alias'; break; case '{': $prefix = $name; $name = $alias = ''; $state = 'use-group'; break; case ',': case ';': if ($name === '' || $name[0] !== '\\') { $name = '\\' . $name; } if ($alias !== '') { if ($isFunc) { $functions[\strtolower($alias)] = $name; } elseif ($isConst) { $constants[$alias] = $name; } else { $classes[\strtolower($alias)] = $name; } } $name = $alias = ''; $state = $token === ';' ? 'start' : 'use'; break; } break; case 'use-group': switch ($token[0]) { case \T_NS_SEPARATOR: $name .= $token[1]; break; case \T_NAME_QUALIFIED: $name .= $token[1]; $pieces = \explode('\\', $token[1]); $alias = \end($pieces); break; case \T_STRING: $name .= $token[1]; $alias = $token[1]; break; case \T_AS: $lastState = 'use-group'; $state = 'alias'; break; case ',': case '}': if ($prefix === '' || $prefix[0] !== '\\') { $prefix = '\\' . $prefix; } if ($alias !== '') { if ($isFunc) { $functions[\strtolower($alias)] = $prefix . $name; } elseif ($isConst) { $constants[$alias] = $prefix . $name; } else { $classes[\strtolower($alias)] = $prefix . $name; } } $name = $alias = ''; $state = $token === '}' ? 'use' : 'use-group'; break; } break; case 'alias': if ($token[0] === \T_STRING) { $alias = $token[1]; $state = $lastState; } break; case 'new': switch ($token[0]) { case \T_WHITESPACE: case \T_COMMENT: case \T_DOC_COMMENT: break 2; case \T_CLASS: $state = 'structure'; $structIgnore = \true; break; default: $state = 'start'; } break; case 'invoke': switch ($token[0]) { case \T_WHITESPACE: case \T_COMMENT: case \T_DOC_COMMENT: break 2; default: $state = 'start'; } break; case 'before_structure': if ($token[0] == \T_STRING) { $structName = $token[1]; $state = 'structure'; } break; case 'structure': switch ($token[0]) { case '{': case \T_CURLY_OPEN: case \T_DOLLAR_OPEN_CURLY_BRACES: $open++; break; case '}': if (--$open == 0) { if (!$structIgnore) { $structures[] = ['type' => $structType, 'name' => $structName, 'start' => $startLine, 'end' => $endLine]; } $structIgnore = \false; $state = 'start'; } break; default: if (\is_array($token)) { $endLine = $token[2]; } } break; } } static::$classes[$key] = $classes; static::$functions[$key] = $functions; static::$constants[$key] = $constants; static::$structures[$key] = $structures; } /** * Returns the namespace associated to the closure. * * @return string */ protected function getClosureNamespaceName() { $ns = $this->getNamespaceName(); // First class callables... if ($this->getName() !== '{closure}' && empty($ns) && !\is_null($this->getClosureScopeClass())) { $ns = $this->getClosureScopeClass()->getNamespaceName(); } return $ns; } /** * Parse the given token. * * @param string $token * @return array */ protected function parseNameQualified($token) { $pieces = \explode('\\', $token); $id_start = \array_shift($pieces); $id_start_ci = \strtolower($id_start); $id_name = '\\' . \implode('\\', $pieces); return [$id_start, $id_start_ci, $id_name]; } } laravel/serializable-closure/src/Support/ClosureScope.php 0000644 00000000611 14717614651 0017706 0 ustar 00 <?php namespace ElementorProDeps\Laravel\SerializableClosure\Support; use SplObjectStorage; class ClosureScope extends SplObjectStorage { /** * The number of serializations in current scope. * * @var int */ public $serializations = 0; /** * The number of closures that have to be serialized. * * @var int */ public $toSerialize = 0; } laravel/serializable-closure/src/Support/SelfReference.php 0000644 00000000622 14717614651 0020012 0 ustar 00 <?php namespace ElementorProDeps\Laravel\SerializableClosure\Support; class SelfReference { /** * The unique hash representing the object. * * @var string */ public $hash; /** * Creates a new self reference instance. * * @param string $hash * @return void */ public function __construct($hash) { $this->hash = $hash; } } laravel/serializable-closure/src/Support/ClosureStream.php 0000644 00000007027 14717614651 0020100 0 ustar 00 <?php namespace ElementorProDeps\Laravel\SerializableClosure\Support; #[\AllowDynamicProperties] class ClosureStream { /** * The stream protocol. */ const STREAM_PROTO = 'laravel-serializable-closure'; /** * Checks if this stream is registered. * * @var bool */ protected static $isRegistered = \false; /** * The stream content. * * @var string */ protected $content; /** * The stream content. * * @var int */ protected $length; /** * The stream pointer. * * @var int */ protected $pointer = 0; /** * Opens file or URL. * * @param string $path * @param string $mode * @param string $options * @param string|null $opened_path * @return bool */ public function stream_open($path, $mode, $options, &$opened_path) { $this->content = "<?php\nreturn " . \substr($path, \strlen(static::STREAM_PROTO . '://')) . ';'; $this->length = \strlen($this->content); return \true; } /** * Read from stream. * * @param int $count * @return string */ public function stream_read($count) { $value = \substr($this->content, $this->pointer, $count); $this->pointer += $count; return $value; } /** * Tests for end-of-file on a file pointer. * * @return bool */ public function stream_eof() { return $this->pointer >= $this->length; } /** * Change stream options. * * @param int $option * @param int $arg1 * @param int $arg2 * @return bool */ public function stream_set_option($option, $arg1, $arg2) { return \false; } /** * Retrieve information about a file resource. * * @return array|bool */ public function stream_stat() { $stat = \stat(__FILE__); // @phpstan-ignore-next-line $stat[7] = $stat['size'] = $this->length; return $stat; } /** * Retrieve information about a file. * * @param string $path * @param int $flags * @return array|bool */ public function url_stat($path, $flags) { $stat = \stat(__FILE__); // @phpstan-ignore-next-line $stat[7] = $stat['size'] = $this->length; return $stat; } /** * Seeks to specific location in a stream. * * @param int $offset * @param int $whence * @return bool */ public function stream_seek($offset, $whence = \SEEK_SET) { $crt = $this->pointer; switch ($whence) { case \SEEK_SET: $this->pointer = $offset; break; case \SEEK_CUR: $this->pointer += $offset; break; case \SEEK_END: $this->pointer = $this->length + $offset; break; } if ($this->pointer < 0 || $this->pointer >= $this->length) { $this->pointer = $crt; return \false; } return \true; } /** * Retrieve the current position of a stream. * * @return int */ public function stream_tell() { return $this->pointer; } /** * Registers the stream. * * @return void */ public static function register() { if (!static::$isRegistered) { static::$isRegistered = \stream_wrapper_register(static::STREAM_PROTO, __CLASS__); } } } laravel/serializable-closure/src/UnsignedSerializableClosure.php 0000644 00000003322 14717614651 0021266 0 ustar 00 <?php namespace ElementorProDeps\Laravel\SerializableClosure; use Closure; use ElementorProDeps\Laravel\SerializableClosure\Exceptions\PhpVersionNotSupportedException; class UnsignedSerializableClosure { /** * The closure's serializable. * * @var \Laravel\SerializableClosure\Contracts\Serializable */ protected $serializable; /** * Creates a new serializable closure instance. * * @param \Closure $closure * @return void */ public function __construct(Closure $closure) { if (\PHP_VERSION_ID < 70400) { throw new PhpVersionNotSupportedException(); } $this->serializable = new Serializers\Native($closure); } /** * Resolve the closure with the given arguments. * * @return mixed */ public function __invoke() { if (\PHP_VERSION_ID < 70400) { throw new PhpVersionNotSupportedException(); } return \call_user_func_array($this->serializable, \func_get_args()); } /** * Gets the closure. * * @return \Closure */ public function getClosure() { if (\PHP_VERSION_ID < 70400) { throw new PhpVersionNotSupportedException(); } return $this->serializable->getClosure(); } /** * Get the serializable representation of the closure. * * @return array */ public function __serialize() { return ['serializable' => $this->serializable]; } /** * Restore the closure after serialization. * * @param array $data * @return void */ public function __unserialize($data) { $this->serializable = $data['serializable']; } } laravel/serializable-closure/src/Serializers/Signed.php 0000644 00000004172 14717614651 0017337 0 ustar 00 <?php namespace ElementorProDeps\Laravel\SerializableClosure\Serializers; use ElementorProDeps\Laravel\SerializableClosure\Contracts\Serializable; use ElementorProDeps\Laravel\SerializableClosure\Exceptions\InvalidSignatureException; use ElementorProDeps\Laravel\SerializableClosure\Exceptions\MissingSecretKeyException; class Signed implements Serializable { /** * The signer that will sign and verify the closure's signature. * * @var \Laravel\SerializableClosure\Contracts\Signer|null */ public static $signer; /** * The closure to be serialized/unserialized. * * @var \Closure */ protected $closure; /** * Creates a new serializable closure instance. * * @param \Closure $closure * @return void */ public function __construct($closure) { $this->closure = $closure; } /** * Resolve the closure with the given arguments. * * @return mixed */ public function __invoke() { return \call_user_func_array($this->closure, \func_get_args()); } /** * Gets the closure. * * @return \Closure */ public function getClosure() { return $this->closure; } /** * Get the serializable representation of the closure. * * @return array */ public function __serialize() { if (!static::$signer) { throw new MissingSecretKeyException(); } return static::$signer->sign(\serialize(new Native($this->closure))); } /** * Restore the closure after serialization. * * @param array $signature * @return void * * @throws \Laravel\SerializableClosure\Exceptions\InvalidSignatureException */ public function __unserialize($signature) { if (static::$signer && !static::$signer->verify($signature)) { throw new InvalidSignatureException(); } /** @var \Laravel\SerializableClosure\Contracts\Serializable $serializable */ $serializable = \unserialize($signature['serializable']); $this->closure = $serializable->getClosure(); } } laravel/serializable-closure/src/Serializers/Native.php 0000644 00000034010 14717614651 0017346 0 ustar 00 <?php namespace ElementorProDeps\Laravel\SerializableClosure\Serializers; use Closure; use DateTimeInterface; use ElementorProDeps\Laravel\SerializableClosure\Contracts\Serializable; use ElementorProDeps\Laravel\SerializableClosure\SerializableClosure; use ElementorProDeps\Laravel\SerializableClosure\Support\ClosureScope; use ElementorProDeps\Laravel\SerializableClosure\Support\ClosureStream; use ElementorProDeps\Laravel\SerializableClosure\Support\ReflectionClosure; use ElementorProDeps\Laravel\SerializableClosure\Support\SelfReference; use ElementorProDeps\Laravel\SerializableClosure\UnsignedSerializableClosure; use ReflectionObject; use UnitEnum; class Native implements Serializable { /** * Transform the use variables before serialization. * * @var \Closure|null */ public static $transformUseVariables; /** * Resolve the use variables after unserialization. * * @var \Closure|null */ public static $resolveUseVariables; /** * The closure to be serialized/unserialized. * * @var \Closure */ protected $closure; /** * The closure's reflection. * * @var \Laravel\SerializableClosure\Support\ReflectionClosure|null */ protected $reflector; /** * The closure's code. * * @var array|null */ protected $code; /** * The closure's reference. * * @var string */ protected $reference; /** * The closure's scope. * * @var \Laravel\SerializableClosure\Support\ClosureScope|null */ protected $scope; /** * The "key" that marks an array as recursive. */ const ARRAY_RECURSIVE_KEY = 'LARAVEL_SERIALIZABLE_RECURSIVE_KEY'; /** * Creates a new serializable closure instance. * * @param \Closure $closure * @return void */ public function __construct(Closure $closure) { $this->closure = $closure; } /** * Resolve the closure with the given arguments. * * @return mixed */ public function __invoke() { return \call_user_func_array($this->closure, \func_get_args()); } /** * Gets the closure. * * @return \Closure */ public function getClosure() { return $this->closure; } /** * Get the serializable representation of the closure. * * @return array */ public function __serialize() { if ($this->scope === null) { $this->scope = new ClosureScope(); $this->scope->toSerialize++; } $this->scope->serializations++; $scope = $object = null; $reflector = $this->getReflector(); if ($reflector->isBindingRequired()) { $object = $reflector->getClosureThis(); static::wrapClosures($object, $this->scope); } if ($scope = $reflector->getClosureScopeClass()) { $scope = $scope->name; } $this->reference = \spl_object_hash($this->closure); $this->scope[$this->closure] = $this; $use = $reflector->getUseVariables(); if (static::$transformUseVariables) { $use = \call_user_func(static::$transformUseVariables, $reflector->getUseVariables()); } $code = $reflector->getCode(); $this->mapByReference($use); $data = ['use' => $use, 'function' => $code, 'scope' => $scope, 'this' => $object, 'self' => $this->reference]; if (!--$this->scope->serializations && !--$this->scope->toSerialize) { $this->scope = null; } return $data; } /** * Restore the closure after serialization. * * @param array $data * @return void */ public function __unserialize($data) { ClosureStream::register(); $this->code = $data; unset($data); $this->code['objects'] = []; if ($this->code['use']) { $this->scope = new ClosureScope(); if (static::$resolveUseVariables) { $this->code['use'] = \call_user_func(static::$resolveUseVariables, $this->code['use']); } $this->mapPointers($this->code['use']); \extract($this->code['use'], \EXTR_OVERWRITE | \EXTR_REFS); $this->scope = null; } $this->closure = (include ClosureStream::STREAM_PROTO . '://' . $this->code['function']); if ($this->code['this'] === $this) { $this->code['this'] = null; } $this->closure = $this->closure->bindTo($this->code['this'], $this->code['scope']); if (!empty($this->code['objects'])) { foreach ($this->code['objects'] as $item) { $item['property']->setValue($item['instance'], $item['object']->getClosure()); } } $this->code = $this->code['function']; } /** * Ensures the given closures are serializable. * * @param mixed $data * @param \Laravel\SerializableClosure\Support\ClosureScope $storage * @return void */ public static function wrapClosures(&$data, $storage) { if ($data instanceof Closure) { $data = new static($data); } elseif (\is_array($data)) { if (isset($data[self::ARRAY_RECURSIVE_KEY])) { return; } $data[self::ARRAY_RECURSIVE_KEY] = \true; foreach ($data as $key => &$value) { if ($key === self::ARRAY_RECURSIVE_KEY) { continue; } static::wrapClosures($value, $storage); } unset($value); unset($data[self::ARRAY_RECURSIVE_KEY]); } elseif ($data instanceof \stdClass) { if (isset($storage[$data])) { $data = $storage[$data]; return; } $data = $storage[$data] = clone $data; foreach ($data as &$value) { static::wrapClosures($value, $storage); } unset($value); } elseif (\is_object($data) && !$data instanceof static && !$data instanceof UnitEnum) { if (isset($storage[$data])) { $data = $storage[$data]; return; } $instance = $data; $reflection = new ReflectionObject($instance); if (!$reflection->isUserDefined()) { $storage[$instance] = $data; return; } $storage[$instance] = $data = $reflection->newInstanceWithoutConstructor(); do { if (!$reflection->isUserDefined()) { break; } foreach ($reflection->getProperties() as $property) { if ($property->isStatic() || !$property->getDeclaringClass()->isUserDefined()) { continue; } $property->setAccessible(\true); if (\PHP_VERSION >= 7.4 && !$property->isInitialized($instance)) { continue; } $value = $property->getValue($instance); if (\is_array($value) || \is_object($value)) { static::wrapClosures($value, $storage); } $property->setValue($data, $value); } } while ($reflection = $reflection->getParentClass()); } } /** * Gets the closure's reflector. * * @return \Laravel\SerializableClosure\Support\ReflectionClosure */ public function getReflector() { if ($this->reflector === null) { $this->code = null; $this->reflector = new ReflectionClosure($this->closure); } return $this->reflector; } /** * Internal method used to map closure pointers. * * @param mixed $data * @return void */ protected function mapPointers(&$data) { $scope = $this->scope; if ($data instanceof static) { $data =& $data->closure; } elseif (\is_array($data)) { if (isset($data[self::ARRAY_RECURSIVE_KEY])) { return; } $data[self::ARRAY_RECURSIVE_KEY] = \true; foreach ($data as $key => &$value) { if ($key === self::ARRAY_RECURSIVE_KEY) { continue; } elseif ($value instanceof static) { $data[$key] =& $value->closure; } elseif ($value instanceof SelfReference && $value->hash === $this->code['self']) { $data[$key] =& $this->closure; } else { $this->mapPointers($value); } } unset($value); unset($data[self::ARRAY_RECURSIVE_KEY]); } elseif ($data instanceof \stdClass) { if (isset($scope[$data])) { return; } $scope[$data] = \true; foreach ($data as $key => &$value) { if ($value instanceof SelfReference && $value->hash === $this->code['self']) { $data->{$key} =& $this->closure; } elseif (\is_array($value) || \is_object($value)) { $this->mapPointers($value); } } unset($value); } elseif (\is_object($data) && !$data instanceof Closure) { if (isset($scope[$data])) { return; } $scope[$data] = \true; $reflection = new ReflectionObject($data); do { if (!$reflection->isUserDefined()) { break; } foreach ($reflection->getProperties() as $property) { if ($property->isStatic() || !$property->getDeclaringClass()->isUserDefined()) { continue; } $property->setAccessible(\true); if (\PHP_VERSION >= 7.4 && !$property->isInitialized($data)) { continue; } if (\PHP_VERSION >= 8.1 && $property->isReadOnly()) { continue; } $item = $property->getValue($data); if ($item instanceof SerializableClosure || $item instanceof UnsignedSerializableClosure || $item instanceof SelfReference && $item->hash === $this->code['self']) { $this->code['objects'][] = ['instance' => $data, 'property' => $property, 'object' => $item instanceof SelfReference ? $this : $item]; } elseif (\is_array($item) || \is_object($item)) { $this->mapPointers($item); $property->setValue($data, $item); } } } while ($reflection = $reflection->getParentClass()); } } /** * Internal method used to map closures by reference. * * @param mixed $data * @return void */ protected function mapByReference(&$data) { if ($data instanceof Closure) { if ($data === $this->closure) { $data = new SelfReference($this->reference); return; } if (isset($this->scope[$data])) { $data = $this->scope[$data]; return; } $instance = new static($data); $instance->scope = $this->scope; $data = $this->scope[$data] = $instance; } elseif (\is_array($data)) { if (isset($data[self::ARRAY_RECURSIVE_KEY])) { return; } $data[self::ARRAY_RECURSIVE_KEY] = \true; foreach ($data as $key => &$value) { if ($key === self::ARRAY_RECURSIVE_KEY) { continue; } $this->mapByReference($value); } unset($value); unset($data[self::ARRAY_RECURSIVE_KEY]); } elseif ($data instanceof \stdClass) { if (isset($this->scope[$data])) { $data = $this->scope[$data]; return; } $instance = $data; $this->scope[$instance] = $data = clone $data; foreach ($data as &$value) { $this->mapByReference($value); } unset($value); } elseif (\is_object($data) && !$data instanceof SerializableClosure && !$data instanceof UnsignedSerializableClosure) { if (isset($this->scope[$data])) { $data = $this->scope[$data]; return; } $instance = $data; if ($data instanceof DateTimeInterface) { $this->scope[$instance] = $data; return; } if ($data instanceof UnitEnum) { $this->scope[$instance] = $data; return; } $reflection = new ReflectionObject($data); if (!$reflection->isUserDefined()) { $this->scope[$instance] = $data; return; } $this->scope[$instance] = $data = $reflection->newInstanceWithoutConstructor(); do { if (!$reflection->isUserDefined()) { break; } foreach ($reflection->getProperties() as $property) { if ($property->isStatic() || !$property->getDeclaringClass()->isUserDefined()) { continue; } $property->setAccessible(\true); if (\PHP_VERSION >= 7.4 && !$property->isInitialized($instance)) { continue; } $value = $property->getValue($instance); if (\is_array($value) || \is_object($value)) { $this->mapByReference($value); } $property->setValue($data, $value); } } while ($reflection = $reflection->getParentClass()); } } } laravel/serializable-closure/src/Contracts/Signer.php 0000644 00000000624 14717614651 0017017 0 ustar 00 <?php namespace ElementorProDeps\Laravel\SerializableClosure\Contracts; interface Signer { /** * Sign the given serializable. * * @param string $serializable * @return array */ public function sign($serializable); /** * Verify the given signature. * * @param array $signature * @return bool */ public function verify($signature); } laravel/serializable-closure/src/Contracts/Serializable.php 0000644 00000000561 14717614651 0020176 0 ustar 00 <?php namespace ElementorProDeps\Laravel\SerializableClosure\Contracts; interface Serializable { /** * Resolve the closure with the given arguments. * * @return mixed */ public function __invoke(); /** * Gets the closure that got serialized/unserialized. * * @return \Closure */ public function getClosure(); } laravel/serializable-closure/src/Exceptions/InvalidSignatureException.php 0000644 00000000671 14717614651 0023102 0 ustar 00 <?php namespace ElementorProDeps\Laravel\SerializableClosure\Exceptions; use Exception; class InvalidSignatureException extends Exception { /** * Create a new exception instance. * * @param string $message * @return void */ public function __construct($message = 'Your serialized closure might have been modified or it\'s unsafe to be unserialized.') { parent::__construct($message); } } laravel/serializable-closure/src/Exceptions/MissingSecretKeyException.php 0000644 00000000633 14717614651 0023060 0 ustar 00 <?php namespace ElementorProDeps\Laravel\SerializableClosure\Exceptions; use Exception; class MissingSecretKeyException extends Exception { /** * Create a new exception instance. * * @param string $message * @return void */ public function __construct($message = 'No serializable closure secret key has been specified.') { parent::__construct($message); } } laravel/serializable-closure/src/Exceptions/PhpVersionNotSupportedException.php 0000644 00000000604 14717614651 0024312 0 ustar 00 <?php namespace ElementorProDeps\Laravel\SerializableClosure\Exceptions; use Exception; class PhpVersionNotSupportedException extends Exception { /** * Create a new exception instance. * * @param string $message * @return void */ public function __construct($message = 'PHP 7.3 is not supported.') { parent::__construct($message); } } laravel/serializable-closure/src/Signers/Hmac.php 0000644 00000002035 14717614651 0016110 0 ustar 00 <?php namespace ElementorProDeps\Laravel\SerializableClosure\Signers; use ElementorProDeps\Laravel\SerializableClosure\Contracts\Signer; class Hmac implements Signer { /** * The secret key. * * @var string */ protected $secret; /** * Creates a new signer instance. * * @param string $secret * @return void */ public function __construct($secret) { $this->secret = $secret; } /** * Sign the given serializable. * * @param string $serialized * @return array */ public function sign($serialized) { return ['serializable' => $serialized, 'hash' => \base64_encode(\hash_hmac('sha256', $serialized, $this->secret, \true))]; } /** * Verify the given signature. * * @param array $signature * @return bool */ public function verify($signature) { return \hash_equals(\base64_encode(\hash_hmac('sha256', $signature['serializable'], $this->secret, \true)), $signature['hash']); } } laravel/serializable-closure/src/SerializableClosure.php 0000644 00000006454 14717614651 0017602 0 ustar 00 <?php namespace ElementorProDeps\Laravel\SerializableClosure; use Closure; use ElementorProDeps\Laravel\SerializableClosure\Exceptions\InvalidSignatureException; use ElementorProDeps\Laravel\SerializableClosure\Exceptions\PhpVersionNotSupportedException; use ElementorProDeps\Laravel\SerializableClosure\Serializers\Signed; use ElementorProDeps\Laravel\SerializableClosure\Signers\Hmac; class SerializableClosure { /** * The closure's serializable. * * @var \Laravel\SerializableClosure\Contracts\Serializable */ protected $serializable; /** * Creates a new serializable closure instance. * * @param \Closure $closure * @return void */ public function __construct(Closure $closure) { if (\PHP_VERSION_ID < 70400) { throw new PhpVersionNotSupportedException(); } $this->serializable = Serializers\Signed::$signer ? new Serializers\Signed($closure) : new Serializers\Native($closure); } /** * Resolve the closure with the given arguments. * * @return mixed */ public function __invoke() { if (\PHP_VERSION_ID < 70400) { throw new PhpVersionNotSupportedException(); } return \call_user_func_array($this->serializable, \func_get_args()); } /** * Gets the closure. * * @return \Closure */ public function getClosure() { if (\PHP_VERSION_ID < 70400) { throw new PhpVersionNotSupportedException(); } return $this->serializable->getClosure(); } /** * Create a new unsigned serializable closure instance. * * @param Closure $closure * @return \Laravel\SerializableClosure\UnsignedSerializableClosure */ public static function unsigned(Closure $closure) { return new UnsignedSerializableClosure($closure); } /** * Sets the serializable closure secret key. * * @param string|null $secret * @return void */ public static function setSecretKey($secret) { Serializers\Signed::$signer = $secret ? new Hmac($secret) : null; } /** * Sets the serializable closure secret key. * * @param \Closure|null $transformer * @return void */ public static function transformUseVariablesUsing($transformer) { Serializers\Native::$transformUseVariables = $transformer; } /** * Sets the serializable closure secret key. * * @param \Closure|null $resolver * @return void */ public static function resolveUseVariablesUsing($resolver) { Serializers\Native::$resolveUseVariables = $resolver; } /** * Get the serializable representation of the closure. * * @return array */ public function __serialize() { return ['serializable' => $this->serializable]; } /** * Restore the closure after serialization. * * @param array $data * @return void * * @throws \Laravel\SerializableClosure\Exceptions\InvalidSignatureException */ public function __unserialize($data) { if (Signed::$signer && !$data['serializable'] instanceof Signed) { throw new InvalidSignatureException(); } $this->serializable = $data['serializable']; } } laravel/serializable-closure/composer.json 0000644 00000002720 14717614651 0015051 0 ustar 00 { "name": "laravel\/serializable-closure", "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", "keywords": [ "laravel", "Serializable", "closure" ], "license": "MIT", "support": { "issues": "https:\/\/github.com\/laravel\/serializable-closure\/issues", "source": "https:\/\/github.com\/laravel\/serializable-closure" }, "authors": [ { "name": "Taylor Otwell", "email": "taylor@laravel.com" }, { "name": "Nuno Maduro", "email": "nuno@laravel.com" } ], "require": { "php": "^7.3|^8.0" }, "require-dev": { "illuminate\/support": "^8.0|^9.0|^10.0|^11.0", "nesbot\/carbon": "^2.61|^3.0", "pestphp\/pest": "^1.21.3", "phpstan\/phpstan": "^1.8.2", "symfony\/var-dumper": "^5.4.11|^6.2.0|^7.0.0" }, "autoload": { "psr-4": { "ElementorProDeps\\Laravel\\SerializableClosure\\": "src\/" } }, "autoload-dev": { "psr-4": { "ElementorProDeps\\Tests\\": "tests\/" } }, "extra": { "branch-alias": { "dev-master": "1.x-dev" } }, "config": { "sort-packages": true, "allow-plugins": { "pestphp\/pest-plugin": true } }, "minimum-stability": "dev", "prefer-stable": true } psr/container/src/ContainerInterface.php 0000644 00000002040 14717614651 0014405 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\Psr\Container; /** * Describes the interface of a container that exposes methods to read its entries. */ interface ContainerInterface { /** * Finds an entry of the container by its identifier and returns it. * * @param string $id Identifier of the entry to look for. * * @throws NotFoundExceptionInterface No entry was found for **this** identifier. * @throws ContainerExceptionInterface Error while retrieving the entry. * * @return mixed Entry. */ public function get(string $id); /** * Returns true if the container can return an entry for the given identifier. * Returns false otherwise. * * `has($id)` returning true does not mean that `get($id)` will not throw an exception. * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`. * * @param string $id Identifier of the entry to look for. * * @return bool */ public function has(string $id); } psr/container/src/NotFoundExceptionInterface.php 0000644 00000000257 14717614651 0016106 0 ustar 00 <?php namespace ElementorProDeps\Psr\Container; /** * No entry was found in the container. */ interface NotFoundExceptionInterface extends ContainerExceptionInterface { } psr/container/src/ContainerExceptionInterface.php 0000644 00000000310 14717614651 0016262 0 ustar 00 <?php namespace ElementorProDeps\Psr\Container; use Throwable; /** * Base interface representing a generic exception in a container. */ interface ContainerExceptionInterface extends Throwable { } psr/container/composer.json 0000644 00000001167 14717614651 0012075 0 ustar 00 { "name": "psr\/container", "type": "library", "description": "Common Container Interface (PHP FIG PSR-11)", "keywords": [ "psr", "psr-11", "container", "container-interop", "container-interface" ], "homepage": "https:\/\/github.com\/php-fig\/container", "license": "MIT", "authors": [ { "name": "PHP-FIG", "homepage": "https:\/\/www.php-fig.org\/" } ], "require": { "php": ">=7.4.0" }, "autoload": { "psr-4": { "ElementorProDeps\\Psr\\Container\\": "src\/" } } } psr/container/LICENSE 0000644 00000002171 14717614651 0010354 0 ustar 00 The MIT License (MIT) Copyright (c) 2013-2016 container-interop Copyright (c) 2016 PHP Framework Interoperability Group Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. php-di/phpdoc-reader/src/PhpDocReader/PhpDocReader.php 0000644 00000023132 14717614651 0016557 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\PhpDocReader; use ElementorProDeps\PhpDocReader\PhpParser\UseStatementParser; use ReflectionClass; use ReflectionMethod; use ReflectionParameter; use ReflectionProperty; use Reflector; /** * PhpDoc reader */ class PhpDocReader { /** @var UseStatementParser */ private $parser; private const PRIMITIVE_TYPES = ['bool' => 'bool', 'boolean' => 'bool', 'string' => 'string', 'int' => 'int', 'integer' => 'int', 'float' => 'float', 'double' => 'float', 'array' => 'array', 'object' => 'object', 'callable' => 'callable', 'resource' => 'resource', 'mixed' => 'mixed', 'iterable' => 'iterable']; /** @var bool */ private $ignorePhpDocErrors; /** * @param bool $ignorePhpDocErrors Enable or disable throwing errors when PhpDoc errors occur (when parsing annotations). */ public function __construct(bool $ignorePhpDocErrors = \false) { $this->parser = new UseStatementParser(); $this->ignorePhpDocErrors = $ignorePhpDocErrors; } /** * Parse the docblock of the property to get the type (class or primitive type) of the var annotation. * * @return string|null Type of the property (content of var annotation) * @throws AnnotationException */ public function getPropertyType(ReflectionProperty $property) : ?string { return $this->readPropertyType($property, \true); } /** * Parse the docblock of the property to get the class of the var annotation. * * @return string|null Type of the property (content of var annotation) * @throws AnnotationException */ public function getPropertyClass(ReflectionProperty $property) : ?string { return $this->readPropertyType($property, \false); } private function readPropertyType(ReflectionProperty $property, bool $allowPrimitiveTypes) : ?string { // Get the content of the @var annotation $docComment = $property->getDocComment(); if (!$docComment) { return null; } if (\preg_match('/@var\\s+([^\\s]+)/', $docComment, $matches)) { [, $type] = $matches; } else { return null; } // Ignore primitive types if (isset(self::PRIMITIVE_TYPES[$type])) { if ($allowPrimitiveTypes) { return self::PRIMITIVE_TYPES[$type]; } return null; } // Ignore types containing special characters ([], <> ...) if (!\preg_match('/^[a-zA-Z0-9\\\\_]+$/', $type)) { return null; } $class = $property->getDeclaringClass(); // If the class name is not fully qualified (i.e. doesn't start with a \) if ($type[0] !== '\\') { // Try to resolve the FQN using the class context $resolvedType = $this->tryResolveFqn($type, $class, $property); if (!$resolvedType && !$this->ignorePhpDocErrors) { throw new AnnotationException(\sprintf('The @var annotation on %s::%s contains a non existent class "%s". ' . 'Did you maybe forget to add a "use" statement for this annotation?', $class->name, $property->getName(), $type)); } $type = $resolvedType; } if (!$this->ignorePhpDocErrors && !$this->classExists($type)) { throw new AnnotationException(\sprintf('The @var annotation on %s::%s contains a non existent class "%s"', $class->name, $property->getName(), $type)); } // Remove the leading \ (FQN shouldn't contain it) $type = \is_string($type) ? \ltrim($type, '\\') : null; return $type; } /** * Parse the docblock of the property to get the type (class or primitive type) of the param annotation. * * @return string|null Type of the property (content of var annotation) * @throws AnnotationException */ public function getParameterType(ReflectionParameter $parameter) : ?string { return $this->readParameterClass($parameter, \true); } /** * Parse the docblock of the property to get the class of the param annotation. * * @return string|null Type of the property (content of var annotation) * @throws AnnotationException */ public function getParameterClass(ReflectionParameter $parameter) : ?string { return $this->readParameterClass($parameter, \false); } private function readParameterClass(ReflectionParameter $parameter, bool $allowPrimitiveTypes) : ?string { // Use reflection $parameterType = $parameter->getType(); if ($parameterType && $parameterType instanceof \ReflectionNamedType && !$parameterType->isBuiltin()) { return $parameterType->getName(); } $parameterName = $parameter->name; // Get the content of the @param annotation $method = $parameter->getDeclaringFunction(); $docComment = $method->getDocComment(); if (!$docComment) { return null; } if (\preg_match('/@param\\s+([^\\s]+)\\s+\\$' . $parameterName . '/', $docComment, $matches)) { [, $type] = $matches; } else { return null; } // Ignore primitive types if (isset(self::PRIMITIVE_TYPES[$type])) { if ($allowPrimitiveTypes) { return self::PRIMITIVE_TYPES[$type]; } return null; } // Ignore types containing special characters ([], <> ...) if (!\preg_match('/^[a-zA-Z0-9\\\\_]+$/', $type)) { return null; } $class = $parameter->getDeclaringClass(); // If the class name is not fully qualified (i.e. doesn't start with a \) if ($type[0] !== '\\') { // Try to resolve the FQN using the class context $resolvedType = $this->tryResolveFqn($type, $class, $parameter); if (!$resolvedType && !$this->ignorePhpDocErrors) { throw new AnnotationException(\sprintf('The @param annotation for parameter "%s" of %s::%s contains a non existent class "%s". ' . 'Did you maybe forget to add a "use" statement for this annotation?', $parameterName, $class->name, $method->name, $type)); } $type = $resolvedType; } if (!$this->ignorePhpDocErrors && !$this->classExists($type)) { throw new AnnotationException(\sprintf('The @param annotation for parameter "%s" of %s::%s contains a non existent class "%s"', $parameterName, $class->name, $method->name, $type)); } // Remove the leading \ (FQN shouldn't contain it) $type = \is_string($type) ? \ltrim($type, '\\') : null; return $type; } /** * Attempts to resolve the FQN of the provided $type based on the $class and $member context. * * @return string|null Fully qualified name of the type, or null if it could not be resolved */ private function tryResolveFqn(string $type, ReflectionClass $class, Reflector $member) : ?string { $alias = ($pos = \strpos($type, '\\')) === \false ? $type : \substr($type, 0, $pos); $loweredAlias = \strtolower($alias); // Retrieve "use" statements $uses = $this->parser->parseUseStatements($class); if (isset($uses[$loweredAlias])) { // Imported classes if ($pos !== \false) { return $uses[$loweredAlias] . \substr($type, $pos); } return $uses[$loweredAlias]; } if ($this->classExists($class->getNamespaceName() . '\\' . $type)) { return $class->getNamespaceName() . '\\' . $type; } if (isset($uses['__NAMESPACE__']) && $this->classExists($uses['__NAMESPACE__'] . '\\' . $type)) { // Class namespace return $uses['__NAMESPACE__'] . '\\' . $type; } if ($this->classExists($type)) { // No namespace return $type; } // If all fail, try resolving through related traits return $this->tryResolveFqnInTraits($type, $class, $member); } /** * Attempts to resolve the FQN of the provided $type based on the $class and $member context, specifically searching * through the traits that are used by the provided $class. * * @return string|null Fully qualified name of the type, or null if it could not be resolved */ private function tryResolveFqnInTraits(string $type, ReflectionClass $class, Reflector $member) : ?string { /** @var ReflectionClass[] $traits */ $traits = []; // Get traits for the class and its parents while ($class) { $traits = \array_merge($traits, $class->getTraits()); $class = $class->getParentClass(); } foreach ($traits as $trait) { // Eliminate traits that don't have the property/method/parameter if ($member instanceof ReflectionProperty && !$trait->hasProperty($member->name)) { continue; } if ($member instanceof ReflectionMethod && !$trait->hasMethod($member->name)) { continue; } if ($member instanceof ReflectionParameter && !$trait->hasMethod($member->getDeclaringFunction()->name)) { continue; } // Run the resolver again with the ReflectionClass instance for the trait $resolvedType = $this->tryResolveFqn($type, $trait, $member); if ($resolvedType) { return $resolvedType; } } return null; } private function classExists(string $class) : bool { return \class_exists($class) || \interface_exists($class); } } php-di/phpdoc-reader/src/PhpDocReader/PhpParser/TokenParser.php 0000644 00000012714 14717614651 0020424 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\PhpDocReader\PhpParser; /** * Parses a file for namespaces/use/class declarations. * * Class taken and adapted from doctrine/annotations to avoid pulling the whole package. * * @author Fabien Potencier <fabien@symfony.com> * @author Christian Kaps <christian.kaps@mohiva.com> */ class TokenParser { /** * The token list. * * @var list<mixed[]> */ private $tokens; /** * The number of tokens. * * @var int */ private $numTokens; /** * The current array pointer. * * @var int */ private $pointer = 0; /** * @param string $contents */ public function __construct($contents) { $this->tokens = \token_get_all($contents); // The PHP parser sets internal compiler globals for certain things. Annoyingly, the last docblock comment it // saw gets stored in doc_comment. When it comes to compile the next thing to be include()d this stored // doc_comment becomes owned by the first thing the compiler sees in the file that it considers might have a // docblock. If the first thing in the file is a class without a doc block this would cause calls to // getDocBlock() on said class to return our long lost doc_comment. Argh. // To workaround, cause the parser to parse an empty docblock. Sure getDocBlock() will return this, but at least // it's harmless to us. \token_get_all("<?php\n/**\n *\n */"); $this->numTokens = \count($this->tokens); } /** * Gets all use statements. * * @param string $namespaceName The namespace name of the reflected class. * * @return array<string, string> A list with all found use statements. */ public function parseUseStatements($namespaceName) { $statements = []; while ($token = $this->next()) { if ($token[0] === \T_USE) { $statements = \array_merge($statements, $this->parseUseStatement()); continue; } if ($token[0] !== \T_NAMESPACE || $this->parseNamespace() !== $namespaceName) { continue; } // Get fresh array for new namespace. This is to prevent the parser to collect the use statements // for a previous namespace with the same name. This is the case if a namespace is defined twice // or if a namespace with the same name is commented out. $statements = []; } return $statements; } /** * Gets the next non whitespace and non comment token. * * @param bool $docCommentIsComment If TRUE then a doc comment is considered a comment and skipped. * If FALSE then only whitespace and normal comments are skipped. * * @return mixed[]|string|null The token if exists, null otherwise. */ private function next($docCommentIsComment = \true) { for ($i = $this->pointer; $i < $this->numTokens; $i++) { $this->pointer++; if ($this->tokens[$i][0] === \T_WHITESPACE || $this->tokens[$i][0] === \T_COMMENT || $docCommentIsComment && $this->tokens[$i][0] === \T_DOC_COMMENT) { continue; } return $this->tokens[$i]; } return null; } /** * Parses a single use statement. * * @return array<string, string> A list with all found class names for a use statement. */ private function parseUseStatement() { $groupRoot = ''; $class = ''; $alias = ''; $statements = []; $explicitAlias = \false; while ($token = $this->next()) { if (!$explicitAlias && $token[0] === \T_STRING) { $class .= $token[1]; $alias = $token[1]; } elseif ($explicitAlias && $token[0] === \T_STRING) { $alias = $token[1]; } elseif (\PHP_VERSION_ID >= 80000 && ($token[0] === \T_NAME_QUALIFIED || $token[0] === \T_NAME_FULLY_QUALIFIED)) { $class .= $token[1]; $classSplit = \explode('\\', $token[1]); $alias = $classSplit[\count($classSplit) - 1]; } elseif ($token[0] === \T_NS_SEPARATOR) { $class .= '\\'; $alias = ''; } elseif ($token[0] === \T_AS) { $explicitAlias = \true; $alias = ''; } elseif ($token === ',') { $statements[\strtolower($alias)] = $groupRoot . $class; $class = ''; $alias = ''; $explicitAlias = \false; } elseif ($token === ';') { $statements[\strtolower($alias)] = $groupRoot . $class; break; } elseif ($token === '{') { $groupRoot = $class; $class = ''; } elseif ($token === '}') { continue; } else { break; } } return $statements; } /** * Gets the namespace. * * @return string The found namespace. */ private function parseNamespace() { $name = ''; while (($token = $this->next()) && ($token[0] === \T_STRING || $token[0] === \T_NS_SEPARATOR || \PHP_VERSION_ID >= 80000 && ($token[0] === \T_NAME_QUALIFIED || $token[0] === \T_NAME_FULLY_QUALIFIED))) { $name .= $token[1]; } return $name; } } php-di/phpdoc-reader/src/PhpDocReader/PhpParser/UseStatementParser.php 0000644 00000003452 14717614651 0021764 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\PhpDocReader\PhpParser; use SplFileObject; /** * Parses a file for "use" declarations. * * Class taken and adapted from doctrine/annotations to avoid pulling the whole package. * * Authors: Fabien Potencier <fabien@symfony.com> and Christian Kaps <christian.kaps@mohiva.com> */ class UseStatementParser { /** * @return array A list with use statements in the form (Alias => FQN). */ public function parseUseStatements(\ReflectionClass $class) : array { $filename = $class->getFilename(); if ($filename === \false) { return []; } $content = $this->getFileContent($filename, $class->getStartLine()); if ($content === null) { return []; } $namespace = \preg_quote($class->getNamespaceName(), '/'); $content = \preg_replace('/^.*?(\\bnamespace\\s+' . $namespace . '\\s*[;{].*)$/s', '\\1', $content); $tokenizer = new TokenParser('<?php ' . $content); return $tokenizer->parseUseStatements($class->getNamespaceName()); } /** * Gets the content of the file right up to the given line number. * * @param string $filename The name of the file to load. * @param int $lineNumber The number of lines to read from file. */ private function getFileContent(string $filename, int $lineNumber) : string { if (!\is_file($filename)) { throw new \RuntimeException("Unable to read file {$filename}"); } $content = ''; $lineCnt = 0; $file = new SplFileObject($filename); while (!$file->eof()) { if ($lineCnt++ === $lineNumber) { break; } $content .= $file->fgets(); } return $content; } } php-di/phpdoc-reader/src/PhpDocReader/AnnotationException.php 0000644 00000000305 14717614651 0020245 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\PhpDocReader; /** * We stumbled upon an invalid class/property/method annotation. */ class AnnotationException extends \Exception { } php-di/phpdoc-reader/composer.json 0000644 00000001303 14717614651 0013175 0 ustar 00 { "name": "php-di\/phpdoc-reader", "type": "library", "description": "PhpDocReader parses @var and @param values in PHP docblocks (supports namespaced class names with the same resolution rules as PHP)", "keywords": [ "phpdoc", "reflection" ], "license": "MIT", "autoload": { "psr-4": { "ElementorProDeps\\PhpDocReader\\": "src\/PhpDocReader" } }, "autoload-dev": { "psr-4": { "ElementorProDeps\\UnitTest\\PhpDocReader\\": "tests\/" } }, "require": { "php": ">=7.2.0" }, "require-dev": { "phpunit\/phpunit": "^8.5|^9.0", "mnapoli\/hard-mode": "~0.3.0" } } php-di/phpdoc-reader/LICENSE 0000644 00000002043 14717614651 0011462 0 ustar 00 Copyright (C) 2019 Matthieu Napoli Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. php-di/invoker/src/Reflection/CallableReflection.php 0000644 00000003056 14717614651 0016526 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\Invoker\Reflection; use Closure; use ElementorProDeps\Invoker\Exception\NotCallableException; use ReflectionException; use ReflectionFunction; use ReflectionFunctionAbstract; use ReflectionMethod; /** * Create a reflection object from a callable or a callable-like. * * @internal */ class CallableReflection { /** * @param callable|array|string $callable Can be a callable or a callable-like. * @throws NotCallableException|ReflectionException */ public static function create($callable) : ReflectionFunctionAbstract { // Closure if ($callable instanceof Closure) { return new ReflectionFunction($callable); } // Array callable if (\is_array($callable)) { [$class, $method] = $callable; if (!\method_exists($class, $method)) { throw NotCallableException::fromInvalidCallable($callable); } return new ReflectionMethod($class, $method); } // Callable object (i.e. implementing __invoke()) if (\is_object($callable) && \method_exists($callable, '__invoke')) { return new ReflectionMethod($callable, '__invoke'); } // Standard function if (\is_string($callable) && \function_exists($callable)) { return new ReflectionFunction($callable); } throw new NotCallableException(\sprintf('%s is not a callable', \is_string($callable) ? $callable : 'Instance of ' . \get_class($callable))); } } php-di/invoker/src/Exception/NotEnoughParametersException.php 0000644 00000000336 14717614651 0020467 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\Invoker\Exception; /** * Not enough parameters could be resolved to invoke the callable. */ class NotEnoughParametersException extends InvocationException { } php-di/invoker/src/Exception/InvocationException.php 0000644 00000000257 14717614651 0016650 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\Invoker\Exception; /** * Impossible to invoke the callable. */ class InvocationException extends \Exception { } php-di/invoker/src/Exception/NotCallableException.php 0000644 00000002227 14717614651 0016716 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\Invoker\Exception; /** * The given callable is not actually callable. */ class NotCallableException extends InvocationException { /** * @param mixed $value */ public static function fromInvalidCallable($value, bool $containerEntry = \false) : self { if (\is_object($value)) { $message = \sprintf('Instance of %s is not a callable', \get_class($value)); } elseif (\is_array($value) && isset($value[0], $value[1])) { $class = \is_object($value[0]) ? \get_class($value[0]) : $value[0]; $extra = \method_exists($class, '__call') || \method_exists($class, '__callStatic') ? ' A __call() or __callStatic() method exists but magic methods are not supported.' : ''; $message = \sprintf('%s::%s() is not a callable.%s', $class, $value[1], $extra); } elseif ($containerEntry) { $message = \var_export($value, \true) . ' is neither a callable nor a valid container entry'; } else { $message = \var_export($value, \true) . ' is not a callable'; } return new self($message); } } php-di/invoker/src/Invoker.php 0000644 00000006434 14717614651 0012342 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\Invoker; use ElementorProDeps\Invoker\Exception\NotCallableException; use ElementorProDeps\Invoker\Exception\NotEnoughParametersException; use ElementorProDeps\Invoker\ParameterResolver\AssociativeArrayResolver; use ElementorProDeps\Invoker\ParameterResolver\DefaultValueResolver; use ElementorProDeps\Invoker\ParameterResolver\NumericArrayResolver; use ElementorProDeps\Invoker\ParameterResolver\ParameterResolver; use ElementorProDeps\Invoker\ParameterResolver\ResolverChain; use ElementorProDeps\Invoker\Reflection\CallableReflection; use ElementorProDeps\Psr\Container\ContainerInterface; use ReflectionParameter; /** * Invoke a callable. */ class Invoker implements InvokerInterface { /** @var CallableResolver|null */ private $callableResolver; /** @var ParameterResolver */ private $parameterResolver; /** @var ContainerInterface|null */ private $container; public function __construct(?ParameterResolver $parameterResolver = null, ?ContainerInterface $container = null) { $this->parameterResolver = $parameterResolver ?: $this->createParameterResolver(); $this->container = $container; if ($container) { $this->callableResolver = new CallableResolver($container); } } /** * {@inheritdoc} */ public function call($callable, array $parameters = []) { if ($this->callableResolver) { $callable = $this->callableResolver->resolve($callable); } if (!\is_callable($callable)) { throw new NotCallableException(\sprintf('%s is not a callable', \is_object($callable) ? 'Instance of ' . \get_class($callable) : \var_export($callable, \true))); } $callableReflection = CallableReflection::create($callable); $args = $this->parameterResolver->getParameters($callableReflection, $parameters, []); // Sort by array key because call_user_func_array ignores numeric keys \ksort($args); // Check all parameters are resolved $diff = \array_diff_key($callableReflection->getParameters(), $args); $parameter = \reset($diff); if ($parameter && \assert($parameter instanceof ReflectionParameter) && !$parameter->isVariadic()) { throw new NotEnoughParametersException(\sprintf('Unable to invoke the callable because no value was given for parameter %d ($%s)', $parameter->getPosition() + 1, $parameter->name)); } return \call_user_func_array($callable, $args); } /** * Create the default parameter resolver. */ private function createParameterResolver() : ParameterResolver { return new ResolverChain([new NumericArrayResolver(), new AssociativeArrayResolver(), new DefaultValueResolver()]); } /** * @return ParameterResolver By default it's a ResolverChain */ public function getParameterResolver() : ParameterResolver { return $this->parameterResolver; } public function getContainer() : ?ContainerInterface { return $this->container; } /** * @return CallableResolver|null Returns null if no container was given in the constructor. */ public function getCallableResolver() : ?CallableResolver { return $this->callableResolver; } } php-di/invoker/src/ParameterResolver/NumericArrayResolver.php 0000644 00000002044 14717614651 0020503 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\Invoker\ParameterResolver; use ReflectionFunctionAbstract; /** * Simply returns all the values of the $providedParameters array that are * indexed by the parameter position (i.e. a number). * * E.g. `->call($callable, ['foo', 'bar'])` will simply resolve the parameters * to `['foo', 'bar']`. * * Parameters that are not indexed by a number (i.e. parameter position) * will be ignored. */ class NumericArrayResolver implements ParameterResolver { public function getParameters(ReflectionFunctionAbstract $reflection, array $providedParameters, array $resolvedParameters) : array { // Skip parameters already resolved if (!empty($resolvedParameters)) { $providedParameters = \array_diff_key($providedParameters, $resolvedParameters); } foreach ($providedParameters as $key => $value) { if (\is_int($key)) { $resolvedParameters[$key] = $value; } } return $resolvedParameters; } } php-di/invoker/src/ParameterResolver/DefaultValueResolver.php 0000644 00000002526 14717614651 0020470 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\Invoker\ParameterResolver; use ReflectionException; use ReflectionFunctionAbstract; /** * Finds the default value for a parameter, *if it exists*. */ class DefaultValueResolver implements ParameterResolver { public function getParameters(ReflectionFunctionAbstract $reflection, array $providedParameters, array $resolvedParameters) : array { $parameters = $reflection->getParameters(); // Skip parameters already resolved if (!empty($resolvedParameters)) { $parameters = \array_diff_key($parameters, $resolvedParameters); } foreach ($parameters as $index => $parameter) { \assert($parameter instanceof \ReflectionParameter); if ($parameter->isDefaultValueAvailable()) { try { $resolvedParameters[$index] = $parameter->getDefaultValue(); } catch (ReflectionException $e) { // Can't get default values from PHP internal classes and functions } } else { $parameterType = $parameter->getType(); if ($parameterType && $parameterType->allowsNull()) { $resolvedParameters[$index] = null; } } } return $resolvedParameters; } } php-di/invoker/src/ParameterResolver/Container/TypeHintContainerResolver.php 0000644 00000003643 14717614651 0023441 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\Invoker\ParameterResolver\Container; use ElementorProDeps\Invoker\ParameterResolver\ParameterResolver; use ElementorProDeps\Psr\Container\ContainerInterface; use ReflectionFunctionAbstract; use ReflectionNamedType; /** * Inject entries from a DI container using the type-hints. */ class TypeHintContainerResolver implements ParameterResolver { /** @var ContainerInterface */ private $container; /** * @param ContainerInterface $container The container to get entries from. */ public function __construct(ContainerInterface $container) { $this->container = $container; } public function getParameters(ReflectionFunctionAbstract $reflection, array $providedParameters, array $resolvedParameters) : array { $parameters = $reflection->getParameters(); // Skip parameters already resolved if (!empty($resolvedParameters)) { $parameters = \array_diff_key($parameters, $resolvedParameters); } foreach ($parameters as $index => $parameter) { $parameterType = $parameter->getType(); if (!$parameterType) { // No type continue; } if (!$parameterType instanceof ReflectionNamedType) { // Union types are not supported continue; } if ($parameterType->isBuiltin()) { // Primitive types are not supported continue; } $parameterClass = $parameterType->getName(); if ($parameterClass === 'self') { $parameterClass = $parameter->getDeclaringClass()->getName(); } if ($this->container->has($parameterClass)) { $resolvedParameters[$index] = $this->container->get($parameterClass); } } return $resolvedParameters; } } php-di/invoker/src/ParameterResolver/Container/ParameterNameContainerResolver.php 0000644 00000002453 14717614651 0024414 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\Invoker\ParameterResolver\Container; use ElementorProDeps\Invoker\ParameterResolver\ParameterResolver; use ElementorProDeps\Psr\Container\ContainerInterface; use ReflectionFunctionAbstract; /** * Inject entries from a DI container using the parameter names. */ class ParameterNameContainerResolver implements ParameterResolver { /** @var ContainerInterface */ private $container; /** * @param ContainerInterface $container The container to get entries from. */ public function __construct(ContainerInterface $container) { $this->container = $container; } public function getParameters(ReflectionFunctionAbstract $reflection, array $providedParameters, array $resolvedParameters) : array { $parameters = $reflection->getParameters(); // Skip parameters already resolved if (!empty($resolvedParameters)) { $parameters = \array_diff_key($parameters, $resolvedParameters); } foreach ($parameters as $index => $parameter) { $name = $parameter->name; if ($name && $this->container->has($name)) { $resolvedParameters[$index] = $this->container->get($name); } } return $resolvedParameters; } } php-di/invoker/src/ParameterResolver/ParameterResolver.php 0000644 00000001677 14717614651 0020035 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\Invoker\ParameterResolver; use ReflectionFunctionAbstract; /** * Resolves the parameters to use to call the callable. */ interface ParameterResolver { /** * Resolves the parameters to use to call the callable. * * `$resolvedParameters` contains parameters that have already been resolved. * * Each ParameterResolver must resolve parameters that are not already * in `$resolvedParameters`. That allows to chain multiple ParameterResolver. * * @param ReflectionFunctionAbstract $reflection Reflection object for the callable. * @param array $providedParameters Parameters provided by the caller. * @param array $resolvedParameters Parameters resolved (indexed by parameter position). * @return array */ public function getParameters(ReflectionFunctionAbstract $reflection, array $providedParameters, array $resolvedParameters); } php-di/invoker/src/ParameterResolver/ResolverChain.php 0000644 00000003016 14717614651 0017124 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\Invoker\ParameterResolver; use ReflectionFunctionAbstract; /** * Dispatches the call to other resolvers until all parameters are resolved. * * Chain of responsibility pattern. */ class ResolverChain implements ParameterResolver { /** @var ParameterResolver[] */ private $resolvers; public function __construct(array $resolvers = []) { $this->resolvers = $resolvers; } public function getParameters(ReflectionFunctionAbstract $reflection, array $providedParameters, array $resolvedParameters) : array { $reflectionParameters = $reflection->getParameters(); foreach ($this->resolvers as $resolver) { $resolvedParameters = $resolver->getParameters($reflection, $providedParameters, $resolvedParameters); $diff = \array_diff_key($reflectionParameters, $resolvedParameters); if (empty($diff)) { // Stop traversing: all parameters are resolved return $resolvedParameters; } } return $resolvedParameters; } /** * Push a parameter resolver after the ones already registered. */ public function appendResolver(ParameterResolver $resolver) : void { $this->resolvers[] = $resolver; } /** * Insert a parameter resolver before the ones already registered. */ public function prependResolver(ParameterResolver $resolver) : void { \array_unshift($this->resolvers, $resolver); } } php-di/invoker/src/ParameterResolver/TypeHintResolver.php 0000644 00000003100 14717614651 0017640 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\Invoker\ParameterResolver; use ReflectionFunctionAbstract; use ReflectionNamedType; /** * Inject entries using type-hints. * * Tries to match type-hints with the parameters provided. */ class TypeHintResolver implements ParameterResolver { public function getParameters(ReflectionFunctionAbstract $reflection, array $providedParameters, array $resolvedParameters) : array { $parameters = $reflection->getParameters(); // Skip parameters already resolved if (!empty($resolvedParameters)) { $parameters = \array_diff_key($parameters, $resolvedParameters); } foreach ($parameters as $index => $parameter) { $parameterType = $parameter->getType(); if (!$parameterType) { // No type continue; } if (!$parameterType instanceof ReflectionNamedType) { // Union types are not supported continue; } if ($parameterType->isBuiltin()) { // Primitive types are not supported continue; } $parameterClass = $parameterType->getName(); if ($parameterClass === 'self') { $parameterClass = $parameter->getDeclaringClass()->getName(); } if (\array_key_exists($parameterClass, $providedParameters)) { $resolvedParameters[$index] = $providedParameters[$parameterClass]; } } return $resolvedParameters; } } php-di/invoker/src/ParameterResolver/AssociativeArrayResolver.php 0000644 00000002112 14717614651 0021347 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\Invoker\ParameterResolver; use ReflectionFunctionAbstract; /** * Tries to map an associative array (string-indexed) to the parameter names. * * E.g. `->call($callable, ['foo' => 'bar'])` will inject the string `'bar'` * in the parameter named `$foo`. * * Parameters that are not indexed by a string are ignored. */ class AssociativeArrayResolver implements ParameterResolver { public function getParameters(ReflectionFunctionAbstract $reflection, array $providedParameters, array $resolvedParameters) : array { $parameters = $reflection->getParameters(); // Skip parameters already resolved if (!empty($resolvedParameters)) { $parameters = \array_diff_key($parameters, $resolvedParameters); } foreach ($parameters as $index => $parameter) { if (\array_key_exists($parameter->name, $providedParameters)) { $resolvedParameters[$index] = $providedParameters[$parameter->name]; } } return $resolvedParameters; } } php-di/invoker/src/CallableResolver.php 0000644 00000007162 14717614651 0014145 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\Invoker; use Closure; use ElementorProDeps\Invoker\Exception\NotCallableException; use ElementorProDeps\Psr\Container\ContainerInterface; use ElementorProDeps\Psr\Container\NotFoundExceptionInterface; use ReflectionException; use ReflectionMethod; /** * Resolves a callable from a container. */ class CallableResolver { /** @var ContainerInterface */ private $container; public function __construct(ContainerInterface $container) { $this->container = $container; } /** * Resolve the given callable into a real PHP callable. * * @param callable|string|array $callable * @return callable Real PHP callable. * @throws NotCallableException|ReflectionException */ public function resolve($callable) : callable { if (\is_string($callable) && \strpos($callable, '::') !== \false) { $callable = \explode('::', $callable, 2); } $callable = $this->resolveFromContainer($callable); if (!\is_callable($callable)) { throw NotCallableException::fromInvalidCallable($callable, \true); } return $callable; } /** * @param callable|string|array $callable * @return callable|mixed * @throws NotCallableException|ReflectionException */ private function resolveFromContainer($callable) { // Shortcut for a very common use case if ($callable instanceof Closure) { return $callable; } // If it's already a callable there is nothing to do if (\is_callable($callable)) { // TODO with PHP 8 that should not be necessary to check this anymore if (!$this->isStaticCallToNonStaticMethod($callable)) { return $callable; } } // The callable is a container entry name if (\is_string($callable)) { try { return $this->container->get($callable); } catch (NotFoundExceptionInterface $e) { if ($this->container->has($callable)) { throw $e; } throw NotCallableException::fromInvalidCallable($callable, \true); } } // The callable is an array whose first item is a container entry name // e.g. ['some-container-entry', 'methodToCall'] if (\is_array($callable) && \is_string($callable[0])) { try { // Replace the container entry name by the actual object $callable[0] = $this->container->get($callable[0]); return $callable; } catch (NotFoundExceptionInterface $e) { if ($this->container->has($callable[0])) { throw $e; } throw new NotCallableException(\sprintf('Cannot call %s() on %s because it is not a class nor a valid container entry', $callable[1], $callable[0])); } } // Unrecognized stuff, we let it fail later return $callable; } /** * Check if the callable represents a static call to a non-static method. * * @param mixed $callable * @throws ReflectionException */ private function isStaticCallToNonStaticMethod($callable) : bool { if (\is_array($callable) && \is_string($callable[0])) { [$class, $method] = $callable; if (!\method_exists($class, $method)) { return \false; } $reflection = new ReflectionMethod($class, $method); return !$reflection->isStatic(); } return \false; } } php-di/invoker/src/InvokerInterface.php 0000644 00000001432 14717614651 0014154 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\Invoker; use ElementorProDeps\Invoker\Exception\InvocationException; use ElementorProDeps\Invoker\Exception\NotCallableException; use ElementorProDeps\Invoker\Exception\NotEnoughParametersException; /** * Invoke a callable. */ interface InvokerInterface { /** * Call the given function using the given parameters. * * @param callable|array|string $callable Function to call. * @param array $parameters Parameters to use. * @return mixed Result of the function. * @throws InvocationException Base exception class for all the sub-exceptions below. * @throws NotCallableException * @throws NotEnoughParametersException */ public function call($callable, array $parameters = []); } php-di/invoker/composer.json 0000644 00000001444 14717614651 0012143 0 ustar 00 { "name": "php-di\/invoker", "description": "Generic and extensible callable invoker", "keywords": [ "invoker", "dependency-injection", "dependency", "injection", "callable", "invoke" ], "homepage": "https:\/\/github.com\/PHP-DI\/Invoker", "license": "MIT", "type": "library", "autoload": { "psr-4": { "ElementorProDeps\\Invoker\\": "src\/" } }, "autoload-dev": { "psr-4": { "ElementorProDeps\\Invoker\\Test\\": "tests\/" } }, "require": { "php": ">=7.3", "psr\/container": "^1.0|^2.0" }, "require-dev": { "phpunit\/phpunit": "^9.0", "athletic\/athletic": "~0.1.8", "mnapoli\/hard-mode": "~0.3.0" } } php-di/invoker/LICENSE 0000644 00000002065 14717614651 0010426 0 ustar 00 The MIT License (MIT) Copyright (c) Matthieu Napoli Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. php-di/php-di/src/NotFoundException.php 0000644 00000000450 14717614651 0014034 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI; use ElementorProDeps\Psr\Container\NotFoundExceptionInterface; /** * Exception thrown when a class or a value is not found in the container. */ class NotFoundException extends \Exception implements NotFoundExceptionInterface { } php-di/php-di/src/Annotation/Inject.php 0000644 00000003705 14717614651 0013755 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Annotation; use ElementorProDeps\DI\Definition\Exception\InvalidAnnotation; /** * "Inject" annotation. * * Marks a property or method as an injection point * * @api * * @Annotation * @Target({"METHOD","PROPERTY"}) * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ final class Inject { /** * Entry name. * @var string */ private $name; /** * Parameters, indexed by the parameter number (index) or name. * * Used if the annotation is set on a method * @var array */ private $parameters = []; /** * @throws InvalidAnnotation */ public function __construct(array $values) { // Process the parameters as a list AND as a parameter array (we don't know on what the annotation is) // @Inject(name="foo") if (isset($values['name']) && \is_string($values['name'])) { $this->name = $values['name']; return; } // @Inject if (!isset($values['value'])) { return; } $values = $values['value']; // @Inject("foo") if (\is_string($values)) { $this->name = $values; } // @Inject({...}) on a method if (\is_array($values)) { foreach ($values as $key => $value) { if (!\is_string($value)) { throw new InvalidAnnotation(\sprintf('@Inject({"param" = "value"}) expects "value" to be a string, %s given.', \json_encode($value))); } $this->parameters[$key] = $value; } } } /** * @return string|null Name of the entry to inject */ public function getName() { return $this->name; } /** * @return array Parameters, indexed by the parameter number (index) or name */ public function getParameters() : array { return $this->parameters; } } php-di/php-di/src/Annotation/Injectable.php 0000644 00000001261 14717614651 0014574 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Annotation; /** * "Injectable" annotation. * * Marks a class as injectable * * @api * * @Annotation * @Target("CLASS") * * @author Domenic Muskulus <domenic@muskulus.eu> * @author Matthieu Napoli <matthieu@mnapoli.fr> */ final class Injectable { /** * Should the object be lazy-loaded. * @var bool|null */ private $lazy; public function __construct(array $values) { if (isset($values['lazy'])) { $this->lazy = (bool) $values['lazy']; } } /** * @return bool|null */ public function isLazy() { return $this->lazy; } } php-di/php-di/src/CompiledContainer.php 0000644 00000010606 14717614651 0014024 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI; use ElementorProDeps\DI\Compiler\RequestedEntryHolder; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\Definition\Exception\InvalidDefinition; use ElementorProDeps\DI\Invoker\FactoryParameterResolver; use ElementorProDeps\Invoker\Exception\NotCallableException; use ElementorProDeps\Invoker\Exception\NotEnoughParametersException; use ElementorProDeps\Invoker\Invoker; use ElementorProDeps\Invoker\InvokerInterface; use ElementorProDeps\Invoker\ParameterResolver\AssociativeArrayResolver; use ElementorProDeps\Invoker\ParameterResolver\DefaultValueResolver; use ElementorProDeps\Invoker\ParameterResolver\NumericArrayResolver; use ElementorProDeps\Invoker\ParameterResolver\ResolverChain; /** * Compiled version of the dependency injection container. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ abstract class CompiledContainer extends Container { /** * This const is overridden in child classes (compiled containers). * @var array */ protected const METHOD_MAPPING = []; /** * @var InvokerInterface */ private $factoryInvoker; /** * {@inheritdoc} */ public function get($name) { // Try to find the entry in the singleton map if (isset($this->resolvedEntries[$name]) || \array_key_exists($name, $this->resolvedEntries)) { return $this->resolvedEntries[$name]; } $method = static::METHOD_MAPPING[$name] ?? null; // If it's a compiled entry, then there is a method in this class if ($method !== null) { // Check if we are already getting this entry -> circular dependency if (isset($this->entriesBeingResolved[$name])) { throw new DependencyException("Circular dependency detected while trying to resolve entry '{$name}'"); } $this->entriesBeingResolved[$name] = \true; try { $value = $this->{$method}(); } finally { unset($this->entriesBeingResolved[$name]); } // Store the entry to always return it without recomputing it $this->resolvedEntries[$name] = $value; return $value; } return parent::get($name); } /** * {@inheritdoc} */ public function has($name) { if (!\is_string($name)) { throw new \InvalidArgumentException(\sprintf('The name parameter must be of type string, %s given', \is_object($name) ? \get_class($name) : \gettype($name))); } // The parent method is overridden to check in our array, it avoids resolving definitions if (isset(static::METHOD_MAPPING[$name])) { return \true; } return parent::has($name); } protected function setDefinition(string $name, Definition $definition) { // It needs to be forbidden because that would mean get() must go through the definitions // every time, which kinds of defeats the performance gains of the compiled container throw new \LogicException('You cannot set a definition at runtime on a compiled container. You can either put your definitions in a file, disable compilation or ->set() a raw value directly (PHP object, string, int, ...) instead of a PHP-DI definition.'); } /** * Invoke the given callable. */ protected function resolveFactory($callable, $entryName, array $extraParameters = []) { // Initialize the factory resolver if (!$this->factoryInvoker) { $parameterResolver = new ResolverChain([new AssociativeArrayResolver(), new FactoryParameterResolver($this->delegateContainer), new NumericArrayResolver(), new DefaultValueResolver()]); $this->factoryInvoker = new Invoker($parameterResolver, $this->delegateContainer); } $parameters = [$this->delegateContainer, new RequestedEntryHolder($entryName)]; $parameters = \array_merge($parameters, $extraParameters); try { return $this->factoryInvoker->call($callable, $parameters); } catch (NotCallableException $e) { throw new InvalidDefinition("Entry \"{$entryName}\" cannot be resolved: factory " . $e->getMessage()); } catch (NotEnoughParametersException $e) { throw new InvalidDefinition("Entry \"{$entryName}\" cannot be resolved: " . $e->getMessage()); } } } php-di/php-di/src/functions.php 0000644 00000011231 14717614651 0012430 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI; use ElementorProDeps\DI\Definition\ArrayDefinitionExtension; use ElementorProDeps\DI\Definition\EnvironmentVariableDefinition; use ElementorProDeps\DI\Definition\Helper\AutowireDefinitionHelper; use ElementorProDeps\DI\Definition\Helper\CreateDefinitionHelper; use ElementorProDeps\DI\Definition\Helper\FactoryDefinitionHelper; use ElementorProDeps\DI\Definition\Reference; use ElementorProDeps\DI\Definition\StringDefinition; use ElementorProDeps\DI\Definition\ValueDefinition; if (!\function_exists('ElementorProDeps\\DI\\value')) { /** * Helper for defining a value. * * @param mixed $value */ function value($value) : ValueDefinition { return new ValueDefinition($value); } } if (!\function_exists('ElementorProDeps\\DI\\create')) { /** * Helper for defining an object. * * @param string|null $className Class name of the object. * If null, the name of the entry (in the container) will be used as class name. */ function create(string $className = null) : CreateDefinitionHelper { return new CreateDefinitionHelper($className); } } if (!\function_exists('ElementorProDeps\\DI\\autowire')) { /** * Helper for autowiring an object. * * @param string|null $className Class name of the object. * If null, the name of the entry (in the container) will be used as class name. */ function autowire(string $className = null) : AutowireDefinitionHelper { return new AutowireDefinitionHelper($className); } } if (!\function_exists('ElementorProDeps\\DI\\factory')) { /** * Helper for defining a container entry using a factory function/callable. * * @param callable $factory The factory is a callable that takes the container as parameter * and returns the value to register in the container. */ function factory($factory) : FactoryDefinitionHelper { return new FactoryDefinitionHelper($factory); } } if (!\function_exists('ElementorProDeps\\DI\\decorate')) { /** * Decorate the previous definition using a callable. * * Example: * * 'foo' => decorate(function ($foo, $container) { * return new CachedFoo($foo, $container->get('cache')); * }) * * @param callable $callable The callable takes the decorated object as first parameter and * the container as second. */ function decorate($callable) : FactoryDefinitionHelper { return new FactoryDefinitionHelper($callable, \true); } } if (!\function_exists('ElementorProDeps\\DI\\get')) { /** * Helper for referencing another container entry in an object definition. */ function get(string $entryName) : Reference { return new Reference($entryName); } } if (!\function_exists('ElementorProDeps\\DI\\env')) { /** * Helper for referencing environment variables. * * @param string $variableName The name of the environment variable. * @param mixed $defaultValue The default value to be used if the environment variable is not defined. */ function env(string $variableName, $defaultValue = null) : EnvironmentVariableDefinition { // Only mark as optional if the default value was *explicitly* provided. $isOptional = 2 === \func_num_args(); return new EnvironmentVariableDefinition($variableName, $isOptional, $defaultValue); } } if (!\function_exists('ElementorProDeps\\DI\\add')) { /** * Helper for extending another definition. * * Example: * * 'log.backends' => DI\add(DI\get('My\Custom\LogBackend')) * * or: * * 'log.backends' => DI\add([ * DI\get('My\Custom\LogBackend') * ]) * * @param mixed|array $values A value or an array of values to add to the array. * * @since 5.0 */ function add($values) : ArrayDefinitionExtension { if (!\is_array($values)) { $values = [$values]; } return new ArrayDefinitionExtension($values); } } if (!\function_exists('ElementorProDeps\\DI\\string')) { /** * Helper for concatenating strings. * * Example: * * 'log.filename' => DI\string('{app.path}/app.log') * * @param string $expression A string expression. Use the `{}` placeholders to reference other container entries. * * @since 5.0 */ function string(string $expression) : StringDefinition { return new StringDefinition($expression); } } php-di/php-di/src/FactoryInterface.php 0000644 00000001773 14717614651 0013662 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI; /** * Describes the basic interface of a factory. * * @api * * @since 4.0 * @author Matthieu Napoli <matthieu@mnapoli.fr> */ interface FactoryInterface { /** * Resolves an entry by its name. If given a class name, it will return a new instance of that class. * * @param string $name Entry name or a class name. * @param array $parameters Optional parameters to use to build the entry. Use this to force specific * parameters to specific values. Parameters not defined in this array will * be automatically resolved. * * @throws \InvalidArgumentException The name parameter must be of type string. * @throws DependencyException Error while resolving the entry. * @throws NotFoundException No entry or class found for the given name. * @return mixed */ public function make($name, array $parameters = []); } php-di/php-di/src/ContainerBuilder.php 0000644 00000026060 14717614651 0013657 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI; use ElementorProDeps\DI\Compiler\Compiler; use ElementorProDeps\DI\Definition\Source\AnnotationBasedAutowiring; use ElementorProDeps\DI\Definition\Source\DefinitionArray; use ElementorProDeps\DI\Definition\Source\DefinitionFile; use ElementorProDeps\DI\Definition\Source\DefinitionSource; use ElementorProDeps\DI\Definition\Source\NoAutowiring; use ElementorProDeps\DI\Definition\Source\ReflectionBasedAutowiring; use ElementorProDeps\DI\Definition\Source\SourceCache; use ElementorProDeps\DI\Definition\Source\SourceChain; use ElementorProDeps\DI\Proxy\ProxyFactory; use InvalidArgumentException; use ElementorProDeps\Psr\Container\ContainerInterface; /** * Helper to create and configure a Container. * * With the default options, the container created is appropriate for the development environment. * * Example: * * $builder = new ContainerBuilder(); * $container = $builder->build(); * * @api * * @since 3.2 * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class ContainerBuilder { /** * Name of the container class, used to create the container. * @var string */ private $containerClass; /** * Name of the container parent class, used on compiled container. * @var string */ private $containerParentClass; /** * @var bool */ private $useAutowiring = \true; /** * @var bool */ private $useAnnotations = \false; /** * @var bool */ private $ignorePhpDocErrors = \false; /** * If true, write the proxies to disk to improve performances. * @var bool */ private $writeProxiesToFile = \false; /** * Directory where to write the proxies (if $writeProxiesToFile is enabled). * @var string|null */ private $proxyDirectory; /** * If PHP-DI is wrapped in another container, this references the wrapper. * @var ContainerInterface */ private $wrapperContainer; /** * @var DefinitionSource[]|string[]|array[] */ private $definitionSources = []; /** * Whether the container has already been built. * @var bool */ private $locked = \false; /** * @var string|null */ private $compileToDirectory; /** * @var bool */ private $sourceCache = \false; /** * @var string */ protected $sourceCacheNamespace; /** * Build a container configured for the dev environment. */ public static function buildDevContainer() : Container { return new Container(); } /** * @param string $containerClass Name of the container class, used to create the container. */ public function __construct(string $containerClass = Container::class) { $this->containerClass = $containerClass; } /** * Build and return a container. * * @return Container */ public function build() { $sources = \array_reverse($this->definitionSources); if ($this->useAnnotations) { $autowiring = new AnnotationBasedAutowiring($this->ignorePhpDocErrors); $sources[] = $autowiring; } elseif ($this->useAutowiring) { $autowiring = new ReflectionBasedAutowiring(); $sources[] = $autowiring; } else { $autowiring = new NoAutowiring(); } $sources = \array_map(function ($definitions) use($autowiring) { if (\is_string($definitions)) { // File return new DefinitionFile($definitions, $autowiring); } elseif (\is_array($definitions)) { return new DefinitionArray($definitions, $autowiring); } return $definitions; }, $sources); $source = new SourceChain($sources); // Mutable definition source $source->setMutableDefinitionSource(new DefinitionArray([], $autowiring)); if ($this->sourceCache) { if (!SourceCache::isSupported()) { throw new \Exception('APCu is not enabled, PHP-DI cannot use it as a cache'); } // Wrap the source with the cache decorator $source = new SourceCache($source, $this->sourceCacheNamespace); } $proxyFactory = new ProxyFactory($this->writeProxiesToFile, $this->proxyDirectory); $this->locked = \true; $containerClass = $this->containerClass; if ($this->compileToDirectory) { $compiler = new Compiler($proxyFactory); $compiledContainerFile = $compiler->compile($source, $this->compileToDirectory, $containerClass, $this->containerParentClass, $this->useAutowiring || $this->useAnnotations); // Only load the file if it hasn't been already loaded // (the container can be created multiple times in the same process) if (!\class_exists($containerClass, \false)) { require $compiledContainerFile; } } return new $containerClass($source, $proxyFactory, $this->wrapperContainer); } /** * Compile the container for optimum performances. * * Be aware that the container is compiled once and never updated! * * Therefore: * * - in production you should clear that directory every time you deploy * - in development you should not compile the container * * @see https://php-di.org/doc/performances.html * * @param string $directory Directory in which to put the compiled container. * @param string $containerClass Name of the compiled class. Customize only if necessary. * @param string $containerParentClass Name of the compiled container parent class. Customize only if necessary. */ public function enableCompilation(string $directory, string $containerClass = 'CompiledContainer', string $containerParentClass = CompiledContainer::class) : self { $this->ensureNotLocked(); $this->compileToDirectory = $directory; $this->containerClass = $containerClass; $this->containerParentClass = $containerParentClass; return $this; } /** * Enable or disable the use of autowiring to guess injections. * * Enabled by default. * * @return $this */ public function useAutowiring(bool $bool) : self { $this->ensureNotLocked(); $this->useAutowiring = $bool; return $this; } /** * Enable or disable the use of annotations to guess injections. * * Disabled by default. * * @return $this */ public function useAnnotations(bool $bool) : self { $this->ensureNotLocked(); $this->useAnnotations = $bool; return $this; } /** * Enable or disable ignoring phpdoc errors (non-existent classes in `@param` or `@var`). * * @return $this */ public function ignorePhpDocErrors(bool $bool) : self { $this->ensureNotLocked(); $this->ignorePhpDocErrors = $bool; return $this; } /** * Configure the proxy generation. * * For dev environment, use `writeProxiesToFile(false)` (default configuration) * For production environment, use `writeProxiesToFile(true, 'tmp/proxies')` * * @see https://php-di.org/doc/lazy-injection.html * * @param bool $writeToFile If true, write the proxies to disk to improve performances * @param string|null $proxyDirectory Directory where to write the proxies * @throws InvalidArgumentException when writeToFile is set to true and the proxy directory is null * @return $this */ public function writeProxiesToFile(bool $writeToFile, string $proxyDirectory = null) : self { $this->ensureNotLocked(); $this->writeProxiesToFile = $writeToFile; if ($writeToFile && $proxyDirectory === null) { throw new InvalidArgumentException('The proxy directory must be specified if you want to write proxies on disk'); } $this->proxyDirectory = $proxyDirectory; return $this; } /** * If PHP-DI's container is wrapped by another container, we can * set this so that PHP-DI will use the wrapper rather than itself for building objects. * * @return $this */ public function wrapContainer(ContainerInterface $otherContainer) : self { $this->ensureNotLocked(); $this->wrapperContainer = $otherContainer; return $this; } /** * Add definitions to the container. * * @param string|array|DefinitionSource ...$definitions Can be an array of definitions, the * name of a file containing definitions * or a DefinitionSource object. * @return $this */ public function addDefinitions(...$definitions) : self { $this->ensureNotLocked(); foreach ($definitions as $definition) { if (!\is_string($definition) && !\is_array($definition) && !$definition instanceof DefinitionSource) { throw new InvalidArgumentException(\sprintf('%s parameter must be a string, an array or a DefinitionSource object, %s given', 'ContainerBuilder::addDefinitions()', \is_object($definition) ? \get_class($definition) : \gettype($definition))); } $this->definitionSources[] = $definition; } return $this; } /** * Enables the use of APCu to cache definitions. * * You must have APCu enabled to use it. * * Before using this feature, you should try these steps first: * - enable compilation if not already done (see `enableCompilation()`) * - if you use autowiring or annotations, add all the classes you are using into your configuration so that * PHP-DI knows about them and compiles them * Once this is done, you can try to optimize performances further with APCu. It can also be useful if you use * `Container::make()` instead of `get()` (`make()` calls cannot be compiled so they are not optimized). * * Remember to clear APCu on each deploy else your application will have a stale cache. Do not enable the cache * in development environment: any change you will make to the code will be ignored because of the cache. * * @see https://php-di.org/doc/performances.html * * @param string $cacheNamespace use unique namespace per container when sharing a single APC memory pool to prevent cache collisions * @return $this */ public function enableDefinitionCache(string $cacheNamespace = '') : self { $this->ensureNotLocked(); $this->sourceCache = \true; $this->sourceCacheNamespace = $cacheNamespace; return $this; } /** * Are we building a compiled container? */ public function isCompilationEnabled() : bool { return (bool) $this->compileToDirectory; } private function ensureNotLocked() { if ($this->locked) { throw new \LogicException('The ContainerBuilder cannot be modified after the container has been built'); } } } php-di/php-di/src/Container.php 0000644 00000033122 14717614651 0012345 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\Definition\Exception\InvalidDefinition; use ElementorProDeps\DI\Definition\FactoryDefinition; use ElementorProDeps\DI\Definition\Helper\DefinitionHelper; use ElementorProDeps\DI\Definition\InstanceDefinition; use ElementorProDeps\DI\Definition\ObjectDefinition; use ElementorProDeps\DI\Definition\Resolver\DefinitionResolver; use ElementorProDeps\DI\Definition\Resolver\ResolverDispatcher; use ElementorProDeps\DI\Definition\Source\DefinitionArray; use ElementorProDeps\DI\Definition\Source\MutableDefinitionSource; use ElementorProDeps\DI\Definition\Source\ReflectionBasedAutowiring; use ElementorProDeps\DI\Definition\Source\SourceChain; use ElementorProDeps\DI\Definition\ValueDefinition; use ElementorProDeps\DI\Invoker\DefinitionParameterResolver; use ElementorProDeps\DI\Proxy\ProxyFactory; use InvalidArgumentException; use ElementorProDeps\Invoker\Invoker; use ElementorProDeps\Invoker\InvokerInterface; use ElementorProDeps\Invoker\ParameterResolver\AssociativeArrayResolver; use ElementorProDeps\Invoker\ParameterResolver\Container\TypeHintContainerResolver; use ElementorProDeps\Invoker\ParameterResolver\DefaultValueResolver; use ElementorProDeps\Invoker\ParameterResolver\NumericArrayResolver; use ElementorProDeps\Invoker\ParameterResolver\ResolverChain; use ElementorProDeps\Psr\Container\ContainerInterface; /** * Dependency Injection Container. * * @api * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class Container implements ContainerInterface, FactoryInterface, InvokerInterface { /** * Map of entries that are already resolved. * @var array */ protected $resolvedEntries = []; /** * @var MutableDefinitionSource */ private $definitionSource; /** * @var DefinitionResolver */ private $definitionResolver; /** * Map of definitions that are already fetched (local cache). * * @var (Definition|null)[] */ private $fetchedDefinitions = []; /** * Array of entries being resolved. Used to avoid circular dependencies and infinite loops. * @var array */ protected $entriesBeingResolved = []; /** * @var InvokerInterface|null */ private $invoker; /** * Container that wraps this container. If none, points to $this. * * @var ContainerInterface */ protected $delegateContainer; /** * @var ProxyFactory */ protected $proxyFactory; /** * Use `$container = new Container()` if you want a container with the default configuration. * * If you want to customize the container's behavior, you are discouraged to create and pass the * dependencies yourself, the ContainerBuilder class is here to help you instead. * * @see ContainerBuilder * * @param ContainerInterface $wrapperContainer If the container is wrapped by another container. */ public function __construct(MutableDefinitionSource $definitionSource = null, ProxyFactory $proxyFactory = null, ContainerInterface $wrapperContainer = null) { $this->delegateContainer = $wrapperContainer ?: $this; $this->definitionSource = $definitionSource ?: $this->createDefaultDefinitionSource(); $this->proxyFactory = $proxyFactory ?: new ProxyFactory(\false); $this->definitionResolver = new ResolverDispatcher($this->delegateContainer, $this->proxyFactory); // Auto-register the container $this->resolvedEntries = [self::class => $this, ContainerInterface::class => $this->delegateContainer, FactoryInterface::class => $this, InvokerInterface::class => $this]; } /** * Returns an entry of the container by its name. * * @template T * @param string|class-string<T> $name Entry name or a class name. * * @throws DependencyException Error while resolving the entry. * @throws NotFoundException No entry found for the given name. * @return mixed|T */ public function get($name) { // If the entry is already resolved we return it if (isset($this->resolvedEntries[$name]) || \array_key_exists($name, $this->resolvedEntries)) { return $this->resolvedEntries[$name]; } $definition = $this->getDefinition($name); if (!$definition) { throw new NotFoundException("No entry or class found for '{$name}'"); } $value = $this->resolveDefinition($definition); $this->resolvedEntries[$name] = $value; return $value; } /** * @param string $name * * @return Definition|null */ private function getDefinition($name) { // Local cache that avoids fetching the same definition twice if (!\array_key_exists($name, $this->fetchedDefinitions)) { $this->fetchedDefinitions[$name] = $this->definitionSource->getDefinition($name); } return $this->fetchedDefinitions[$name]; } /** * Build an entry of the container by its name. * * This method behave like get() except resolves the entry again every time. * For example if the entry is a class then a new instance will be created each time. * * This method makes the container behave like a factory. * * @template T * @param string|class-string<T> $name Entry name or a class name. * @param array $parameters Optional parameters to use to build the entry. Use this to force * specific parameters to specific values. Parameters not defined in this * array will be resolved using the container. * * @throws InvalidArgumentException The name parameter must be of type string. * @throws DependencyException Error while resolving the entry. * @throws NotFoundException No entry found for the given name. * @return mixed|T */ public function make($name, array $parameters = []) { if (!\is_string($name)) { throw new InvalidArgumentException(\sprintf('The name parameter must be of type string, %s given', \is_object($name) ? \get_class($name) : \gettype($name))); } $definition = $this->getDefinition($name); if (!$definition) { // If the entry is already resolved we return it if (\array_key_exists($name, $this->resolvedEntries)) { return $this->resolvedEntries[$name]; } throw new NotFoundException("No entry or class found for '{$name}'"); } return $this->resolveDefinition($definition, $parameters); } /** * Test if the container can provide something for the given name. * * @param string $name Entry name or a class name. * * @throws InvalidArgumentException The name parameter must be of type string. * @return bool */ public function has($name) { if (!\is_string($name)) { throw new InvalidArgumentException(\sprintf('The name parameter must be of type string, %s given', \is_object($name) ? \get_class($name) : \gettype($name))); } if (\array_key_exists($name, $this->resolvedEntries)) { return \true; } $definition = $this->getDefinition($name); if ($definition === null) { return \false; } return $this->definitionResolver->isResolvable($definition); } /** * Inject all dependencies on an existing instance. * * @template T * @param object|T $instance Object to perform injection upon * @throws InvalidArgumentException * @throws DependencyException Error while injecting dependencies * @return object|T $instance Returns the same instance */ public function injectOn($instance) { if (!$instance) { return $instance; } $className = \get_class($instance); // If the class is anonymous, don't cache its definition // Checking for anonymous classes is cleaner via Reflection, but also slower $objectDefinition = \false !== \strpos($className, '@anonymous') ? $this->definitionSource->getDefinition($className) : $this->getDefinition($className); if (!$objectDefinition instanceof ObjectDefinition) { return $instance; } $definition = new InstanceDefinition($instance, $objectDefinition); $this->definitionResolver->resolve($definition); return $instance; } /** * Call the given function using the given parameters. * * Missing parameters will be resolved from the container. * * @param callable $callable Function to call. * @param array $parameters Parameters to use. Can be indexed by the parameter names * or not indexed (same order as the parameters). * The array can also contain DI definitions, e.g. DI\get(). * * @return mixed Result of the function. */ public function call($callable, array $parameters = []) { return $this->getInvoker()->call($callable, $parameters); } /** * Define an object or a value in the container. * * @param string $name Entry name * @param mixed|DefinitionHelper $value Value, use definition helpers to define objects */ public function set(string $name, $value) { if ($value instanceof DefinitionHelper) { $value = $value->getDefinition($name); } elseif ($value instanceof \Closure) { $value = new FactoryDefinition($name, $value); } if ($value instanceof ValueDefinition) { $this->resolvedEntries[$name] = $value->getValue(); } elseif ($value instanceof Definition) { $value->setName($name); $this->setDefinition($name, $value); } else { $this->resolvedEntries[$name] = $value; } } /** * Get defined container entries. * * @return string[] */ public function getKnownEntryNames() : array { $entries = \array_unique(\array_merge(\array_keys($this->definitionSource->getDefinitions()), \array_keys($this->resolvedEntries))); \sort($entries); return $entries; } /** * Get entry debug information. * * @param string $name Entry name * * @throws InvalidDefinition * @throws NotFoundException */ public function debugEntry(string $name) : string { $definition = $this->definitionSource->getDefinition($name); if ($definition instanceof Definition) { return (string) $definition; } if (\array_key_exists($name, $this->resolvedEntries)) { return $this->getEntryType($this->resolvedEntries[$name]); } throw new NotFoundException("No entry or class found for '{$name}'"); } /** * Get formatted entry type. * * @param mixed $entry */ private function getEntryType($entry) : string { if (\is_object($entry)) { return \sprintf("Object (\n class = %s\n)", \get_class($entry)); } if (\is_array($entry)) { return \preg_replace(['/^array \\(/', '/\\)$/'], ['[', ']'], \var_export($entry, \true)); } if (\is_string($entry)) { return \sprintf('Value (\'%s\')', $entry); } if (\is_bool($entry)) { return \sprintf('Value (%s)', $entry === \true ? 'true' : 'false'); } return \sprintf('Value (%s)', \is_scalar($entry) ? $entry : \ucfirst(\gettype($entry))); } /** * Resolves a definition. * * Checks for circular dependencies while resolving the definition. * * @throws DependencyException Error while resolving the entry. * @return mixed */ private function resolveDefinition(Definition $definition, array $parameters = []) { $entryName = $definition->getName(); // Check if we are already getting this entry -> circular dependency if (isset($this->entriesBeingResolved[$entryName])) { throw new DependencyException("Circular dependency detected while trying to resolve entry '{$entryName}'"); } $this->entriesBeingResolved[$entryName] = \true; // Resolve the definition try { $value = $this->definitionResolver->resolve($definition, $parameters); } finally { unset($this->entriesBeingResolved[$entryName]); } return $value; } protected function setDefinition(string $name, Definition $definition) { // Clear existing entry if it exists if (\array_key_exists($name, $this->resolvedEntries)) { unset($this->resolvedEntries[$name]); } $this->fetchedDefinitions = []; // Completely clear this local cache $this->definitionSource->addDefinition($definition); } private function getInvoker() : InvokerInterface { if (!$this->invoker) { $parameterResolver = new ResolverChain([new DefinitionParameterResolver($this->definitionResolver), new NumericArrayResolver(), new AssociativeArrayResolver(), new DefaultValueResolver(), new TypeHintContainerResolver($this->delegateContainer)]); $this->invoker = new Invoker($parameterResolver, $this); } return $this->invoker; } private function createDefaultDefinitionSource() : SourceChain { $source = new SourceChain([new ReflectionBasedAutowiring()]); $source->setMutableDefinitionSource(new DefinitionArray([], new ReflectionBasedAutowiring())); return $source; } } php-di/php-di/src/Compiler/ObjectCreationCompiler.php 0000644 00000015216 14717614651 0016567 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Compiler; use ElementorProDeps\DI\Definition\Exception\InvalidDefinition; use ElementorProDeps\DI\Definition\ObjectDefinition; use ElementorProDeps\DI\Definition\ObjectDefinition\MethodInjection; use ReflectionClass; use ReflectionMethod; use ReflectionParameter; use ReflectionProperty; /** * Compiles an object definition into native PHP code that, when executed, creates the object. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class ObjectCreationCompiler { /** * @var Compiler */ private $compiler; public function __construct(Compiler $compiler) { $this->compiler = $compiler; } public function compile(ObjectDefinition $definition) : string { $this->assertClassIsNotAnonymous($definition); $this->assertClassIsInstantiable($definition); // Lazy? if ($definition->isLazy()) { return $this->compileLazyDefinition($definition); } try { $classReflection = new ReflectionClass($definition->getClassName()); $constructorArguments = $this->resolveParameters($definition->getConstructorInjection(), $classReflection->getConstructor()); $dumpedConstructorArguments = \array_map(function ($value) { return $this->compiler->compileValue($value); }, $constructorArguments); $code = []; $code[] = \sprintf('$object = new %s(%s);', $definition->getClassName(), \implode(', ', $dumpedConstructorArguments)); // Property injections foreach ($definition->getPropertyInjections() as $propertyInjection) { $value = $propertyInjection->getValue(); $value = $this->compiler->compileValue($value); $className = $propertyInjection->getClassName() ?: $definition->getClassName(); $property = new ReflectionProperty($className, $propertyInjection->getPropertyName()); if ($property->isPublic()) { $code[] = \sprintf('$object->%s = %s;', $propertyInjection->getPropertyName(), $value); } else { // Private/protected property $code[] = \sprintf('\\DI\\Definition\\Resolver\\ObjectCreator::setPrivatePropertyValue(%s, $object, \'%s\', %s);', \var_export($propertyInjection->getClassName(), \true), $propertyInjection->getPropertyName(), $value); } } // Method injections foreach ($definition->getMethodInjections() as $methodInjection) { $methodReflection = new \ReflectionMethod($definition->getClassName(), $methodInjection->getMethodName()); $parameters = $this->resolveParameters($methodInjection, $methodReflection); $dumpedParameters = \array_map(function ($value) { return $this->compiler->compileValue($value); }, $parameters); $code[] = \sprintf('$object->%s(%s);', $methodInjection->getMethodName(), \implode(', ', $dumpedParameters)); } } catch (InvalidDefinition $e) { throw InvalidDefinition::create($definition, \sprintf('Entry "%s" cannot be compiled: %s', $definition->getName(), $e->getMessage())); } return \implode("\n ", $code); } public function resolveParameters(MethodInjection $definition = null, ReflectionMethod $method = null) : array { $args = []; if (!$method) { return $args; } $definitionParameters = $definition ? $definition->getParameters() : []; foreach ($method->getParameters() as $index => $parameter) { if (\array_key_exists($index, $definitionParameters)) { // Look in the definition $value =& $definitionParameters[$index]; } elseif ($parameter->isOptional()) { // If the parameter is optional and wasn't specified, we take its default value $args[] = $this->getParameterDefaultValue($parameter, $method); continue; } else { throw new InvalidDefinition(\sprintf('Parameter $%s of %s has no value defined or guessable', $parameter->getName(), $this->getFunctionName($method))); } $args[] =& $value; } return $args; } private function compileLazyDefinition(ObjectDefinition $definition) : string { $subDefinition = clone $definition; $subDefinition->setLazy(\false); $subDefinition = $this->compiler->compileValue($subDefinition); $this->compiler->getProxyFactory()->generateProxyClass($definition->getClassName()); return <<<PHP \$object = \$this->proxyFactory->createProxy( '{$definition->getClassName()}', function (&\$wrappedObject, \$proxy, \$method, \$params, &\$initializer) { \$wrappedObject = {$subDefinition}; \$initializer = null; // turning off further lazy initialization return true; } ); PHP; } /** * Returns the default value of a function parameter. * * @throws InvalidDefinition Can't get default values from PHP internal classes and functions * @return mixed */ private function getParameterDefaultValue(ReflectionParameter $parameter, ReflectionMethod $function) { try { return $parameter->getDefaultValue(); } catch (\ReflectionException $e) { 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))); } } private function getFunctionName(ReflectionMethod $method) : string { return $method->getName() . '()'; } private function assertClassIsNotAnonymous(ObjectDefinition $definition) { if (\strpos($definition->getClassName(), '@') !== \false) { throw InvalidDefinition::create($definition, \sprintf('Entry "%s" cannot be compiled: anonymous classes cannot be compiled', $definition->getName())); } } private function assertClassIsInstantiable(ObjectDefinition $definition) { if ($definition->isInstantiable()) { return; } $message = !$definition->classExists() ? 'Entry "%s" cannot be compiled: the class doesn\'t exist' : 'Entry "%s" cannot be compiled: the class is not instantiable'; throw InvalidDefinition::create($definition, \sprintf($message, $definition->getName())); } } php-di/php-di/src/Compiler/Template.php 0000644 00000000614 14717614651 0013750 0 ustar 00 /** * This class has been auto-generated by PHP-DI. */ class <?=$this->containerClass; ?> extends <?=$this->containerParentClass; ?> { const METHOD_MAPPING = <?php var_export($this->entryToMethodMapping); ?>; <?php foreach ($this->methods as $methodName => $methodContent) : ?> protected function <?=$methodName; ?>() { <?=$methodContent; ?> } <?php endforeach; ?> } php-di/php-di/src/Compiler/RequestedEntryHolder.php 0000644 00000000710 14717614651 0016313 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Compiler; use ElementorProDeps\DI\Factory\RequestedEntry; /** * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class RequestedEntryHolder implements RequestedEntry { /** * @var string */ private $name; public function __construct(string $name) { $this->name = $name; } public function getName() : string { return $this->name; } } php-di/php-di/src/Compiler/Compiler.php 0000644 00000035262 14717614651 0013756 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Compiler; use function chmod; use ElementorProDeps\DI\Definition\ArrayDefinition; use ElementorProDeps\DI\Definition\DecoratorDefinition; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\Definition\EnvironmentVariableDefinition; use ElementorProDeps\DI\Definition\Exception\InvalidDefinition; use ElementorProDeps\DI\Definition\FactoryDefinition; use ElementorProDeps\DI\Definition\ObjectDefinition; use ElementorProDeps\DI\Definition\Reference; use ElementorProDeps\DI\Definition\Source\DefinitionSource; use ElementorProDeps\DI\Definition\StringDefinition; use ElementorProDeps\DI\Definition\ValueDefinition; use ElementorProDeps\DI\DependencyException; use ElementorProDeps\DI\Proxy\ProxyFactory; use function dirname; use function file_put_contents; use InvalidArgumentException; use ElementorProDeps\Laravel\SerializableClosure\Support\ReflectionClosure; use function rename; use function sprintf; use function tempnam; use function unlink; /** * Compiles the container into PHP code much more optimized for performances. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class Compiler { /** * @var string */ private $containerClass; /** * @var string */ private $containerParentClass; /** * Definitions indexed by the entry name. The value can be null if the definition needs to be fetched. * * Keys are strings, values are `Definition` objects or null. * * @var \ArrayIterator */ private $entriesToCompile; /** * Progressive counter for definitions. * * Each key in $entriesToCompile is defined as 'SubEntry' + counter * and each definition has always the same key in the CompiledContainer * if PHP-DI configuration does not change. * * @var int */ private $subEntryCounter; /** * Progressive counter for CompiledContainer get methods. * * Each CompiledContainer method name is defined as 'get' + counter * and remains the same after each recompilation * if PHP-DI configuration does not change. * * @var int */ private $methodMappingCounter; /** * Map of entry names to method names. * * @var string[] */ private $entryToMethodMapping = []; /** * @var string[] */ private $methods = []; /** * @var bool */ private $autowiringEnabled; /** * @var ProxyFactory */ private $proxyFactory; public function __construct(ProxyFactory $proxyFactory) { $this->proxyFactory = $proxyFactory; } public function getProxyFactory() : ProxyFactory { return $this->proxyFactory; } /** * Compile the container. * * @return string The compiled container file name. */ public function compile(DefinitionSource $definitionSource, string $directory, string $className, string $parentClassName, bool $autowiringEnabled) : string { $fileName = \rtrim($directory, '/') . '/' . $className . '.php'; if (\file_exists($fileName)) { // The container is already compiled return $fileName; } $this->autowiringEnabled = $autowiringEnabled; // Validate that a valid class name was provided $validClassName = \preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $className); if (!$validClassName) { throw new InvalidArgumentException("The container cannot be compiled: `{$className}` is not a valid PHP class name"); } $this->entriesToCompile = new \ArrayIterator($definitionSource->getDefinitions()); // We use an ArrayIterator so that we can keep adding new items to the list while we compile entries foreach ($this->entriesToCompile as $entryName => $definition) { $silenceErrors = \false; // This is an entry found by reference during autowiring if (!$definition) { $definition = $definitionSource->getDefinition($entryName); // We silence errors for those entries because type-hints may reference interfaces/abstract classes // which could later be defined, or even not used (we don't want to block the compilation for those) $silenceErrors = \true; } if (!$definition) { // We do not throw a `NotFound` exception here because the dependency // could be defined at runtime continue; } // Check that the definition can be compiled $errorMessage = $this->isCompilable($definition); if ($errorMessage !== \true) { continue; } try { $this->compileDefinition($entryName, $definition); } catch (InvalidDefinition $e) { if ($silenceErrors) { // forget the entry unset($this->entryToMethodMapping[$entryName]); } else { throw $e; } } } $this->containerClass = $className; $this->containerParentClass = $parentClassName; \ob_start(); require __DIR__ . '/Template.php'; $fileContent = \ob_get_clean(); $fileContent = "<?php\n" . $fileContent; $this->createCompilationDirectory(dirname($fileName)); $this->writeFileAtomic($fileName, $fileContent); return $fileName; } private function writeFileAtomic(string $fileName, string $content) : int { $tmpFile = @tempnam(dirname($fileName), 'swap-compile'); if ($tmpFile === \false) { throw new InvalidArgumentException(sprintf('Error while creating temporary file in %s', dirname($fileName))); } @chmod($tmpFile, 0666); $written = file_put_contents($tmpFile, $content); if ($written === \false) { @unlink($tmpFile); throw new InvalidArgumentException(sprintf('Error while writing to %s', $tmpFile)); } @chmod($tmpFile, 0666); $renamed = @rename($tmpFile, $fileName); if (!$renamed) { @unlink($tmpFile); throw new InvalidArgumentException(sprintf('Error while renaming %s to %s', $tmpFile, $fileName)); } return $written; } /** * @throws DependencyException * @throws InvalidDefinition * @return string The method name */ private function compileDefinition(string $entryName, Definition $definition) : string { // Generate a unique method name $methodName = 'get' . ++$this->methodMappingCounter; $this->entryToMethodMapping[$entryName] = $methodName; switch (\true) { case $definition instanceof ValueDefinition: $value = $definition->getValue(); $code = 'return ' . $this->compileValue($value) . ';'; break; case $definition instanceof Reference: $targetEntryName = $definition->getTargetEntryName(); $code = 'return $this->delegateContainer->get(' . $this->compileValue($targetEntryName) . ');'; // If this method is not yet compiled we store it for compilation if (!isset($this->entriesToCompile[$targetEntryName])) { $this->entriesToCompile[$targetEntryName] = null; } break; case $definition instanceof StringDefinition: $entryName = $this->compileValue($definition->getName()); $expression = $this->compileValue($definition->getExpression()); $code = 'return \\DI\\Definition\\StringDefinition::resolveExpression(' . $entryName . ', ' . $expression . ', $this->delegateContainer);'; break; case $definition instanceof EnvironmentVariableDefinition: $variableName = $this->compileValue($definition->getVariableName()); $isOptional = $this->compileValue($definition->isOptional()); $defaultValue = $this->compileValue($definition->getDefaultValue()); $code = <<<PHP \$value = \$_ENV[{$variableName}] ?? \$_SERVER[{$variableName}] ?? getenv({$variableName}); if (false !== \$value) return \$value; if (!{$isOptional}) { throw new \\DI\\Definition\\Exception\\InvalidDefinition("The environment variable '{$definition->getVariableName()}' has not been defined"); } return {$defaultValue}; PHP; break; case $definition instanceof ArrayDefinition: try { $code = 'return ' . $this->compileValue($definition->getValues()) . ';'; } catch (\Exception $e) { throw new DependencyException(sprintf('Error while compiling %s. %s', $definition->getName(), $e->getMessage()), 0, $e); } break; case $definition instanceof ObjectDefinition: $compiler = new ObjectCreationCompiler($this); $code = $compiler->compile($definition); $code .= "\n return \$object;"; break; case $definition instanceof DecoratorDefinition: $decoratedDefinition = $definition->getDecoratedDefinition(); if (!$decoratedDefinition instanceof Definition) { if (!$definition->getName()) { throw new InvalidDefinition('Decorators cannot be nested in another definition'); } throw new InvalidDefinition(sprintf('Entry "%s" decorates nothing: no previous definition with the same name was found', $definition->getName())); } $code = sprintf('return call_user_func(%s, %s, $this->delegateContainer);', $this->compileValue($definition->getCallable()), $this->compileValue($decoratedDefinition)); break; case $definition instanceof FactoryDefinition: $value = $definition->getCallable(); // Custom error message to help debugging $isInvokableClass = \is_string($value) && \class_exists($value) && \method_exists($value, '__invoke'); if ($isInvokableClass && !$this->autowiringEnabled) { throw new InvalidDefinition(sprintf('Entry "%s" cannot be compiled. Invokable classes cannot be automatically resolved if autowiring is disabled on the container, you need to enable autowiring or define the entry manually.', $entryName)); } $definitionParameters = ''; if (!empty($definition->getParameters())) { $definitionParameters = ', ' . $this->compileValue($definition->getParameters()); } $code = sprintf('return $this->resolveFactory(%s, %s%s);', $this->compileValue($value), \var_export($entryName, \true), $definitionParameters); break; default: // This case should not happen (so it cannot be tested) throw new \Exception('Cannot compile definition of type ' . \get_class($definition)); } $this->methods[$methodName] = $code; return $methodName; } public function compileValue($value) : string { // Check that the value can be compiled $errorMessage = $this->isCompilable($value); if ($errorMessage !== \true) { throw new InvalidDefinition($errorMessage); } if ($value instanceof Definition) { // Give it an arbitrary unique name $subEntryName = 'subEntry' . ++$this->subEntryCounter; // Compile the sub-definition in another method $methodName = $this->compileDefinition($subEntryName, $value); // The value is now a method call to that method (which returns the value) return "\$this->{$methodName}()"; } if (\is_array($value)) { $value = \array_map(function ($value, $key) { $compiledValue = $this->compileValue($value); $key = \var_export($key, \true); return " {$key} => {$compiledValue},\n"; }, $value, \array_keys($value)); $value = \implode('', $value); return "[\n{$value} ]"; } if ($value instanceof \Closure) { return $this->compileClosure($value); } return \var_export($value, \true); } private function createCompilationDirectory(string $directory) { if (!\is_dir($directory) && !@\mkdir($directory, 0777, \true) && !\is_dir($directory)) { throw new InvalidArgumentException(sprintf('Compilation directory does not exist and cannot be created: %s.', $directory)); } if (!\is_writable($directory)) { throw new InvalidArgumentException(sprintf('Compilation directory is not writable: %s.', $directory)); } } /** * @return string|true If true is returned that means that the value is compilable. */ private function isCompilable($value) { if ($value instanceof ValueDefinition) { return $this->isCompilable($value->getValue()); } if ($value instanceof DecoratorDefinition) { if (empty($value->getName())) { return 'Decorators cannot be nested in another definition'; } } // All other definitions are compilable if ($value instanceof Definition) { return \true; } if ($value instanceof \Closure) { return \true; } if (\is_object($value)) { return 'An object was found but objects cannot be compiled'; } if (\is_resource($value)) { return 'A resource was found but resources cannot be compiled'; } return \true; } /** * @throws \DI\Definition\Exception\InvalidDefinition */ private function compileClosure(\Closure $closure) : string { $reflector = new ReflectionClosure($closure); if ($reflector->getUseVariables()) { throw new InvalidDefinition('Cannot compile closures which import variables using the `use` keyword'); } if ($reflector->isBindingRequired() || $reflector->isScopeRequired()) { throw new InvalidDefinition('Cannot compile closures which use $this or self/static/parent references'); } // Force all closures to be static (add the `static` keyword), i.e. they can't use // $this, which makes sense since their code is copied into another class. $code = ($reflector->isStatic() ? '' : 'static ') . $reflector->getCode(); $code = \trim($code, "\t\n\r;"); return $code; } } php-di/php-di/src/Invoker/DefinitionParameterResolver.php 0000644 00000003777 14717614651 0017530 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Invoker; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\Definition\Helper\DefinitionHelper; use ElementorProDeps\DI\Definition\Resolver\DefinitionResolver; use ElementorProDeps\Invoker\ParameterResolver\ParameterResolver; use ReflectionFunctionAbstract; /** * Resolves callable parameters using definitions. * * @since 5.0 * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class DefinitionParameterResolver implements ParameterResolver { /** * @var DefinitionResolver */ private $definitionResolver; public function __construct(DefinitionResolver $definitionResolver) { $this->definitionResolver = $definitionResolver; } public function getParameters(ReflectionFunctionAbstract $reflection, array $providedParameters, array $resolvedParameters) : array { // Skip parameters already resolved if (!empty($resolvedParameters)) { $providedParameters = \array_diff_key($providedParameters, $resolvedParameters); } foreach ($providedParameters as $key => $value) { if ($value instanceof DefinitionHelper) { $value = $value->getDefinition(''); } if (!$value instanceof Definition) { continue; } $value = $this->definitionResolver->resolve($value); if (\is_int($key)) { // Indexed by position $resolvedParameters[$key] = $value; } else { // Indexed by parameter name // TODO optimize? $reflectionParameters = $reflection->getParameters(); foreach ($reflectionParameters as $reflectionParameter) { if ($key === $reflectionParameter->name) { $resolvedParameters[$reflectionParameter->getPosition()] = $value; } } } } return $resolvedParameters; } } php-di/php-di/src/Invoker/FactoryParameterResolver.php 0000644 00000004440 14717614651 0017033 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Invoker; use ElementorProDeps\Invoker\ParameterResolver\ParameterResolver; use ElementorProDeps\Psr\Container\ContainerInterface; use ReflectionFunctionAbstract; use ReflectionNamedType; /** * Inject the container, the definition or any other service using type-hints. * * {@internal This class is similar to TypeHintingResolver and TypeHintingContainerResolver, * we use this instead for performance reasons} * * @author Quim Calpe <quim@kalpe.com> * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class FactoryParameterResolver implements ParameterResolver { /** * @var ContainerInterface */ private $container; public function __construct(ContainerInterface $container) { $this->container = $container; } public function getParameters(ReflectionFunctionAbstract $reflection, array $providedParameters, array $resolvedParameters) : array { $parameters = $reflection->getParameters(); // Skip parameters already resolved if (!empty($resolvedParameters)) { $parameters = \array_diff_key($parameters, $resolvedParameters); } foreach ($parameters as $index => $parameter) { $parameterType = $parameter->getType(); if (!$parameterType) { // No type continue; } if (!$parameterType instanceof ReflectionNamedType) { // Union types are not supported continue; } if ($parameterType->isBuiltin()) { // Primitive types are not supported continue; } $parameterClass = $parameterType->getName(); if ($parameterClass === 'Psr\\Container\\ContainerInterface') { $resolvedParameters[$index] = $this->container; } elseif ($parameterClass === 'DI\\Factory\\RequestedEntry') { // By convention the second parameter is the definition $resolvedParameters[$index] = $providedParameters[1]; } elseif ($this->container->has($parameterClass)) { $resolvedParameters[$index] = $this->container->get($parameterClass); } } return $resolvedParameters; } } php-di/php-di/src/Proxy/ProxyFactory.php 0000644 00000006337 14717614651 0014225 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Proxy; use ElementorProDeps\ProxyManager\Configuration; use ElementorProDeps\ProxyManager\Factory\LazyLoadingValueHolderFactory; use ElementorProDeps\ProxyManager\FileLocator\FileLocator; use ElementorProDeps\ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy; use ElementorProDeps\ProxyManager\GeneratorStrategy\FileWriterGeneratorStrategy; use ElementorProDeps\ProxyManager\Proxy\LazyLoadingInterface; /** * Creates proxy classes. * * Wraps Ocramius/ProxyManager LazyLoadingValueHolderFactory. * * @see \ProxyManager\Factory\LazyLoadingValueHolderFactory * * @since 5.0 * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class ProxyFactory { /** * If true, write the proxies to disk to improve performances. * @var bool */ private $writeProxiesToFile; /** * Directory where to write the proxies (if $writeProxiesToFile is enabled). * @var string|null */ private $proxyDirectory; /** * @var LazyLoadingValueHolderFactory|null */ private $proxyManager; public function __construct(bool $writeProxiesToFile = \false, string $proxyDirectory = null) { $this->writeProxiesToFile = $writeProxiesToFile; $this->proxyDirectory = $proxyDirectory; } /** * Creates a new lazy proxy instance of the given class with * the given initializer. * * @param string $className name of the class to be proxied * @param \Closure $initializer initializer to be passed to the proxy */ public function createProxy(string $className, \Closure $initializer) : LazyLoadingInterface { $this->createProxyManager(); return $this->proxyManager->createProxy($className, $initializer); } /** * Generates and writes the proxy class to file. * * @param string $className name of the class to be proxied */ public function generateProxyClass(string $className) { // If proxy classes a written to file then we pre-generate the class // If they are not written to file then there is no point to do this if ($this->writeProxiesToFile) { $this->createProxyManager(); $this->createProxy($className, function () { }); } } private function createProxyManager() { if ($this->proxyManager !== null) { return; } if (!\class_exists(Configuration::class)) { throw new \RuntimeException('The ocramius/proxy-manager library is not installed. Lazy injection requires that library to be installed with Composer in order to work. Run "composer require ocramius/proxy-manager:~2.0".'); } $config = new Configuration(); if ($this->writeProxiesToFile) { $config->setProxiesTargetDir($this->proxyDirectory); $config->setGeneratorStrategy(new FileWriterGeneratorStrategy(new FileLocator($this->proxyDirectory))); // @phpstan-ignore-next-line \spl_autoload_register($config->getProxyAutoloader()); } else { $config->setGeneratorStrategy(new EvaluatingGeneratorStrategy()); } $this->proxyManager = new LazyLoadingValueHolderFactory($config); } } php-di/php-di/src/DependencyException.php 0000644 00000000401 14717614651 0014352 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI; use ElementorProDeps\Psr\Container\ContainerExceptionInterface; /** * Exception for the Container. */ class DependencyException extends \Exception implements ContainerExceptionInterface { } php-di/php-di/src/Factory/RequestedEntry.php 0000644 00000000761 14717614651 0015020 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Factory; /** * Represents the container entry that was requested. * * Implementations of this interface can be injected in factory parameters in order * to know what was the name of the requested entry. * * @api * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ interface RequestedEntry { /** * Returns the name of the entry that was requested by the container. */ public function getName() : string; } php-di/php-di/src/Definition/AutowireDefinition.php 0000644 00000000273 14717614651 0016324 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition; /** * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class AutowireDefinition extends ObjectDefinition { } php-di/php-di/src/Definition/Reference.php 0000644 00000002627 14717614651 0014417 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition; use ElementorProDeps\Psr\Container\ContainerInterface; /** * Represents a reference to another entry. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class Reference implements Definition, SelfResolvingDefinition { /** * Entry name. * @var string */ private $name = ''; /** * Name of the target entry. * @var string */ private $targetEntryName; /** * @param string $targetEntryName Name of the target entry */ public function __construct(string $targetEntryName) { $this->targetEntryName = $targetEntryName; } public function getName() : string { return $this->name; } public function setName(string $name) { $this->name = $name; } public function getTargetEntryName() : string { return $this->targetEntryName; } public function resolve(ContainerInterface $container) { return $container->get($this->getTargetEntryName()); } public function isResolvable(ContainerInterface $container) : bool { return $container->has($this->getTargetEntryName()); } public function replaceNestedDefinitions(callable $replacer) { // no nested definitions } public function __toString() { return \sprintf('get(%s)', $this->targetEntryName); } } php-di/php-di/src/Definition/SelfResolvingDefinition.php 0000644 00000001104 14717614651 0017301 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition; use ElementorProDeps\Psr\Container\ContainerInterface; /** * Describes a definition that can resolve itself. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ interface SelfResolvingDefinition { /** * Resolve the definition and return the resulting value. * * @return mixed */ public function resolve(ContainerInterface $container); /** * Check if a definition can be resolved. */ public function isResolvable(ContainerInterface $container) : bool; } php-di/php-di/src/Definition/ExtendsPreviousDefinition.php 0000644 00000000515 14717614651 0017673 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition; /** * A definition that extends a previous definition with the same name. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ interface ExtendsPreviousDefinition extends Definition { public function setExtendedDefinition(Definition $definition); } php-di/php-di/src/Definition/Exception/InvalidDefinition.php 0000644 00000001166 14717614651 0020053 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Exception; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\Psr\Container\ContainerExceptionInterface; /** * Invalid DI definitions. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class InvalidDefinition extends \Exception implements ContainerExceptionInterface { public static function create(Definition $definition, string $message, \Exception $previous = null) : self { return new self(\sprintf('%s' . \PHP_EOL . 'Full definition:' . \PHP_EOL . '%s', $message, (string) $definition), 0, $previous); } } php-di/php-di/src/Definition/Exception/InvalidAnnotation.php 0000644 00000000367 14717614651 0020077 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Exception; /** * Error in the definitions using annotations. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class InvalidAnnotation extends InvalidDefinition { } php-di/php-di/src/Definition/ObjectDefinition/PropertyInjection.php 0000644 00000003021 14717614651 0021414 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\ObjectDefinition; /** * Describe an injection in a class property. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class PropertyInjection { /** * Property name. * @var string */ private $propertyName; /** * Value that should be injected in the property. * @var mixed */ private $value; /** * Use for injecting in properties of parent classes: the class name * must be the name of the parent class because private properties * can be attached to the parent classes, not the one we are resolving. * @var string|null */ private $className; /** * @param string $propertyName Property name * @param mixed $value Value that should be injected in the property */ public function __construct(string $propertyName, $value, string $className = null) { $this->propertyName = $propertyName; $this->value = $value; $this->className = $className; } public function getPropertyName() : string { return $this->propertyName; } /** * @return mixed Value that should be injected in the property */ public function getValue() { return $this->value; } /** * @return string|null */ public function getClassName() { return $this->className; } public function replaceNestedDefinition(callable $replacer) { $this->value = $replacer($this->value); } } php-di/php-di/src/Definition/ObjectDefinition/MethodInjection.php 0000644 00000003425 14717614651 0021020 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\ObjectDefinition; use ElementorProDeps\DI\Definition\Definition; /** * Describe an injection in an object method. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class MethodInjection implements Definition { /** * @var string */ private $methodName; /** * @var mixed[] */ private $parameters = []; public function __construct(string $methodName, array $parameters = []) { $this->methodName = $methodName; $this->parameters = $parameters; } public static function constructor(array $parameters = []) : self { return new self('__construct', $parameters); } public function getMethodName() : string { return $this->methodName; } /** * @return mixed[] */ public function getParameters() : array { return $this->parameters; } /** * Replace the parameters of the definition by a new array of parameters. */ public function replaceParameters(array $parameters) { $this->parameters = $parameters; } public function merge(self $definition) { // In case of conflicts, the current definition prevails. $this->parameters = $this->parameters + $definition->parameters; } public function getName() : string { return ''; } public function setName(string $name) { // The name does not matter for method injections } public function replaceNestedDefinitions(callable $replacer) { $this->parameters = \array_map($replacer, $this->parameters); } /** * {@inheritdoc} */ public function __toString() { return \sprintf('method(%s)', $this->methodName); } } php-di/php-di/src/Definition/Definition.php 0000644 00000001511 14717614651 0014600 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition; use ElementorProDeps\DI\Factory\RequestedEntry; /** * Definition. * * @internal This interface is internal to PHP-DI and may change between minor versions. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ interface Definition extends RequestedEntry { /** * Returns the name of the entry in the container. */ public function getName() : string; /** * Set the name of the entry in the container. */ public function setName(string $name); /** * Apply a callable that replaces the definitions nested in this definition. */ public function replaceNestedDefinitions(callable $replacer); /** * Definitions can be cast to string for debugging information. */ public function __toString(); } php-di/php-di/src/Definition/EnvironmentVariableDefinition.php 0000644 00000005744 14717614651 0020507 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition; /** * Defines a reference to an environment variable, with fallback to a default * value if the environment variable is not defined. * * @author James Harris <james.harris@icecave.com.au> */ class EnvironmentVariableDefinition implements Definition { /** * Entry name. * @var string */ private $name = ''; /** * The name of the environment variable. * @var string */ private $variableName; /** * Whether or not the environment variable definition is optional. * * If true and the environment variable given by $variableName has not been * defined, $defaultValue is used. * * @var bool */ private $isOptional; /** * The default value to use if the environment variable is optional and not provided. * @var mixed */ private $defaultValue; /** * @param string $variableName The name of the environment variable * @param bool $isOptional Whether or not the environment variable definition is optional * @param mixed $defaultValue The default value to use if the environment variable is optional and not provided */ public function __construct(string $variableName, bool $isOptional = \false, $defaultValue = null) { $this->variableName = $variableName; $this->isOptional = $isOptional; $this->defaultValue = $defaultValue; } public function getName() : string { return $this->name; } public function setName(string $name) { $this->name = $name; } /** * @return string The name of the environment variable */ public function getVariableName() : string { return $this->variableName; } /** * @return bool Whether or not the environment variable definition is optional */ public function isOptional() : bool { return $this->isOptional; } /** * @return mixed The default value to use if the environment variable is optional and not provided */ public function getDefaultValue() { return $this->defaultValue; } public function replaceNestedDefinitions(callable $replacer) { $this->defaultValue = $replacer($this->defaultValue); } public function __toString() { $str = ' variable = ' . $this->variableName . \PHP_EOL . ' optional = ' . ($this->isOptional ? 'yes' : 'no'); if ($this->isOptional) { if ($this->defaultValue instanceof Definition) { $nestedDefinition = (string) $this->defaultValue; $defaultValueStr = \str_replace(\PHP_EOL, \PHP_EOL . ' ', $nestedDefinition); } else { $defaultValueStr = \var_export($this->defaultValue, \true); } $str .= \PHP_EOL . ' default = ' . $defaultValueStr; } return \sprintf('Environment variable (' . \PHP_EOL . '%s' . \PHP_EOL . ')', $str); } } php-di/php-di/src/Definition/ArrayDefinition.php 0000644 00000002614 14717614651 0015604 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition; /** * Definition of an array containing values or references. * * @since 5.0 * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class ArrayDefinition implements Definition { /** * Entry name. * @var string */ private $name = ''; /** * @var array */ private $values; public function __construct(array $values) { $this->values = $values; } public function getName() : string { return $this->name; } public function setName(string $name) { $this->name = $name; } public function getValues() : array { return $this->values; } public function replaceNestedDefinitions(callable $replacer) { $this->values = \array_map($replacer, $this->values); } public function __toString() { $str = '[' . \PHP_EOL; foreach ($this->values as $key => $value) { if (\is_string($key)) { $key = "'" . $key . "'"; } $str .= ' ' . $key . ' => '; if ($value instanceof Definition) { $str .= \str_replace(\PHP_EOL, \PHP_EOL . ' ', (string) $value); } else { $str .= \var_export($value, \true); } $str .= ',' . \PHP_EOL; } return $str . ']'; } } php-di/php-di/src/Definition/DecoratorDefinition.php 0000644 00000001476 14717614651 0016455 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition; /** * Factory that decorates a sub-definition. * * @since 5.0 * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class DecoratorDefinition extends FactoryDefinition implements Definition, ExtendsPreviousDefinition { /** * @var Definition|null */ private $decorated; public function setExtendedDefinition(Definition $definition) { $this->decorated = $definition; } /** * @return Definition|null */ public function getDecoratedDefinition() { return $this->decorated; } public function replaceNestedDefinitions(callable $replacer) { // no nested definitions } public function __toString() { return 'Decorate(' . $this->getName() . ')'; } } php-di/php-di/src/Definition/FactoryDefinition.php 0000644 00000003223 14717614651 0016132 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition; /** * Definition of a value or class with a factory. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class FactoryDefinition implements Definition { /** * Entry name. * @var string */ private $name; /** * Callable that returns the value. * @var callable */ private $factory; /** * Factory parameters. * @var mixed[] */ private $parameters = []; /** * @param string $name Entry name * @param callable $factory Callable that returns the value associated to the entry name. * @param array $parameters Parameters to be passed to the callable */ public function __construct(string $name, $factory, array $parameters = []) { $this->name = $name; $this->factory = $factory; $this->parameters = $parameters; } public function getName() : string { return $this->name; } public function setName(string $name) { $this->name = $name; } /** * @return callable Callable that returns the value associated to the entry name. */ public function getCallable() { return $this->factory; } /** * @return array Array containing the parameters to be passed to the callable, indexed by name. */ public function getParameters() : array { return $this->parameters; } public function replaceNestedDefinitions(callable $replacer) { $this->parameters = \array_map($replacer, $this->parameters); } public function __toString() { return 'Factory'; } } php-di/php-di/src/Definition/ValueDefinition.php 0000644 00000002402 14717614651 0015575 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition; use ElementorProDeps\Psr\Container\ContainerInterface; /** * Definition of a value for dependency injection. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class ValueDefinition implements Definition, SelfResolvingDefinition { /** * Entry name. * @var string */ private $name = ''; /** * @var mixed */ private $value; /** * @param mixed $value */ public function __construct($value) { $this->value = $value; } public function getName() : string { return $this->name; } public function setName(string $name) { $this->name = $name; } /** * @return mixed */ public function getValue() { return $this->value; } public function resolve(ContainerInterface $container) { return $this->getValue(); } public function isResolvable(ContainerInterface $container) : bool { return \true; } public function replaceNestedDefinitions(callable $replacer) { // no nested definitions } public function __toString() { return \sprintf('Value (%s)', \var_export($this->value, \true)); } } php-di/php-di/src/Definition/Dumper/ObjectDefinitionDumper.php 0000644 00000010424 14717614651 0020343 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Dumper; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\Definition\ObjectDefinition; use ElementorProDeps\DI\Definition\ObjectDefinition\MethodInjection; use ReflectionException; /** * Dumps object definitions to string for debugging purposes. * * @since 4.1 * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class ObjectDefinitionDumper { /** * Returns the definition as string representation. */ public function dump(ObjectDefinition $definition) : string { $className = $definition->getClassName(); $classExist = \class_exists($className) || \interface_exists($className); // Class if (!$classExist) { $warning = '#UNKNOWN# '; } else { $class = new \ReflectionClass($className); $warning = $class->isInstantiable() ? '' : '#NOT INSTANTIABLE# '; } $str = \sprintf(' class = %s%s', $warning, $className); // Lazy $str .= \PHP_EOL . ' lazy = ' . \var_export($definition->isLazy(), \true); if ($classExist) { // Constructor $str .= $this->dumpConstructor($className, $definition); // Properties $str .= $this->dumpProperties($definition); // Methods $str .= $this->dumpMethods($className, $definition); } return \sprintf('Object (' . \PHP_EOL . '%s' . \PHP_EOL . ')', $str); } private function dumpConstructor(string $className, ObjectDefinition $definition) : string { $str = ''; $constructorInjection = $definition->getConstructorInjection(); if ($constructorInjection !== null) { $parameters = $this->dumpMethodParameters($className, $constructorInjection); $str .= \sprintf(\PHP_EOL . ' __construct(' . \PHP_EOL . ' %s' . \PHP_EOL . ' )', $parameters); } return $str; } private function dumpProperties(ObjectDefinition $definition) : string { $str = ''; foreach ($definition->getPropertyInjections() as $propertyInjection) { $value = $propertyInjection->getValue(); $valueStr = $value instanceof Definition ? (string) $value : \var_export($value, \true); $str .= \sprintf(\PHP_EOL . ' $%s = %s', $propertyInjection->getPropertyName(), $valueStr); } return $str; } private function dumpMethods(string $className, ObjectDefinition $definition) : string { $str = ''; foreach ($definition->getMethodInjections() as $methodInjection) { $parameters = $this->dumpMethodParameters($className, $methodInjection); $str .= \sprintf(\PHP_EOL . ' %s(' . \PHP_EOL . ' %s' . \PHP_EOL . ' )', $methodInjection->getMethodName(), $parameters); } return $str; } private function dumpMethodParameters(string $className, MethodInjection $methodInjection) : string { $methodReflection = new \ReflectionMethod($className, $methodInjection->getMethodName()); $args = []; $definitionParameters = $methodInjection->getParameters(); foreach ($methodReflection->getParameters() as $index => $parameter) { if (\array_key_exists($index, $definitionParameters)) { $value = $definitionParameters[$index]; $valueStr = $value instanceof Definition ? (string) $value : \var_export($value, \true); $args[] = \sprintf('$%s = %s', $parameter->getName(), $valueStr); continue; } // If the parameter is optional and wasn't specified, we take its default value if ($parameter->isOptional()) { try { $value = $parameter->getDefaultValue(); $args[] = \sprintf('$%s = (default value) %s', $parameter->getName(), \var_export($value, \true)); continue; } catch (ReflectionException $e) { // The default value can't be read through Reflection because it is a PHP internal class } } $args[] = \sprintf('$%s = #UNDEFINED#', $parameter->getName()); } return \implode(\PHP_EOL . ' ', $args); } } php-di/php-di/src/Definition/ObjectDefinition.php 0000644 00000015126 14717614651 0015736 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition; use ElementorProDeps\DI\Definition\Dumper\ObjectDefinitionDumper; use ElementorProDeps\DI\Definition\ObjectDefinition\MethodInjection; use ElementorProDeps\DI\Definition\ObjectDefinition\PropertyInjection; use ElementorProDeps\DI\Definition\Source\DefinitionArray; use ReflectionClass; /** * Defines how an object can be instantiated. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class ObjectDefinition implements Definition { /** * Entry name (most of the time, same as $classname). * @var string */ private $name; /** * Class name (if null, then the class name is $name). * @var string|null */ protected $className; /** * Constructor parameter injection. * @var MethodInjection|null */ protected $constructorInjection; /** * Property injections. * @var PropertyInjection[] */ protected $propertyInjections = []; /** * Method calls. * @var MethodInjection[][] */ protected $methodInjections = []; /** * @var bool|null */ protected $lazy; /** * Store if the class exists. Storing it (in cache) avoids recomputing this. * * @var bool */ private $classExists; /** * Store if the class is instantiable. Storing it (in cache) avoids recomputing this. * * @var bool */ private $isInstantiable; /** * @param string $name Entry name */ public function __construct(string $name, string $className = null) { $this->name = $name; $this->setClassName($className); } public function getName() : string { return $this->name; } public function setName(string $name) { $this->name = $name; } public function setClassName(string $className = null) { $this->className = $className; $this->updateCache(); } public function getClassName() : string { if ($this->className !== null) { return $this->className; } return $this->name; } /** * @return MethodInjection|null */ public function getConstructorInjection() { return $this->constructorInjection; } public function setConstructorInjection(MethodInjection $constructorInjection) { $this->constructorInjection = $constructorInjection; } public function completeConstructorInjection(MethodInjection $injection) { if ($this->constructorInjection !== null) { // Merge $this->constructorInjection->merge($injection); } else { // Set $this->constructorInjection = $injection; } } /** * @return PropertyInjection[] Property injections */ public function getPropertyInjections() : array { return $this->propertyInjections; } public function addPropertyInjection(PropertyInjection $propertyInjection) { $className = $propertyInjection->getClassName(); if ($className) { // Index with the class name to avoid collisions between parent and // child private properties with the same name $key = $className . '::' . $propertyInjection->getPropertyName(); } else { $key = $propertyInjection->getPropertyName(); } $this->propertyInjections[$key] = $propertyInjection; } /** * @return MethodInjection[] Method injections */ public function getMethodInjections() : array { // Return array leafs $injections = []; \array_walk_recursive($this->methodInjections, function ($injection) use(&$injections) { $injections[] = $injection; }); return $injections; } public function addMethodInjection(MethodInjection $methodInjection) { $method = $methodInjection->getMethodName(); if (!isset($this->methodInjections[$method])) { $this->methodInjections[$method] = []; } $this->methodInjections[$method][] = $methodInjection; } public function completeFirstMethodInjection(MethodInjection $injection) { $method = $injection->getMethodName(); if (isset($this->methodInjections[$method][0])) { // Merge $this->methodInjections[$method][0]->merge($injection); } else { // Set $this->addMethodInjection($injection); } } public function setLazy(bool $lazy = null) { $this->lazy = $lazy; } public function isLazy() : bool { if ($this->lazy !== null) { return $this->lazy; } // Default value return \false; } public function classExists() : bool { return $this->classExists; } public function isInstantiable() : bool { return $this->isInstantiable; } public function replaceNestedDefinitions(callable $replacer) { \array_walk($this->propertyInjections, function (PropertyInjection $propertyInjection) use($replacer) { $propertyInjection->replaceNestedDefinition($replacer); }); if ($this->constructorInjection) { $this->constructorInjection->replaceNestedDefinitions($replacer); } \array_walk($this->methodInjections, function ($injectionArray) use($replacer) { \array_walk($injectionArray, function (MethodInjection $methodInjection) use($replacer) { $methodInjection->replaceNestedDefinitions($replacer); }); }); } /** * Replaces all the wildcards in the string with the given replacements. * * @param string[] $replacements */ public function replaceWildcards(array $replacements) { $className = $this->getClassName(); foreach ($replacements as $replacement) { $pos = \strpos($className, DefinitionArray::WILDCARD); if ($pos !== \false) { $className = \substr_replace($className, $replacement, $pos, 1); } } $this->setClassName($className); } public function __toString() { return (new ObjectDefinitionDumper())->dump($this); } private function updateCache() { $className = $this->getClassName(); $this->classExists = \class_exists($className) || \interface_exists($className); if (!$this->classExists) { $this->isInstantiable = \false; return; } $class = new ReflectionClass($className); $this->isInstantiable = $class->isInstantiable(); } } php-di/php-di/src/Definition/StringDefinition.php 0000644 00000004317 14717614651 0015776 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition; use ElementorProDeps\DI\DependencyException; use ElementorProDeps\Psr\Container\ContainerInterface; use ElementorProDeps\Psr\Container\NotFoundExceptionInterface; /** * Definition of a string composed of other strings. * * @since 5.0 * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class StringDefinition implements Definition, SelfResolvingDefinition { /** * Entry name. * @var string */ private $name = ''; /** * @var string */ private $expression; public function __construct(string $expression) { $this->expression = $expression; } public function getName() : string { return $this->name; } public function setName(string $name) { $this->name = $name; } public function getExpression() : string { return $this->expression; } public function resolve(ContainerInterface $container) : string { return self::resolveExpression($this->name, $this->expression, $container); } public function isResolvable(ContainerInterface $container) : bool { return \true; } public function replaceNestedDefinitions(callable $replacer) { // no nested definitions } public function __toString() { return $this->expression; } /** * Resolve a string expression. */ public static function resolveExpression(string $entryName, string $expression, ContainerInterface $container) : string { $callback = function (array $matches) use($entryName, $container) { try { return $container->get($matches[1]); } catch (NotFoundExceptionInterface $e) { throw new DependencyException(\sprintf("Error while parsing string expression for entry '%s': %s", $entryName, $e->getMessage()), 0, $e); } }; $result = \preg_replace_callback('#\\{([^\\{\\}]+)\\}#', $callback, $expression); if ($result === null) { throw new \RuntimeException(\sprintf('An unknown error occurred while parsing the string definition: \'%s\'', $expression)); } return $result; } } php-di/php-di/src/Definition/Source/DefinitionArray.php 0000644 00000007144 14717614651 0017047 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Source; use ElementorProDeps\DI\Definition\Definition; /** * Reads DI definitions from a PHP array. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class DefinitionArray implements DefinitionSource, MutableDefinitionSource { const WILDCARD = '*'; /** * Matches anything except "\". */ const WILDCARD_PATTERN = '([^\\\\]+)'; /** * DI definitions in a PHP array. * @var array */ private $definitions = []; /** * Cache of wildcard definitions. * @var array|null */ private $wildcardDefinitions; /** * @var DefinitionNormalizer */ private $normalizer; public function __construct(array $definitions = [], Autowiring $autowiring = null) { if (isset($definitions[0])) { throw new \Exception('The PHP-DI definition is not indexed by an entry name in the definition array'); } $this->definitions = $definitions; $autowiring = $autowiring ?: new NoAutowiring(); $this->normalizer = new DefinitionNormalizer($autowiring); } /** * @param array $definitions DI definitions in a PHP array indexed by the definition name. */ public function addDefinitions(array $definitions) { if (isset($definitions[0])) { throw new \Exception('The PHP-DI definition is not indexed by an entry name in the definition array'); } // The newly added data prevails // "for keys that exist in both arrays, the elements from the left-hand array will be used" $this->definitions = $definitions + $this->definitions; // Clear cache $this->wildcardDefinitions = null; } /** * {@inheritdoc} */ public function addDefinition(Definition $definition) { $this->definitions[$definition->getName()] = $definition; // Clear cache $this->wildcardDefinitions = null; } public function getDefinition(string $name) { // Look for the definition by name if (\array_key_exists($name, $this->definitions)) { $definition = $this->definitions[$name]; $definition = $this->normalizer->normalizeRootDefinition($definition, $name); return $definition; } // Build the cache of wildcard definitions if ($this->wildcardDefinitions === null) { $this->wildcardDefinitions = []; foreach ($this->definitions as $key => $definition) { if (\strpos($key, self::WILDCARD) !== \false) { $this->wildcardDefinitions[$key] = $definition; } } } // Look in wildcards definitions foreach ($this->wildcardDefinitions as $key => $definition) { // Turn the pattern into a regex $key = \preg_quote($key); $key = '#' . \str_replace('\\' . self::WILDCARD, self::WILDCARD_PATTERN, $key) . '#'; if (\preg_match($key, $name, $matches) === 1) { \array_shift($matches); $definition = $this->normalizer->normalizeRootDefinition($definition, $name, $matches); return $definition; } } return null; } public function getDefinitions() : array { // Return all definitions except wildcard definitions $definitions = []; foreach ($this->definitions as $key => $definition) { if (\strpos($key, self::WILDCARD) === \false) { $definitions[$key] = $definition; } } return $definitions; } } php-di/php-di/src/Definition/Source/DefinitionFile.php 0000644 00000002707 14717614651 0016650 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Source; /** * Reads DI definitions from a file returning a PHP array. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class DefinitionFile extends DefinitionArray { /** * @var bool */ private $initialized = \false; /** * File containing definitions, or null if the definitions are given as a PHP array. * @var string|null */ private $file; /** * @param string $file File in which the definitions are returned as an array. */ public function __construct($file, Autowiring $autowiring = null) { // Lazy-loading to improve performances $this->file = $file; parent::__construct([], $autowiring); } public function getDefinition(string $name) { $this->initialize(); return parent::getDefinition($name); } public function getDefinitions() : array { $this->initialize(); return parent::getDefinitions(); } /** * Lazy-loading of the definitions. */ private function initialize() { if ($this->initialized === \true) { return; } $definitions = (require $this->file); if (!\is_array($definitions)) { throw new \Exception("File {$this->file} should return an array of definitions"); } $this->addDefinitions($definitions); $this->initialized = \true; } } php-di/php-di/src/Definition/Source/SourceChain.php 0000644 00000005753 14717614651 0016167 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Source; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\Definition\ExtendsPreviousDefinition; /** * Manages a chain of other definition sources. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class SourceChain implements DefinitionSource, MutableDefinitionSource { /** * @var DefinitionSource[] */ private $sources; /** * @var DefinitionSource */ private $rootSource; /** * @var MutableDefinitionSource|null */ private $mutableSource; /** * @param DefinitionSource[] $sources */ public function __construct(array $sources) { // We want a numerically indexed array to ease the traversal later $this->sources = \array_values($sources); $this->rootSource = $this; } /** * {@inheritdoc} * * @param int $startIndex Use this parameter to start looking from a specific * point in the source chain. */ public function getDefinition(string $name, int $startIndex = 0) { $count = \count($this->sources); for ($i = $startIndex; $i < $count; ++$i) { $source = $this->sources[$i]; $definition = $source->getDefinition($name); if ($definition) { if ($definition instanceof ExtendsPreviousDefinition) { $this->resolveExtendedDefinition($definition, $i); } return $definition; } } return null; } public function getDefinitions() : array { $names = []; foreach ($this->sources as $source) { $names = \array_merge($names, $source->getDefinitions()); } $names = \array_keys($names); $definitions = \array_combine($names, \array_map(function (string $name) { return $this->getDefinition($name); }, $names)); return $definitions; } public function addDefinition(Definition $definition) { if (!$this->mutableSource) { throw new \LogicException("The container's definition source has not been initialized correctly"); } $this->mutableSource->addDefinition($definition); } private function resolveExtendedDefinition(ExtendsPreviousDefinition $definition, int $currentIndex) { // Look in the next sources only (else infinite recursion, and we can only extend // entries defined in the previous definition files - a previous == next here because // the array was reversed ;) ) $subDefinition = $this->getDefinition($definition->getName(), $currentIndex + 1); if ($subDefinition) { $definition->setExtendedDefinition($subDefinition); } } public function setMutableDefinitionSource(MutableDefinitionSource $mutableSource) { $this->mutableSource = $mutableSource; \array_unshift($this->sources, $mutableSource); } } php-di/php-di/src/Definition/Source/SourceCache.php 0000644 00000004567 14717614651 0016152 0 ustar 00 <?php namespace ElementorProDeps\DI\Definition\Source; use ElementorProDeps\DI\Definition\AutowireDefinition; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\Definition\ObjectDefinition; /** * Decorator that caches another definition source. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class SourceCache implements DefinitionSource, MutableDefinitionSource { /** * @var string */ const CACHE_KEY = 'php-di.definitions.'; /** * @var DefinitionSource */ private $cachedSource; /** * @var string */ private $cacheNamespace; public function __construct(DefinitionSource $cachedSource, string $cacheNamespace = '') { $this->cachedSource = $cachedSource; $this->cacheNamespace = $cacheNamespace; } public function getDefinition(string $name) { $definition = \apcu_fetch($this->getCacheKey($name)); if ($definition === \false) { $definition = $this->cachedSource->getDefinition($name); // Update the cache if ($this->shouldBeCached($definition)) { \apcu_store($this->getCacheKey($name), $definition); } } return $definition; } /** * Used only for the compilation so we can skip the cache safely. */ public function getDefinitions() : array { return $this->cachedSource->getDefinitions(); } public static function isSupported() : bool { return \function_exists('apcu_fetch') && \ini_get('apc.enabled') && !('cli' === \PHP_SAPI && !\ini_get('apc.enable_cli')); } public function getCacheKey(string $name) : string { return self::CACHE_KEY . $this->cacheNamespace . $name; } public function addDefinition(Definition $definition) { throw new \LogicException('You cannot set a definition at runtime on a container that has caching enabled. Doing so would risk caching the definition for the next execution, where it might be different. You can either put your definitions in a file, remove the cache or ->set() a raw value directly (PHP object, string, int, ...) instead of a PHP-DI definition.'); } private function shouldBeCached(Definition $definition = null) : bool { return $definition === null || $definition instanceof ObjectDefinition || $definition instanceof AutowireDefinition; } } php-di/php-di/src/Definition/Source/Autowiring.php 0000644 00000001111 14717614651 0016074 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Source; use ElementorProDeps\DI\Definition\Exception\InvalidDefinition; use ElementorProDeps\DI\Definition\ObjectDefinition; /** * Source of definitions for entries of the container. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ interface Autowiring { /** * Autowire the given definition. * * @throws InvalidDefinition An invalid definition was found. * @return ObjectDefinition|null */ public function autowire(string $name, ObjectDefinition $definition = null); } php-di/php-di/src/Definition/Source/NoAutowiring.php 0000644 00000001077 14717614651 0016404 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Source; use ElementorProDeps\DI\Definition\Exception\InvalidDefinition; use ElementorProDeps\DI\Definition\ObjectDefinition; /** * Implementation used when autowiring is completely disabled. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class NoAutowiring implements Autowiring { public function autowire(string $name, ObjectDefinition $definition = null) { throw new InvalidDefinition(\sprintf('Cannot autowire entry "%s" because autowiring is disabled', $name)); } } php-di/php-di/src/Definition/Source/MutableDefinitionSource.php 0000644 00000000576 14717614651 0020545 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Source; use ElementorProDeps\DI\Definition\Definition; /** * Describes a definition source to which we can add new definitions. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ interface MutableDefinitionSource extends DefinitionSource { public function addDefinition(Definition $definition); } php-di/php-di/src/Definition/Source/DefinitionSource.php 0000644 00000001257 14717614651 0017230 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Source; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\Definition\Exception\InvalidDefinition; /** * Source of definitions for entries of the container. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ interface DefinitionSource { /** * Returns the DI definition for the entry name. * * @throws InvalidDefinition An invalid definition was found. * @return Definition|null */ public function getDefinition(string $name); /** * @return Definition[] Definitions indexed by their name. */ public function getDefinitions() : array; } php-di/php-di/src/Definition/Source/DefinitionNormalizer.php 0000644 00000007557 14717614651 0020123 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Source; use ElementorProDeps\DI\Definition\ArrayDefinition; use ElementorProDeps\DI\Definition\AutowireDefinition; use ElementorProDeps\DI\Definition\DecoratorDefinition; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\Definition\Exception\InvalidDefinition; use ElementorProDeps\DI\Definition\FactoryDefinition; use ElementorProDeps\DI\Definition\Helper\DefinitionHelper; use ElementorProDeps\DI\Definition\ObjectDefinition; use ElementorProDeps\DI\Definition\ValueDefinition; /** * Turns raw definitions/definition helpers into definitions ready * to be resolved or compiled. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class DefinitionNormalizer { /** * @var Autowiring */ private $autowiring; public function __construct(Autowiring $autowiring) { $this->autowiring = $autowiring; } /** * Normalize a definition that is *not* nested in another one. * * This is usually a definition declared at the root of a definition array. * * @param mixed $definition * @param string $name The definition name. * @param string[] $wildcardsReplacements Replacements for wildcard definitions. * * @throws InvalidDefinition */ public function normalizeRootDefinition($definition, string $name, array $wildcardsReplacements = null) : Definition { if ($definition instanceof DefinitionHelper) { $definition = $definition->getDefinition($name); } elseif (\is_array($definition)) { $definition = new ArrayDefinition($definition); } elseif ($definition instanceof \Closure) { $definition = new FactoryDefinition($name, $definition); } elseif (!$definition instanceof Definition) { $definition = new ValueDefinition($definition); } // For a class definition, we replace * in the class name with the matches // *Interface -> *Impl => FooInterface -> FooImpl if ($wildcardsReplacements && $definition instanceof ObjectDefinition) { $definition->replaceWildcards($wildcardsReplacements); } if ($definition instanceof AutowireDefinition) { $definition = $this->autowiring->autowire($name, $definition); } $definition->setName($name); try { $definition->replaceNestedDefinitions([$this, 'normalizeNestedDefinition']); } catch (InvalidDefinition $e) { throw InvalidDefinition::create($definition, \sprintf('Definition "%s" contains an error: %s', $definition->getName(), $e->getMessage()), $e); } return $definition; } /** * Normalize a definition that is nested in another one. * * @param mixed $definition * @return mixed * * @throws InvalidDefinition */ public function normalizeNestedDefinition($definition) { $name = '<nested definition>'; if ($definition instanceof DefinitionHelper) { $definition = $definition->getDefinition($name); } elseif (\is_array($definition)) { $definition = new ArrayDefinition($definition); } elseif ($definition instanceof \Closure) { $definition = new FactoryDefinition($name, $definition); } if ($definition instanceof DecoratorDefinition) { throw new InvalidDefinition('Decorators cannot be nested in another definition'); } if ($definition instanceof AutowireDefinition) { $definition = $this->autowiring->autowire($name, $definition); } if ($definition instanceof Definition) { $definition->setName($name); // Recursively traverse nested definitions $definition->replaceNestedDefinitions([$this, 'normalizeNestedDefinition']); } return $definition; } } php-di/php-di/src/Definition/Source/ReflectionBasedAutowiring.php 0000644 00000004607 14717614651 0021063 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Source; use ElementorProDeps\DI\Definition\ObjectDefinition; use ElementorProDeps\DI\Definition\ObjectDefinition\MethodInjection; use ElementorProDeps\DI\Definition\Reference; use ReflectionNamedType; /** * Reads DI class definitions using reflection. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class ReflectionBasedAutowiring implements DefinitionSource, Autowiring { public function autowire(string $name, ObjectDefinition $definition = null) { $className = $definition ? $definition->getClassName() : $name; if (!\class_exists($className) && !\interface_exists($className)) { return $definition; } $definition = $definition ?: new ObjectDefinition($name); // Constructor $class = new \ReflectionClass($className); $constructor = $class->getConstructor(); if ($constructor && $constructor->isPublic()) { $constructorInjection = MethodInjection::constructor($this->getParametersDefinition($constructor)); $definition->completeConstructorInjection($constructorInjection); } return $definition; } public function getDefinition(string $name) { return $this->autowire($name); } /** * Autowiring cannot guess all existing definitions. */ public function getDefinitions() : array { return []; } /** * Read the type-hinting from the parameters of the function. */ private function getParametersDefinition(\ReflectionFunctionAbstract $constructor) : array { $parameters = []; foreach ($constructor->getParameters() as $index => $parameter) { // Skip optional parameters if ($parameter->isOptional()) { continue; } $parameterType = $parameter->getType(); if (!$parameterType) { // No type continue; } if (!$parameterType instanceof ReflectionNamedType) { // Union types are not supported continue; } if ($parameterType->isBuiltin()) { // Primitive types are not supported continue; } $parameters[$index] = new Reference($parameterType->getName()); } return $parameters; } } php-di/php-di/src/Definition/Source/AnnotationBasedAutowiring.php 0000644 00000022327 14717614651 0021102 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Source; use ElementorProDeps\DI\Annotation\Inject; use ElementorProDeps\DI\Annotation\Injectable; use ElementorProDeps\DI\Definition\Exception\InvalidAnnotation; use ElementorProDeps\DI\Definition\ObjectDefinition; use ElementorProDeps\DI\Definition\ObjectDefinition\MethodInjection; use ElementorProDeps\DI\Definition\ObjectDefinition\PropertyInjection; use ElementorProDeps\DI\Definition\Reference; use ElementorProDeps\Doctrine\Common\Annotations\AnnotationRegistry; use ElementorProDeps\Doctrine\Common\Annotations\Reader; use ElementorProDeps\Doctrine\Common\Annotations\SimpleAnnotationReader; use InvalidArgumentException; use ElementorProDeps\PhpDocReader\PhpDocReader; use ReflectionClass; use ReflectionMethod; use ReflectionNamedType; use ReflectionParameter; use ReflectionProperty; use UnexpectedValueException; /** * Provides DI definitions by reading annotations such as @ Inject and @ var annotations. * * Uses Autowiring, Doctrine's Annotations and regex docblock parsing. * This source automatically includes the reflection source. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class AnnotationBasedAutowiring implements DefinitionSource, Autowiring { /** * @var Reader */ private $annotationReader; /** * @var PhpDocReader */ private $phpDocReader; /** * @var bool */ private $ignorePhpDocErrors; public function __construct($ignorePhpDocErrors = \false) { $this->ignorePhpDocErrors = (bool) $ignorePhpDocErrors; } public function autowire(string $name, ObjectDefinition $definition = null) { $className = $definition ? $definition->getClassName() : $name; if (!\class_exists($className) && !\interface_exists($className)) { return $definition; } $definition = $definition ?: new ObjectDefinition($name); $class = new ReflectionClass($className); $this->readInjectableAnnotation($class, $definition); // Browse the class properties looking for annotated properties $this->readProperties($class, $definition); // Browse the object's methods looking for annotated methods $this->readMethods($class, $definition); return $definition; } /** * {@inheritdoc} * @throws InvalidAnnotation * @throws InvalidArgumentException The class doesn't exist */ public function getDefinition(string $name) { return $this->autowire($name); } /** * Autowiring cannot guess all existing definitions. */ public function getDefinitions() : array { return []; } /** * Browse the class properties looking for annotated properties. */ private function readProperties(ReflectionClass $class, ObjectDefinition $definition) { foreach ($class->getProperties() as $property) { if ($property->isStatic()) { continue; } $this->readProperty($property, $definition); } // Read also the *private* properties of the parent classes /** @noinspection PhpAssignmentInConditionInspection */ while ($class = $class->getParentClass()) { foreach ($class->getProperties(ReflectionProperty::IS_PRIVATE) as $property) { if ($property->isStatic()) { continue; } $this->readProperty($property, $definition, $class->getName()); } } } private function readProperty(ReflectionProperty $property, ObjectDefinition $definition, $classname = null) { // Look for @Inject annotation $annotation = $this->getAnnotationReader()->getPropertyAnnotation($property, 'ElementorProDeps\\DI\\Annotation\\Inject'); if (!$annotation instanceof Inject) { return; } // Try to @Inject("name") or look for @var content $entryName = $annotation->getName() ?: $this->getPhpDocReader()->getPropertyClass($property); // Try using PHP7.4 typed properties if (\PHP_VERSION_ID > 70400 && $entryName === null && $property->getType() instanceof ReflectionNamedType && (\class_exists($property->getType()->getName()) || \interface_exists($property->getType()->getName()))) { $entryName = $property->getType()->getName(); } if ($entryName === null) { throw new InvalidAnnotation(\sprintf('@Inject found on property %s::%s but unable to guess what to inject, use a @var annotation', $property->getDeclaringClass()->getName(), $property->getName())); } $definition->addPropertyInjection(new PropertyInjection($property->getName(), new Reference($entryName), $classname)); } /** * Browse the object's methods looking for annotated methods. */ private function readMethods(ReflectionClass $class, ObjectDefinition $objectDefinition) { // This will look in all the methods, including those of the parent classes foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { if ($method->isStatic()) { continue; } $methodInjection = $this->getMethodInjection($method); if (!$methodInjection) { continue; } if ($method->isConstructor()) { $objectDefinition->completeConstructorInjection($methodInjection); } else { $objectDefinition->completeFirstMethodInjection($methodInjection); } } } /** * @return MethodInjection|null */ private function getMethodInjection(ReflectionMethod $method) { // Look for @Inject annotation try { $annotation = $this->getAnnotationReader()->getMethodAnnotation($method, 'ElementorProDeps\\DI\\Annotation\\Inject'); } catch (InvalidAnnotation $e) { throw new InvalidAnnotation(\sprintf('@Inject annotation on %s::%s is malformed. %s', $method->getDeclaringClass()->getName(), $method->getName(), $e->getMessage()), 0, $e); } // @Inject on constructor is implicit if (!($annotation || $method->isConstructor())) { return null; } $annotationParameters = $annotation instanceof Inject ? $annotation->getParameters() : []; $parameters = []; foreach ($method->getParameters() as $index => $parameter) { $entryName = $this->getMethodParameter($index, $parameter, $annotationParameters); if ($entryName !== null) { $parameters[$index] = new Reference($entryName); } } if ($method->isConstructor()) { return MethodInjection::constructor($parameters); } return new MethodInjection($method->getName(), $parameters); } /** * @param int $parameterIndex * * @return string|null Entry name or null if not found. */ private function getMethodParameter($parameterIndex, ReflectionParameter $parameter, array $annotationParameters) { // @Inject has definition for this parameter (by index, or by name) if (isset($annotationParameters[$parameterIndex])) { return $annotationParameters[$parameterIndex]; } if (isset($annotationParameters[$parameter->getName()])) { return $annotationParameters[$parameter->getName()]; } // Skip optional parameters if not explicitly defined if ($parameter->isOptional()) { return null; } // Try to use the type-hinting $parameterType = $parameter->getType(); if ($parameterType && $parameterType instanceof ReflectionNamedType && !$parameterType->isBuiltin()) { return $parameterType->getName(); } // Last resort, look for @param tag return $this->getPhpDocReader()->getParameterClass($parameter); } /** * @return Reader The annotation reader */ public function getAnnotationReader() { if ($this->annotationReader === null) { AnnotationRegistry::registerLoader('class_exists'); $this->annotationReader = new SimpleAnnotationReader(); $this->annotationReader->addNamespace('ElementorProDeps\\DI\\Annotation'); } return $this->annotationReader; } /** * @return PhpDocReader */ private function getPhpDocReader() { if ($this->phpDocReader === null) { $this->phpDocReader = new PhpDocReader($this->ignorePhpDocErrors); } return $this->phpDocReader; } private function readInjectableAnnotation(ReflectionClass $class, ObjectDefinition $definition) { try { /** @var Injectable|null $annotation */ $annotation = $this->getAnnotationReader()->getClassAnnotation($class, 'ElementorProDeps\\DI\\Annotation\\Injectable'); } catch (UnexpectedValueException $e) { throw new InvalidAnnotation(\sprintf('Error while reading @Injectable on %s: %s', $class->getName(), $e->getMessage()), 0, $e); } if (!$annotation) { return; } if ($annotation->isLazy() !== null) { $definition->setLazy($annotation->isLazy()); } } } php-di/php-di/src/Definition/InstanceDefinition.php 0000644 00000002517 14717614651 0016274 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition; /** * Defines injections on an existing class instance. * * @since 5.0 * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class InstanceDefinition implements Definition { /** * Instance on which to inject dependencies. * * @var object */ private $instance; /** * @var ObjectDefinition */ private $objectDefinition; /** * @param object $instance */ public function __construct($instance, ObjectDefinition $objectDefinition) { $this->instance = $instance; $this->objectDefinition = $objectDefinition; } public function getName() : string { // Name are superfluous for instance definitions return ''; } public function setName(string $name) { // Name are superfluous for instance definitions } /** * @return object */ public function getInstance() { return $this->instance; } public function getObjectDefinition() : ObjectDefinition { return $this->objectDefinition; } public function replaceNestedDefinitions(callable $replacer) { $this->objectDefinition->replaceNestedDefinitions($replacer); } public function __toString() { return 'Instance'; } } php-di/php-di/src/Definition/Helper/DefinitionHelper.php 0000644 00000000612 14717614651 0017160 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Helper; use ElementorProDeps\DI\Definition\Definition; /** * Helps defining container entries. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ interface DefinitionHelper { /** * @param string $entryName Container entry name */ public function getDefinition(string $entryName) : Definition; } php-di/php-di/src/Definition/Helper/FactoryDefinitionHelper.php 0000644 00000003673 14717614651 0020522 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Helper; use ElementorProDeps\DI\Definition\DecoratorDefinition; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\Definition\FactoryDefinition; /** * Helps defining how to create an instance of a class using a factory (callable). * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class FactoryDefinitionHelper implements DefinitionHelper { /** * @var callable */ private $factory; /** * @var bool */ private $decorate; /** * @var array */ private $parameters = []; /** * @param callable $factory * @param bool $decorate Is the factory decorating a previous definition? */ public function __construct($factory, bool $decorate = \false) { $this->factory = $factory; $this->decorate = $decorate; } /** * @param string $entryName Container entry name * @return FactoryDefinition */ public function getDefinition(string $entryName) : Definition { if ($this->decorate) { return new DecoratorDefinition($entryName, $this->factory, $this->parameters); } return new FactoryDefinition($entryName, $this->factory, $this->parameters); } /** * Defines arguments to pass to the factory. * * Because factory methods do not yet support annotations or autowiring, this method * should be used to define all parameters except the ContainerInterface and RequestedEntry. * * Multiple calls can be made to the method to override individual values. * * @param string $parameter Name or index of the parameter for which the value will be given. * @param mixed $value Value to give to this parameter. * * @return $this */ public function parameter(string $parameter, $value) { $this->parameters[$parameter] = $value; return $this; } } php-di/php-di/src/Definition/Helper/CreateDefinitionHelper.php 0000644 00000013104 14717614651 0020304 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Helper; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\Definition\Exception\InvalidDefinition; use ElementorProDeps\DI\Definition\ObjectDefinition; use ElementorProDeps\DI\Definition\ObjectDefinition\MethodInjection; use ElementorProDeps\DI\Definition\ObjectDefinition\PropertyInjection; /** * Helps defining how to create an instance of a class. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class CreateDefinitionHelper implements DefinitionHelper { const DEFINITION_CLASS = ObjectDefinition::class; /** * @var string|null */ private $className; /** * @var bool|null */ private $lazy; /** * Array of constructor parameters. * @var array */ protected $constructor = []; /** * Array of properties and their value. * @var array */ private $properties = []; /** * Array of methods and their parameters. * @var array */ protected $methods = []; /** * Helper for defining an object. * * @param string|null $className Class name of the object. * If null, the name of the entry (in the container) will be used as class name. */ public function __construct(string $className = null) { $this->className = $className; } /** * Define the entry as lazy. * * A lazy entry is created only when it is used, a proxy is injected instead. * * @return $this */ public function lazy() { $this->lazy = \true; return $this; } /** * Defines the arguments to use to call the constructor. * * This method takes a variable number of arguments, example: * ->constructor($param1, $param2, $param3) * * @param mixed... $parameters Parameters to use for calling the constructor of the class. * * @return $this */ public function constructor(...$parameters) { $this->constructor = $parameters; return $this; } /** * Defines a value to inject in a property of the object. * * @param string $property Entry in which to inject the value. * @param mixed $value Value to inject in the property. * * @return $this */ public function property(string $property, $value) { $this->properties[$property] = $value; return $this; } /** * Defines a method to call and the arguments to use. * * This method takes a variable number of arguments after the method name, example: * * ->method('myMethod', $param1, $param2) * * Can be used multiple times to declare multiple calls. * * @param string $method Name of the method to call. * @param mixed... $parameters Parameters to use for calling the method. * * @return $this */ public function method(string $method, ...$parameters) { if (!isset($this->methods[$method])) { $this->methods[$method] = []; } $this->methods[$method][] = $parameters; return $this; } /** * @return ObjectDefinition */ public function getDefinition(string $entryName) : Definition { $class = $this::DEFINITION_CLASS; /** @var ObjectDefinition $definition */ $definition = new $class($entryName, $this->className); if ($this->lazy !== null) { $definition->setLazy($this->lazy); } if (!empty($this->constructor)) { $parameters = $this->fixParameters($definition, '__construct', $this->constructor); $constructorInjection = MethodInjection::constructor($parameters); $definition->setConstructorInjection($constructorInjection); } if (!empty($this->properties)) { foreach ($this->properties as $property => $value) { $definition->addPropertyInjection(new PropertyInjection($property, $value)); } } if (!empty($this->methods)) { foreach ($this->methods as $method => $calls) { foreach ($calls as $parameters) { $parameters = $this->fixParameters($definition, $method, $parameters); $methodInjection = new MethodInjection($method, $parameters); $definition->addMethodInjection($methodInjection); } } } return $definition; } /** * Fixes parameters indexed by the parameter name -> reindex by position. * * This is necessary so that merging definitions between sources is possible. * * @throws InvalidDefinition */ private function fixParameters(ObjectDefinition $definition, string $method, array $parameters) : array { $fixedParameters = []; foreach ($parameters as $index => $parameter) { // Parameter indexed by the parameter name, we reindex it with its position if (\is_string($index)) { $callable = [$definition->getClassName(), $method]; try { $reflectionParameter = new \ReflectionParameter($callable, $index); } catch (\ReflectionException $e) { throw InvalidDefinition::create($definition, \sprintf("Parameter with name '%s' could not be found. %s.", $index, $e->getMessage())); } $index = $reflectionParameter->getPosition(); } $fixedParameters[$index] = $parameter; } return $fixedParameters; } } php-di/php-di/src/Definition/Helper/AutowireDefinitionHelper.php 0000644 00000004564 14717614651 0020712 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Helper; use ElementorProDeps\DI\Definition\AutowireDefinition; /** * Helps defining how to create an instance of a class using autowiring. * * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class AutowireDefinitionHelper extends CreateDefinitionHelper { const DEFINITION_CLASS = AutowireDefinition::class; /** * Defines a value for a specific argument of the constructor. * * This method is usually used together with annotations or autowiring, when a parameter * is not (or cannot be) type-hinted. Using this method instead of constructor() allows to * avoid defining all the parameters (letting them being resolved using annotations or autowiring) * and only define one. * * @param string|int $parameter Parameter name of position for which the value will be given. * @param mixed $value Value to give to this parameter. * * @return $this */ public function constructorParameter($parameter, $value) { $this->constructor[$parameter] = $value; return $this; } /** * Defines a method to call and a value for a specific argument. * * This method is usually used together with annotations or autowiring, when a parameter * is not (or cannot be) type-hinted. Using this method instead of method() allows to * avoid defining all the parameters (letting them being resolved using annotations or * autowiring) and only define one. * * If multiple calls to the method have been configured already (e.g. in a previous definition) * then this method only overrides the parameter for the *first* call. * * @param string $method Name of the method to call. * @param string|int $parameter Parameter name of position for which the value will be given. * @param mixed $value Value to give to this parameter. * * @return $this */ public function methodParameter(string $method, $parameter, $value) { // Special case for the constructor if ($method === '__construct') { $this->constructor[$parameter] = $value; return $this; } if (!isset($this->methods[$method])) { $this->methods[$method] = [0 => []]; } $this->methods[$method][0][$parameter] = $value; return $this; } } php-di/php-di/src/Definition/ArrayDefinitionExtension.php 0000644 00000001765 14717614651 0017507 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition; use ElementorProDeps\DI\Definition\Exception\InvalidDefinition; /** * Extends an array definition by adding new elements into it. * * @since 5.0 * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class ArrayDefinitionExtension extends ArrayDefinition implements ExtendsPreviousDefinition { /** * @var ArrayDefinition */ private $subDefinition; public function getValues() : array { if (!$this->subDefinition) { return parent::getValues(); } return \array_merge($this->subDefinition->getValues(), parent::getValues()); } public function setExtendedDefinition(Definition $definition) { if (!$definition instanceof ArrayDefinition) { throw new InvalidDefinition(\sprintf('Definition %s tries to add array entries but the previous definition is not an array', $this->getName())); } $this->subDefinition = $definition; } } php-di/php-di/src/Definition/Resolver/EnvironmentVariableResolver.php 0000644 00000004014 14717614651 0022006 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Resolver; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\Definition\EnvironmentVariableDefinition; use ElementorProDeps\DI\Definition\Exception\InvalidDefinition; /** * Resolves a environment variable definition to a value. * * @author James Harris <james.harris@icecave.com.au> */ class EnvironmentVariableResolver implements DefinitionResolver { /** * @var DefinitionResolver */ private $definitionResolver; /** * @var callable */ private $variableReader; public function __construct(DefinitionResolver $definitionResolver, $variableReader = null) { $this->definitionResolver = $definitionResolver; $this->variableReader = $variableReader ?? [$this, 'getEnvVariable']; } /** * Resolve an environment variable definition to a value. * * @param EnvironmentVariableDefinition $definition */ public function resolve(Definition $definition, array $parameters = []) { $value = \call_user_func($this->variableReader, $definition->getVariableName()); if (\false !== $value) { return $value; } if (!$definition->isOptional()) { throw new InvalidDefinition(\sprintf("The environment variable '%s' has not been defined", $definition->getVariableName())); } $value = $definition->getDefaultValue(); // Nested definition if ($value instanceof Definition) { return $this->definitionResolver->resolve($value); } return $value; } public function isResolvable(Definition $definition, array $parameters = []) : bool { return \true; } protected function getEnvVariable(string $variableName) { if (isset($_ENV[$variableName])) { return $_ENV[$variableName]; } elseif (isset($_SERVER[$variableName])) { return $_SERVER[$variableName]; } return \getenv($variableName); } } php-di/php-di/src/Definition/Resolver/ResolverDispatcher.php 0000644 00000011117 14717614651 0020124 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Resolver; use ElementorProDeps\DI\Definition\ArrayDefinition; use ElementorProDeps\DI\Definition\DecoratorDefinition; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\Definition\EnvironmentVariableDefinition; use ElementorProDeps\DI\Definition\Exception\InvalidDefinition; use ElementorProDeps\DI\Definition\FactoryDefinition; use ElementorProDeps\DI\Definition\InstanceDefinition; use ElementorProDeps\DI\Definition\ObjectDefinition; use ElementorProDeps\DI\Definition\SelfResolvingDefinition; use ElementorProDeps\DI\Proxy\ProxyFactory; use ElementorProDeps\Psr\Container\ContainerInterface; /** * Dispatches to more specific resolvers. * * Dynamic dispatch pattern. * * @since 5.0 * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class ResolverDispatcher implements DefinitionResolver { /** * @var ContainerInterface */ private $container; /** * @var ProxyFactory */ private $proxyFactory; private $arrayResolver; private $factoryResolver; private $decoratorResolver; private $objectResolver; private $instanceResolver; private $envVariableResolver; public function __construct(ContainerInterface $container, ProxyFactory $proxyFactory) { $this->container = $container; $this->proxyFactory = $proxyFactory; } /** * Resolve a definition to a value. * * @param Definition $definition Object that defines how the value should be obtained. * @param array $parameters Optional parameters to use to build the entry. * * @throws InvalidDefinition If the definition cannot be resolved. * * @return mixed Value obtained from the definition. */ public function resolve(Definition $definition, array $parameters = []) { // Special case, tested early for speed if ($definition instanceof SelfResolvingDefinition) { return $definition->resolve($this->container); } $definitionResolver = $this->getDefinitionResolver($definition); return $definitionResolver->resolve($definition, $parameters); } public function isResolvable(Definition $definition, array $parameters = []) : bool { // Special case, tested early for speed if ($definition instanceof SelfResolvingDefinition) { return $definition->isResolvable($this->container); } $definitionResolver = $this->getDefinitionResolver($definition); return $definitionResolver->isResolvable($definition, $parameters); } /** * Returns a resolver capable of handling the given definition. * * @throws \RuntimeException No definition resolver was found for this type of definition. */ private function getDefinitionResolver(Definition $definition) : DefinitionResolver { switch (\true) { case $definition instanceof ObjectDefinition: if (!$this->objectResolver) { $this->objectResolver = new ObjectCreator($this, $this->proxyFactory); } return $this->objectResolver; case $definition instanceof DecoratorDefinition: if (!$this->decoratorResolver) { $this->decoratorResolver = new DecoratorResolver($this->container, $this); } return $this->decoratorResolver; case $definition instanceof FactoryDefinition: if (!$this->factoryResolver) { $this->factoryResolver = new FactoryResolver($this->container, $this); } return $this->factoryResolver; case $definition instanceof ArrayDefinition: if (!$this->arrayResolver) { $this->arrayResolver = new ArrayResolver($this); } return $this->arrayResolver; case $definition instanceof EnvironmentVariableDefinition: if (!$this->envVariableResolver) { $this->envVariableResolver = new EnvironmentVariableResolver($this); } return $this->envVariableResolver; case $definition instanceof InstanceDefinition: if (!$this->instanceResolver) { $this->instanceResolver = new InstanceInjector($this, $this->proxyFactory); } return $this->instanceResolver; default: throw new \RuntimeException('No definition resolver was configured for definition of type ' . \get_class($definition)); } } } php-di/php-di/src/Definition/Resolver/ParameterResolver.php 0000644 00000007035 14717614651 0017762 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Resolver; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\Definition\Exception\InvalidDefinition; use ElementorProDeps\DI\Definition\ObjectDefinition\MethodInjection; use ReflectionMethod; use ReflectionParameter; /** * Resolves parameters for a function call. * * @since 4.2 * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class ParameterResolver { /** * @var DefinitionResolver */ private $definitionResolver; /** * @param DefinitionResolver $definitionResolver Will be used to resolve nested definitions. */ public function __construct(DefinitionResolver $definitionResolver) { $this->definitionResolver = $definitionResolver; } /** * @throws InvalidDefinition A parameter has no value defined or guessable. * @return array Parameters to use to call the function. */ public function resolveParameters(MethodInjection $definition = null, ReflectionMethod $method = null, array $parameters = []) { $args = []; if (!$method) { return $args; } $definitionParameters = $definition ? $definition->getParameters() : []; foreach ($method->getParameters() as $index => $parameter) { if (\array_key_exists($parameter->getName(), $parameters)) { // Look in the $parameters array $value =& $parameters[$parameter->getName()]; } elseif (\array_key_exists($index, $definitionParameters)) { // Look in the definition $value =& $definitionParameters[$index]; } else { // If the parameter is optional and wasn't specified, we take its default value if ($parameter->isDefaultValueAvailable() || $parameter->isOptional()) { $args[] = $this->getParameterDefaultValue($parameter, $method); continue; } throw new InvalidDefinition(\sprintf('Parameter $%s of %s has no value defined or guessable', $parameter->getName(), $this->getFunctionName($method))); } // Nested definitions if ($value instanceof Definition) { // If the container cannot produce the entry, we can use the default parameter value if ($parameter->isOptional() && !$this->definitionResolver->isResolvable($value)) { $value = $this->getParameterDefaultValue($parameter, $method); } else { $value = $this->definitionResolver->resolve($value); } } $args[] =& $value; } return $args; } /** * Returns the default value of a function parameter. * * @throws InvalidDefinition Can't get default values from PHP internal classes and functions * @return mixed */ private function getParameterDefaultValue(ReflectionParameter $parameter, ReflectionMethod $function) { try { return $parameter->getDefaultValue(); } catch (\ReflectionException $e) { 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))); } } private function getFunctionName(ReflectionMethod $method) : string { return $method->getName() . '()'; } } php-di/php-di/src/Definition/Resolver/DefinitionResolver.php 0000644 00000002152 14717614651 0020125 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Resolver; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\Definition\Exception\InvalidDefinition; /** * Resolves a definition to a value. * * @since 4.0 * @author Matthieu Napoli <matthieu@mnapoli.fr> */ interface DefinitionResolver { /** * Resolve a definition to a value. * * @param Definition $definition Object that defines how the value should be obtained. * @param array $parameters Optional parameters to use to build the entry. * * @throws InvalidDefinition If the definition cannot be resolved. * * @return mixed Value obtained from the definition. */ public function resolve(Definition $definition, array $parameters = []); /** * Check if a definition can be resolved. * * @param Definition $definition Object that defines how the value should be obtained. * @param array $parameters Optional parameters to use to build the entry. */ public function isResolvable(Definition $definition, array $parameters = []) : bool; } php-di/php-di/src/Definition/Resolver/InstanceInjector.php 0000644 00000002274 14717614651 0017562 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Resolver; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\Definition\InstanceDefinition; use ElementorProDeps\DI\DependencyException; use ElementorProDeps\Psr\Container\NotFoundExceptionInterface; /** * Injects dependencies on an existing instance. * * @since 5.0 * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class InstanceInjector extends ObjectCreator { /** * Injects dependencies on an existing instance. * * @param InstanceDefinition $definition */ public function resolve(Definition $definition, array $parameters = []) { try { $this->injectMethodsAndProperties($definition->getInstance(), $definition->getObjectDefinition()); } catch (NotFoundExceptionInterface $e) { $message = \sprintf('Error while injecting dependencies into %s: %s', \get_class($definition->getInstance()), $e->getMessage()); throw new DependencyException($message, 0, $e); } return $definition; } public function isResolvable(Definition $definition, array $parameters = []) : bool { return \true; } } php-di/php-di/src/Definition/Resolver/DecoratorResolver.php 0000644 00000004515 14717614651 0017764 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Resolver; use ElementorProDeps\DI\Definition\DecoratorDefinition; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\Definition\Exception\InvalidDefinition; use ElementorProDeps\Psr\Container\ContainerInterface; /** * Resolves a decorator definition to a value. * * @since 5.0 * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class DecoratorResolver implements DefinitionResolver { /** * @var ContainerInterface */ private $container; /** * @var DefinitionResolver */ private $definitionResolver; /** * The resolver needs a container. This container will be passed to the factory as a parameter * so that the factory can access other entries of the container. * * @param DefinitionResolver $definitionResolver Used to resolve nested definitions. */ public function __construct(ContainerInterface $container, DefinitionResolver $definitionResolver) { $this->container = $container; $this->definitionResolver = $definitionResolver; } /** * Resolve a decorator definition to a value. * * This will call the callable of the definition and pass it the decorated entry. * * @param DecoratorDefinition $definition */ public function resolve(Definition $definition, array $parameters = []) { $callable = $definition->getCallable(); if (!\is_callable($callable)) { throw new InvalidDefinition(\sprintf('The decorator "%s" is not callable', $definition->getName())); } $decoratedDefinition = $definition->getDecoratedDefinition(); if (!$decoratedDefinition instanceof Definition) { if (!$definition->getName()) { throw new InvalidDefinition('Decorators cannot be nested in another definition'); } throw new InvalidDefinition(\sprintf('Entry "%s" decorates nothing: no previous definition with the same name was found', $definition->getName())); } $decorated = $this->definitionResolver->resolve($decoratedDefinition, $parameters); return \call_user_func($callable, $decorated, $this->container); } public function isResolvable(Definition $definition, array $parameters = []) : bool { return \true; } } php-di/php-di/src/Definition/Resolver/FactoryResolver.php 0000644 00000007374 14717614651 0017457 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Resolver; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\Definition\Exception\InvalidDefinition; use ElementorProDeps\DI\Definition\FactoryDefinition; use ElementorProDeps\DI\Invoker\FactoryParameterResolver; use ElementorProDeps\Invoker\Exception\NotCallableException; use ElementorProDeps\Invoker\Exception\NotEnoughParametersException; use ElementorProDeps\Invoker\Invoker; use ElementorProDeps\Invoker\ParameterResolver\AssociativeArrayResolver; use ElementorProDeps\Invoker\ParameterResolver\DefaultValueResolver; use ElementorProDeps\Invoker\ParameterResolver\NumericArrayResolver; use ElementorProDeps\Invoker\ParameterResolver\ResolverChain; use ElementorProDeps\Psr\Container\ContainerInterface; /** * Resolves a factory definition to a value. * * @since 4.0 * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class FactoryResolver implements DefinitionResolver { /** * @var ContainerInterface */ private $container; /** * @var Invoker|null */ private $invoker; /** * @var DefinitionResolver */ private $resolver; /** * The resolver needs a container. This container will be passed to the factory as a parameter * so that the factory can access other entries of the container. */ public function __construct(ContainerInterface $container, DefinitionResolver $resolver) { $this->container = $container; $this->resolver = $resolver; } /** * Resolve a factory definition to a value. * * This will call the callable of the definition. * * @param FactoryDefinition $definition */ public function resolve(Definition $definition, array $parameters = []) { if (!$this->invoker) { $parameterResolver = new ResolverChain([new AssociativeArrayResolver(), new FactoryParameterResolver($this->container), new NumericArrayResolver(), new DefaultValueResolver()]); $this->invoker = new Invoker($parameterResolver, $this->container); } $callable = $definition->getCallable(); try { $providedParams = [$this->container, $definition]; $extraParams = $this->resolveExtraParams($definition->getParameters()); $providedParams = \array_merge($providedParams, $extraParams, $parameters); return $this->invoker->call($callable, $providedParams); } catch (NotCallableException $e) { // Custom error message to help debugging if (\is_string($callable) && \class_exists($callable) && \method_exists($callable, '__invoke')) { throw new InvalidDefinition(\sprintf('Entry "%s" cannot be resolved: factory %s. Invokable classes cannot be automatically resolved if autowiring is disabled on the container, you need to enable autowiring or define the entry manually.', $definition->getName(), $e->getMessage())); } throw new InvalidDefinition(\sprintf('Entry "%s" cannot be resolved: factory %s', $definition->getName(), $e->getMessage())); } catch (NotEnoughParametersException $e) { throw new InvalidDefinition(\sprintf('Entry "%s" cannot be resolved: %s', $definition->getName(), $e->getMessage())); } } public function isResolvable(Definition $definition, array $parameters = []) : bool { return \true; } private function resolveExtraParams(array $params) : array { $resolved = []; foreach ($params as $key => $value) { // Nested definitions if ($value instanceof Definition) { $value = $this->resolver->resolve($value); } $resolved[$key] = $value; } return $resolved; } } php-di/php-di/src/Definition/Resolver/ArrayResolver.php 0000644 00000003623 14717614651 0017117 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Resolver; use ElementorProDeps\DI\Definition\ArrayDefinition; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\DependencyException; use Exception; /** * Resolves an array definition to a value. * * @since 5.0 * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class ArrayResolver implements DefinitionResolver { /** * @var DefinitionResolver */ private $definitionResolver; /** * @param DefinitionResolver $definitionResolver Used to resolve nested definitions. */ public function __construct(DefinitionResolver $definitionResolver) { $this->definitionResolver = $definitionResolver; } /** * Resolve an array definition to a value. * * An array definition can contain simple values or references to other entries. * * @param ArrayDefinition $definition */ public function resolve(Definition $definition, array $parameters = []) : array { $values = $definition->getValues(); // Resolve nested definitions \array_walk_recursive($values, function (&$value, $key) use($definition) { if ($value instanceof Definition) { $value = $this->resolveDefinition($value, $definition, $key); } }); return $values; } public function isResolvable(Definition $definition, array $parameters = []) : bool { return \true; } private function resolveDefinition(Definition $value, ArrayDefinition $definition, $key) { try { return $this->definitionResolver->resolve($value); } catch (DependencyException $e) { throw $e; } catch (Exception $e) { throw new DependencyException(\sprintf('Error while resolving %s[%s]. %s', $definition->getName(), $key, $e->getMessage()), 0, $e); } } } php-di/php-di/src/Definition/Resolver/ObjectCreator.php 0000644 00000015371 14717614651 0017050 0 ustar 00 <?php declare (strict_types=1); namespace ElementorProDeps\DI\Definition\Resolver; use ElementorProDeps\DI\Definition\Definition; use ElementorProDeps\DI\Definition\Exception\InvalidDefinition; use ElementorProDeps\DI\Definition\ObjectDefinition; use ElementorProDeps\DI\Definition\ObjectDefinition\PropertyInjection; use ElementorProDeps\DI\DependencyException; use ElementorProDeps\DI\Proxy\ProxyFactory; use Exception; use ElementorProDeps\ProxyManager\Proxy\LazyLoadingInterface; use ElementorProDeps\Psr\Container\NotFoundExceptionInterface; use ReflectionClass; use ReflectionProperty; /** * Create objects based on an object definition. * * @since 4.0 * @author Matthieu Napoli <matthieu@mnapoli.fr> */ class ObjectCreator implements DefinitionResolver { /** * @var ProxyFactory */ private $proxyFactory; /** * @var ParameterResolver */ private $parameterResolver; /** * @var DefinitionResolver */ private $definitionResolver; /** * @param DefinitionResolver $definitionResolver Used to resolve nested definitions. * @param ProxyFactory $proxyFactory Used to create proxies for lazy injections. */ public function __construct(DefinitionResolver $definitionResolver, ProxyFactory $proxyFactory) { $this->definitionResolver = $definitionResolver; $this->proxyFactory = $proxyFactory; $this->parameterResolver = new ParameterResolver($definitionResolver); } /** * Resolve a class definition to a value. * * This will create a new instance of the class using the injections points defined. * * @param ObjectDefinition $definition * * @return object|null */ public function resolve(Definition $definition, array $parameters = []) { // Lazy? if ($definition->isLazy()) { return $this->createProxy($definition, $parameters); } return $this->createInstance($definition, $parameters); } /** * The definition is not resolvable if the class is not instantiable (interface or abstract) * or if the class doesn't exist. * * @param ObjectDefinition $definition */ public function isResolvable(Definition $definition, array $parameters = []) : bool { return $definition->isInstantiable(); } /** * Returns a proxy instance. */ private function createProxy(ObjectDefinition $definition, array $parameters) : LazyLoadingInterface { /** @noinspection PhpUnusedParameterInspection */ $proxy = $this->proxyFactory->createProxy($definition->getClassName(), function (&$wrappedObject, $proxy, $method, $params, &$initializer) use($definition, $parameters) { $wrappedObject = $this->createInstance($definition, $parameters); $initializer = null; // turning off further lazy initialization return \true; }); return $proxy; } /** * Creates an instance of the class and injects dependencies.. * * @param array $parameters Optional parameters to use to create the instance. * * @throws InvalidDefinition * @throws DependencyException * @return object */ private function createInstance(ObjectDefinition $definition, array $parameters) { // Check that the class is instantiable if (!$definition->isInstantiable()) { // Check that the class exists if (!$definition->classExists()) { throw InvalidDefinition::create($definition, \sprintf('Entry "%s" cannot be resolved: the class doesn\'t exist', $definition->getName())); } throw InvalidDefinition::create($definition, \sprintf('Entry "%s" cannot be resolved: the class is not instantiable', $definition->getName())); } $classname = $definition->getClassName(); $classReflection = new ReflectionClass($classname); $constructorInjection = $definition->getConstructorInjection(); try { $args = $this->parameterResolver->resolveParameters($constructorInjection, $classReflection->getConstructor(), $parameters); $object = new $classname(...$args); $this->injectMethodsAndProperties($object, $definition); } catch (NotFoundExceptionInterface $e) { throw new DependencyException(\sprintf('Error while injecting dependencies into %s: %s', $classReflection->getName(), $e->getMessage()), 0, $e); } catch (InvalidDefinition $e) { throw InvalidDefinition::create($definition, \sprintf('Entry "%s" cannot be resolved: %s', $definition->getName(), $e->getMessage())); } return $object; } protected function injectMethodsAndProperties($object, ObjectDefinition $objectDefinition) { // Property injections foreach ($objectDefinition->getPropertyInjections() as $propertyInjection) { $this->injectProperty($object, $propertyInjection); } // Method injections foreach ($objectDefinition->getMethodInjections() as $methodInjection) { $methodReflection = new \ReflectionMethod($object, $methodInjection->getMethodName()); $args = $this->parameterResolver->resolveParameters($methodInjection, $methodReflection); $methodReflection->invokeArgs($object, $args); } } /** * Inject dependencies into properties. * * @param object $object Object to inject dependencies into * @param PropertyInjection $propertyInjection Property injection definition * * @throws DependencyException * @throws InvalidDefinition */ private function injectProperty($object, PropertyInjection $propertyInjection) { $propertyName = $propertyInjection->getPropertyName(); $value = $propertyInjection->getValue(); if ($value instanceof Definition) { try { $value = $this->definitionResolver->resolve($value); } catch (DependencyException $e) { throw $e; } catch (Exception $e) { throw new DependencyException(\sprintf('Error while injecting in %s::%s. %s', \get_class($object), $propertyName, $e->getMessage()), 0, $e); } } self::setPrivatePropertyValue($propertyInjection->getClassName(), $object, $propertyName, $value); } public static function setPrivatePropertyValue(string $className = null, $object, string $propertyName, $propertyValue) { $className = $className ?: \get_class($object); $property = new ReflectionProperty($className, $propertyName); if (!$property->isPublic()) { $property->setAccessible(\true); } $property->setValue($object, $propertyValue); } } php-di/php-di/composer.json 0000644 00000003232 14717614651 0011644 0 ustar 00 { "name": "php-di\/php-di", "type": "library", "description": "The dependency injection container for humans", "keywords": [ "di", "dependency injection", "container", "ioc", "psr-11", "psr11", "container-interop" ], "homepage": "https:\/\/php-di.org\/", "license": "MIT", "autoload": { "psr-4": { "ElementorProDeps\\DI\\": "src\/" }, "files": [ "src\/functions.php" ] }, "autoload-dev": { "psr-4": { "ElementorProDeps\\DI\\Test\\IntegrationTest\\": "tests\/IntegrationTest\/", "ElementorProDeps\\DI\\Test\\UnitTest\\": "tests\/UnitTest\/" } }, "scripts": { "test": "phpunit", "format-code": "php-cs-fixer fix --allow-risky=yes", "phpstan": "phpstan analyse -l 5 -c phpstan.neon src" }, "require": { "php": ">=7.4.0", "psr\/container": "^1.0", "php-di\/invoker": "^2.0", "php-di\/phpdoc-reader": "^2.0.1", "laravel\/serializable-closure": "^1.0" }, "require-dev": { "phpunit\/phpunit": "^9.5", "mnapoli\/phpunit-easymock": "^1.2", "doctrine\/annotations": "~1.10", "ocramius\/proxy-manager": "^2.11.2", "friendsofphp\/php-cs-fixer": "^2.4", "phpstan\/phpstan": "^0.12" }, "provide": { "psr\/container-implementation": "^1.0" }, "suggest": { "doctrine\/annotations": "Install it if you want to use annotations (version ~1.2)", "ocramius\/proxy-manager": "Install it if you want to use lazy injection (version ~2.0)" } } php-di/php-di/LICENSE 0000644 00000002065 14717614651 0010132 0 ustar 00 The MIT License (MIT) Copyright (c) Matthieu Napoli Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
| ver. 1.4 |
Github
|
.
| PHP 7.4.3-4ubuntu2.24 | Генерация страницы: 0.01 |
proxy
|
phpinfo
|
Настройка