/modules/main/lib/engine/autowire/binder.php
https://gitlab.com/alexprowars/bitrix · PHP · 442 lines · 308 code · 68 blank · 66 comment · 28 complexity · ea7117b8ffb79502ab8de0160944ef1b MD5 · raw file
- <?php
- namespace Bitrix\Main\Engine\AutoWire;
- use Bitrix\Main\Result;
- class Binder
- {
- const STATUS_FOUND = true;
- const STATUS_NOT_FOUND = false;
- private $instance;
- private $method;
- /** @var array */
- private $configuration = [];
- /** @var \SplObjectStorage|Parameter[] */
- private static $globalAutoWiredParameters;
- /** @var Parameter[] */
- private $autoWiredParameters = [];
- /** @var \ReflectionFunctionAbstract */
- private $reflectionFunctionAbstract;
- /** @var array */
- private $methodParams = null;
- /** @var array */
- private $args = null;
- public function __construct($instance, $method, $configuration = [])
- {
- $this->instance = $instance;
- $this->method = $method;
- $this->configuration = $configuration;
- if ($this->instance === null)
- {
- $this->buildReflectionFunction();
- }
- else
- {
- $this->buildReflectionMethod();
- }
- }
- final public static function buildForFunction($callable, $configuration = [])
- {
- return new static(null, $callable, $configuration);
- }
- final public static function buildForMethod($instance, $method, $configuration = [])
- {
- return new static($instance, $method, $configuration);
- }
- private function buildReflectionMethod()
- {
- $this->reflectionFunctionAbstract = new \ReflectionMethod($this->instance, $this->method);
- $this->reflectionFunctionAbstract->setAccessible(true);
- }
- private function buildReflectionFunction()
- {
- $this->reflectionFunctionAbstract = new \ReflectionFunction($this->method);
- }
- /**
- * @return mixed
- */
- public function getInstance()
- {
- return $this->instance;
- }
- /**
- * @return mixed
- */
- public function getMethod()
- {
- return $this->method;
- }
- /**
- * @return array
- */
- public function getConfiguration()
- {
- return $this->configuration;
- }
- /**
- * @param array $configuration
- *
- * @return Binder
- */
- public function setConfiguration($configuration)
- {
- $this->configuration = $configuration;
- return $this;
- }
- /**
- * @param Parameter[] $parameters
- *
- * @return $this
- */
- public function setAutoWiredParameters(array $parameters)
- {
- $this->autoWiredParameters = [];
-
- foreach ($parameters as $parameter)
- {
- $this->appendAutoWiredParameter($parameter);
- }
-
- return $this;
- }
- public function appendAutoWiredParameter(Parameter $parameter)
- {
- $this->autoWiredParameters[] = $parameter;
- return $this;
- }
- /**
- * Register globally auto wired parameter. The method was added in backwards compatibility reason.
- * @param Parameter $parameter
- * @return void
- */
- public static function registerGlobalAutoWiredParameter(Parameter $parameter)
- {
- if (self::$globalAutoWiredParameters === null)
- {
- self::$globalAutoWiredParameters = new \SplObjectStorage();
- }
- if (!self::$globalAutoWiredParameters->contains($parameter))
- {
- self::$globalAutoWiredParameters[$parameter] = $parameter;
- }
- }
- /**
- * @param Parameter $parameter
- * @return void
- */
- public static function unRegisterGlobalAutoWiredParameter(Parameter $parameter): void
- {
- if (self::$globalAutoWiredParameters === null)
- {
- return;
- }
- if (self::$globalAutoWiredParameters->contains($parameter))
- {
- self::$globalAutoWiredParameters->detach($parameter);
- }
- }
- private function getPriorityByParameter(Parameter $parameter)
- {
- return $parameter->getPriority();
- }
- /**
- * @return Parameter[]
- */
- public function getAutoWiredParameters()
- {
- return $this->autoWiredParameters;
- }
- public function setSourcesParametersToMap(array $parameters)
- {
- $this->configuration['sourceParameters'] = $parameters;
- return $this;
- }
- public function getSourcesParametersToMap()
- {
- return $this->configuration['sourceParameters']?: [];
- }
- public function appendSourcesParametersToMap(array $parameter)
- {
- if (!isset($this->configuration['sourceParameters']))
- {
- $this->configuration['sourceParameters'] = [];
- }
- $this->configuration['sourceParameters'][] = $parameter;
- return $this;
- }
- /**
- * Invokes method with binded parameters.
- * return @mixed
- */
- final public function invoke()
- {
- try
- {
- if($this->reflectionFunctionAbstract instanceof \ReflectionMethod)
- {
- return $this->reflectionFunctionAbstract->invokeArgs($this->instance, $this->getArgs());
- }
- elseif ($this->reflectionFunctionAbstract instanceof \ReflectionFunction)
- {
- return $this->reflectionFunctionAbstract->invokeArgs($this->getArgs());
- }
- }
- catch (\TypeError $exception)
- {
- throw $exception;
- // $this->processException($exception);
- }
- catch (\ErrorException $exception)
- {
- throw $exception;
- // $this->processException($exception);
- }
- return null;
- }
- /**
- * Returns list of method params.
- * @return array
- */
- final public function getMethodParams()
- {
- if ($this->methodParams === null)
- {
- $this->bindParams();
- }
- return $this->methodParams;
- }
- /**
- * Sets list of method params.
- * @param array $params List of parameters.
- *
- * @return $this
- */
- final public function setMethodParams(array $params)
- {
- $this->methodParams = $params;
- $this->args = array_values($params);
- return $this;
- }
- /**
- * Returns list of method params which possible use in call_user_func_array().
- * @return array
- */
- final public function getArgs()
- {
- if ($this->args === null)
- {
- $this->bindParams();
- }
- return $this->args;
- }
- private function bindParams()
- {
- $this->args = $this->methodParams = [];
- foreach ($this->reflectionFunctionAbstract->getParameters() as $param)
- {
- $value = $this->getParameterValue($param);
- $this->args[] = $this->methodParams[$param->getName()] = $value;
- }
- return $this->args;
- }
- /**
- * @param \ReflectionParameter $reflectionParameter
- *
- * @return \SplPriorityQueue|Parameter[]
- */
- private function getAutoWiredByClass(\ReflectionParameter $reflectionParameter)
- {
- $result = new \SplPriorityQueue();
- foreach ($this->getAllAutoWiredParameters() as $parameter)
- {
- if ($parameter->match($reflectionParameter))
- {
- $result->insert($parameter, $this->getPriorityByParameter($parameter));
- }
- }
- return $result;
- }
- /**
- * @return Parameter[]
- */
- private function getAllAutoWiredParameters()
- {
- $list = $this->getAutoWiredParameters();
- foreach (self::$globalAutoWiredParameters as $globalAutoWiredParameter)
- {
- $list[] = $globalAutoWiredParameter;
- }
- return $list;
- }
- protected function constructValue(\ReflectionParameter $parameter, Parameter $autoWireParameter, Result $captureResult): Result
- {
- $result = new Result();
- $constructedValue = $autoWireParameter->constructValue($parameter, $captureResult);
- $result->setData([
- 'value' => $constructedValue,
- ]);
- return $result;
- }
- private function getParameterValue(\ReflectionParameter $parameter)
- {
- $sourceParameters = $this->getSourcesParametersToMap();
- if ($parameter->getClass())
- {
- foreach ($this->getAutoWiredByClass($parameter) as $autoWireParameter)
- {
- $result = $autoWireParameter->captureData($parameter, $sourceParameters, $this->getAllAutoWiredParameters());
- if (!$result->isSuccess())
- {
- continue;
- }
- $constructedValue = null;
- $constructResult = $this->constructValue($parameter, $autoWireParameter, $result);
- if ($constructResult->isSuccess())
- {
- ['value' => $constructedValue] = $constructResult->getData();
- }
- if ($constructedValue === null)
- {
- if ($parameter->isDefaultValueAvailable())
- {
- return $parameter->getDefaultValue();
- }
- throw new BinderArgumentException(
- "Could not construct parameter {{$parameter->getName()}}",
- $parameter,
- $constructResult->getErrors(),
- );
- }
- return $constructedValue;
- }
- if ($parameter->isDefaultValueAvailable())
- {
- return $parameter->getDefaultValue();
- }
- $exceptionMessage = "Could not find value for parameter to build auto wired argument {{$parameter->getClass()->getName()} \${$parameter->getName()}}";
- if ($result !== null && $result->getErrorMessages())
- {
- $exceptionMessage = $result->getErrorMessages()[0];
- }
- throw new BinderArgumentException(
- $exceptionMessage,
- $parameter
- );
- }
- $value = $this->findParameterInSourceList($parameter->getName(), $status);
- if ($status === self::STATUS_NOT_FOUND)
- {
- if ($parameter->isDefaultValueAvailable())
- {
- return $parameter->getDefaultValue();
- }
- throw new BinderArgumentException(
- "Could not find value for parameter {{$parameter->getName()}}",
- $parameter
- );
- }
- elseif ($parameter->getType() instanceof \ReflectionNamedType)
- {
- /** @var \ReflectionNamedType $reflectionType */
- $reflectionType = $parameter->getType();
- $declarationChecker = new TypeDeclarationChecker($reflectionType, $value);
- if (!$declarationChecker->isSatisfied())
- {
- throw new BinderArgumentException(
- "Invalid value {{$value}} to match with parameter {{$parameter->getName()}}. Should be value of type {$reflectionType->getName()}.",
- $parameter
- );
- }
- }
- if ($parameter->isArray())
- {
- $value = (array)$value;
- }
- return $value;
- }
- private function findParameterInSourceList($name, &$status)
- {
- $status = self::STATUS_FOUND;
- foreach ($this->getSourcesParametersToMap() as $source)
- {
- if (isset($source[$name]))
- {
- return $source[$name];
- }
- if ($source instanceof \ArrayAccess && $source->offsetExists($name))
- {
- return $source[$name];
- }
- elseif (is_array($source) && array_key_exists($name, $source))
- {
- return $source[$name];
- }
- }
- $status = self::STATUS_NOT_FOUND;
- return null;
- }
- }