Current Path : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/lib/engine/ |
Current File : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/lib/engine/binder.php |
<?php namespace Bitrix\Main\Engine; use Bitrix\Main\ArgumentException; use Bitrix\Main\ArgumentNullException; use Bitrix\Main\ArgumentTypeException; use Bitrix\Main\Event; use Bitrix\Main\ObjectNotFoundException; /** * Class Binder * @package Bitrix\Main\Engine * @deprecated * @see \Bitrix\Main\Engine\AutoWire\Binder */ class Binder { const ANY_PARAMETER_NAME = -1; const STATUS_FOUND = true; const STATUS_NOT_FOUND = false; const EVENT_ON_BUILD_AUTO_WIRED_CLASSES = 'onBuildAutoWiredClasses'; private $instance; private $method; /** @var array */ private $methodParams; /** @var array */ private $args; private $listSourceParameters; /** @var \ReflectionFunctionAbstract */ private $reflectionFunctionAbstract; /** @var array */ private static $autoWiredHandlers = null; /** * Binder constructor. * @param mixed $instance Instance of the class that contains the method. * @param string $method Name of the method. * @param array $listSourceParameters List of parameters source which we want to bind. */ public function __construct($instance, $method, array $listSourceParameters) { $this->instance = $instance; $this->method = $method; $this->listSourceParameters = $listSourceParameters; self::registerDefaultAutoWirings(); //self::$autoWiredHandlers = $this->collectAutoWiredClasses(); if ($instance === null) { $this->buildReflectionFunction(); } else { $this->buildReflectionMethod(); } // $this->bindParams(); } private static function registerDefaultAutoWirings() { static $isAlreadyRegistered = false; if ($isAlreadyRegistered) { return; } $isAlreadyRegistered = true; self::registerParameter( CurrentUser::className(), function() { return CurrentUser::get(); } ); } final public static function buildForFunction($callable, array $listSourceParameters) { return AutoWire\Binder::buildForFunction($callable) ->setSourcesParametersToMap($listSourceParameters); } final public static function buildForMethod($instance, $method, array $listSourceParameters) { return AutoWire\Binder::buildForMethod($instance, $method) ->setSourcesParametersToMap($listSourceParameters); } final public static function registerParameter($className, \Closure $constructObjectByClassAndId) { self::registerDefaultAutoWirings(); $dependsOnParameter = new AutoWire\Parameter($className, $constructObjectByClassAndId); AutoWire\Binder::registerGlobalAutoWiredParameter($dependsOnParameter); self::$autoWiredHandlers[$className] = array( 'onConstructObjectByClassAndId' => $constructObjectByClassAndId, 'onConstructIdParameterName' => self::ANY_PARAMETER_NAME, ); } final public static function registerParameterDependsOnName($className, \Closure $constructObjectByClassAndId, \Closure $constructIdParameterName = null) { self::registerDefaultAutoWirings(); $dependsOnParameter = new AutoWire\Parameter( $className, $constructObjectByClassAndId, $constructIdParameterName ); AutoWire\Binder::registerGlobalAutoWiredParameter($dependsOnParameter); self::$autoWiredHandlers[$className] = array( 'onConstructObjectByClassAndId' => $constructObjectByClassAndId, 'onConstructIdParameterName' => $constructIdParameterName, ); } /** * Builds instance of reflection method. * @return void */ private function buildReflectionMethod() { $this->reflectionFunctionAbstract = new \ReflectionMethod($this->instance, $this->method); $this->reflectionFunctionAbstract->setAccessible(true); } private function buildReflectionFunction() { $this->reflectionFunctionAbstract = new \ReflectionFunction($this->method); } final protected function collectAutoWiredClasses() { $event = new Event( 'main', static::EVENT_ON_BUILD_AUTO_WIRED_CLASSES, array() ); $event->send($this); $autoWiredHandler = array(); foreach ($event->getResults() as $eventResult) { $parameters = $eventResult->getParameters(); foreach ($parameters as $handler) { if (empty($handler['class'])) { throw new ArgumentNullException('class'); } if (empty($handler['onConstructObjectByClassAndId']) || !is_callable($handler['onConstructObjectByClassAndId'], true)) { throw new ArgumentTypeException('onConstructObjectByClassAndId', 'callable'); } if (empty($handler['onConstructIdParameterName'])) { $handler['onConstructIdParameterName'] = function(\ReflectionParameter $parameter){ return $parameter->getName() . 'Id'; }; } elseif ( !is_callable($handler['onConstructIdParameterName'], true) && $handler['onConstructIdParameterName'] !== self::ANY_PARAMETER_NAME) { throw new ArgumentTypeException('onConstructIdParameterName', 'callable'); } $autoWiredHandler[$handler['class']] = array( 'onConstructObjectByClassAndId' => $handler['onConstructObjectByClassAndId'], 'onConstructIdParameterName' => $handler['onConstructIdParameterName'], ); } } return $autoWiredHandler; } /** * 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; } /** * 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()); } if ($this->reflectionFunctionAbstract instanceof \ReflectionFunction) { return $this->reflectionFunctionAbstract->invokeArgs($this->getArgs()); } } catch (\TypeError $exception) { $this->processException($exception); } catch (\ErrorException $exception) { $this->processException($exception); } return null; } private function processException($exception) { if (!($exception instanceof \TypeError) && !($exception instanceof \ErrorException)) { return; } if ( mb_stripos($exception->getMessage(), 'must be an instance of') === false || mb_stripos($exception->getMessage(), 'null given') === false ) { throw $exception; } $message = $this->extractParameterClassName($exception->getMessage()); throw new ObjectNotFoundException( "Could not find value for class {{$message}} to build auto wired argument", $exception instanceof \TypeError? null : $exception //fix it. When we will use php7.1 ); } private function extractParameterClassName($message) { if (preg_match('%must be an instance of ([a-zA-Z0-9_\\\\]+), null given%', $message, $m)) { return $m[1]; } return null; } private function buildReflectionClass(\ReflectionParameter $parameter): ?\ReflectionClass { $namedType = $parameter->getType(); if (!($namedType instanceof \ReflectionNamedType)) { return null; } if ($namedType->isBuiltin()) { return null; } return new \ReflectionClass($namedType->getName()); } private function getParameterValue(\ReflectionParameter $parameter) { $autoWiredHandler = null; $reflectionClass = $this->buildReflectionClass($parameter); if ($reflectionClass) { $autoWiredHandler = $this->getAutoWiredHandler($reflectionClass); } if ($autoWiredHandler && $reflectionClass) { $primaryId = null; if($autoWiredHandler['onConstructIdParameterName'] !== self::ANY_PARAMETER_NAME) { $parameterName = call_user_func_array($autoWiredHandler['onConstructIdParameterName'], array($parameter)); $primaryId = $this->findParameterInSourceList($parameterName, $status); if ($status === self::STATUS_NOT_FOUND) { if ($parameter->isDefaultValueAvailable()) { return $parameter->getDefaultValue(); } throw new ArgumentException( "Could not find value for parameter {{$parameterName}} to build auto wired argument {{$reflectionClass->name} {$parameter->getName()}}", $parameter ); } } return call_user_func_array( $autoWiredHandler['onConstructObjectByClassAndId'], array($reflectionClass->getName(), $primaryId) ); } $value = $this->findParameterInSourceList($parameter->getName(), $status); if ($status === self::STATUS_NOT_FOUND) { if ($parameter->isDefaultValueAvailable()) { $value = $parameter->getDefaultValue(); } else { throw new ArgumentException( "Could not find value for parameter {{$parameter->getName()}}", $parameter ); } } if ($parameter->isArray()) { $value = (array)$value; } return $value; } /** * @param array $listSourceParameters */ public function setListSourceParameters($listSourceParameters) { $this->listSourceParameters = $listSourceParameters; $this->args = $this->methodParams = null; return $this; } private function findParameterInSourceList($name, &$status) { $status = self::STATUS_FOUND; foreach ($this->listSourceParameters as $source) { if (isset($source[$name])) { return $source[$name]; } if (($source instanceof \ArrayAccess) && $source->offsetExists($name)) { return $source[$name]; } if (is_array($source) && array_key_exists($name, $source)) { return $source[$name]; } } $status = self::STATUS_NOT_FOUND; return null; } private function bindParams() { $this->args = $this->methodParams = array(); foreach ($this->reflectionFunctionAbstract->getParameters() as $param) { $value = $this->getParameterValue($param); $this->args[] = $this->methodParams[$param->getName()] = $value; } return $this->args; } private function getAutoWiredHandler(\ReflectionClass $class) { foreach (self::$autoWiredHandlers as $autoWiredClass => $handler) { if ($class->isSubclassOf($autoWiredClass) || $class->name === ltrim($autoWiredClass, '\\')) { return $handler; } } return null; } private function isAutoWiredClass(\ReflectionClass $class) { return (bool)$this->getAutoWiredHandler($class); } }