Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
binder.php
1<?php
2
3namespace Bitrix\Main\Engine;
4
5
11
18class Binder
19{
21
22 const STATUS_FOUND = true;
23 const STATUS_NOT_FOUND = false;
24
25 const EVENT_ON_BUILD_AUTO_WIRED_CLASSES = 'onBuildAutoWiredClasses';
26
27 private $instance;
28 private $method;
30 private $methodParams;
32 private $args;
33 private $listSourceParameters;
35 private $reflectionFunctionAbstract;
37 private static $autoWiredHandlers = null;
38
45 public function __construct($instance, $method, array $listSourceParameters)
46 {
47 $this->instance = $instance;
48 $this->method = $method;
49 $this->listSourceParameters = $listSourceParameters;
50
51 self::registerDefaultAutoWirings();
52 //self::$autoWiredHandlers = $this->collectAutoWiredClasses();
53
54 if ($instance === null)
55 {
56 $this->buildReflectionFunction();
57 }
58 else
59 {
60 $this->buildReflectionMethod();
61 }
62
63// $this->bindParams();
64 }
65
66 private static function registerDefaultAutoWirings()
67 {
68 static $isAlreadyRegistered = false;
69 if ($isAlreadyRegistered)
70 {
71 return;
72 }
73
74 $isAlreadyRegistered = true;
75 self::registerParameter(
77 function() {
78 return CurrentUser::get();
79 }
80 );
81 }
82
83 final public static function buildForFunction($callable, array $listSourceParameters)
84 {
85 return AutoWire\Binder::buildForFunction($callable)
86 ->setSourcesParametersToMap($listSourceParameters);
87 }
88
89 final public static function buildForMethod($instance, $method, array $listSourceParameters)
90 {
91 return AutoWire\Binder::buildForMethod($instance, $method)
92 ->setSourcesParametersToMap($listSourceParameters);
93 }
94
95 final public static function registerParameter($className, \Closure $constructObjectByClassAndId)
96 {
97 self::registerDefaultAutoWirings();
98
99 $dependsOnParameter = new AutoWire\Parameter($className, $constructObjectByClassAndId);
100 AutoWire\Binder::registerGlobalAutoWiredParameter($dependsOnParameter);
101
102 self::$autoWiredHandlers[$className] = array(
103 'onConstructObjectByClassAndId' => $constructObjectByClassAndId,
104 'onConstructIdParameterName' => self::ANY_PARAMETER_NAME,
105 );
106 }
107
108 final public static function registerParameterDependsOnName($className, \Closure $constructObjectByClassAndId, \Closure $constructIdParameterName = null)
109 {
110 self::registerDefaultAutoWirings();
111
112 $dependsOnParameter = new AutoWire\Parameter(
113 $className, $constructObjectByClassAndId, $constructIdParameterName
114 );
115 AutoWire\Binder::registerGlobalAutoWiredParameter($dependsOnParameter);
116
117 self::$autoWiredHandlers[$className] = array(
118 'onConstructObjectByClassAndId' => $constructObjectByClassAndId,
119 'onConstructIdParameterName' => $constructIdParameterName,
120 );
121 }
122
127 private function buildReflectionMethod()
128 {
129 $this->reflectionFunctionAbstract = new \ReflectionMethod($this->instance, $this->method);
130 $this->reflectionFunctionAbstract->setAccessible(true);
131 }
132
133 private function buildReflectionFunction()
134 {
135 $this->reflectionFunctionAbstract = new \ReflectionFunction($this->method);
136 }
137
138 final protected function collectAutoWiredClasses()
139 {
140 $event = new Event(
141 'main',
142 static::EVENT_ON_BUILD_AUTO_WIRED_CLASSES,
143 array()
144 );
145 $event->send($this);
146
147 $autoWiredHandler = array();
148 foreach ($event->getResults() as $eventResult)
149 {
150 $parameters = $eventResult->getParameters();
151 foreach ($parameters as $handler)
152 {
153 if (empty($handler['class']))
154 {
155 throw new ArgumentNullException('class');
156 }
157
158 if (empty($handler['onConstructObjectByClassAndId']) || !is_callable($handler['onConstructObjectByClassAndId'], true))
159 {
160 throw new ArgumentTypeException('onConstructObjectByClassAndId', 'callable');
161 }
162
163 if (empty($handler['onConstructIdParameterName']))
164 {
165 $handler['onConstructIdParameterName'] = function(\ReflectionParameter $parameter){
166 return $parameter->getName() . 'Id';
167 };
168 }
169 elseif (
170 !is_callable($handler['onConstructIdParameterName'], true) &&
171 $handler['onConstructIdParameterName'] !== self::ANY_PARAMETER_NAME)
172 {
173 throw new ArgumentTypeException('onConstructIdParameterName', 'callable');
174 }
175
176 $autoWiredHandler[$handler['class']] = array(
177 'onConstructObjectByClassAndId' => $handler['onConstructObjectByClassAndId'],
178 'onConstructIdParameterName' => $handler['onConstructIdParameterName'],
179 );
180 }
181 }
182
183 return $autoWiredHandler;
184 }
185
190 final public function getMethodParams()
191 {
192 if ($this->methodParams === null)
193 {
194 $this->bindParams();
195 }
196
197 return $this->methodParams;
198 }
199
206 final public function setMethodParams(array $params)
207 {
208 $this->methodParams = $params;
209 $this->args = array_values($params);
210
211 return $this;
212 }
213
218 final public function getArgs()
219 {
220 if ($this->args === null)
221 {
222 $this->bindParams();
223 }
224
225 return $this->args;
226 }
227
232 final public function invoke()
233 {
234 try
235 {
236 if ($this->reflectionFunctionAbstract instanceof \ReflectionMethod)
237 {
238 return $this->reflectionFunctionAbstract->invokeArgs($this->instance, $this->getArgs());
239 }
240 if ($this->reflectionFunctionAbstract instanceof \ReflectionFunction)
241 {
242 return $this->reflectionFunctionAbstract->invokeArgs($this->getArgs());
243 }
244 }
245 catch (\TypeError $exception)
246 {
247 $this->processException($exception);
248 }
249 catch (\ErrorException $exception)
250 {
251 $this->processException($exception);
252 }
253
254 return null;
255 }
256
257 private function processException($exception)
258 {
259 if (!($exception instanceof \TypeError) && !($exception instanceof \ErrorException))
260 {
261 return;
262 }
263
264 if (
265 mb_stripos($exception->getMessage(), 'must be an instance of') === false ||
266 mb_stripos($exception->getMessage(), 'null given') === false
267 )
268 {
269 throw $exception;
270 }
271
272 $message = $this->extractParameterClassName($exception->getMessage());
273
274 throw new ObjectNotFoundException(
275 "Could not find value for class {{$message}} to build auto wired argument",
276 $exception instanceof \TypeError? null : $exception //fix it. When we will use php7.1
277 );
278 }
279
280 private function extractParameterClassName($message)
281 {
282 if (preg_match('%must be an instance of ([a-zA-Z0-9_\\\\]+), null given%', $message, $m))
283 {
284 return $m[1];
285 }
286
287 return null;
288 }
289
290 private function buildReflectionClass(\ReflectionParameter $parameter): ?\ReflectionClass
291 {
292 $namedType = $parameter->getType();
293 if (!($namedType instanceof \ReflectionNamedType))
294 {
295 return null;
296 }
297 if ($namedType->isBuiltin())
298 {
299 return null;
300 }
301
302 return new \ReflectionClass($namedType->getName());
303 }
304
305 private function getParameterValue(\ReflectionParameter $parameter)
306 {
307 $autoWiredHandler = null;
308 $reflectionClass = $this->buildReflectionClass($parameter);
309 if ($reflectionClass)
310 {
311 $autoWiredHandler = $this->getAutoWiredHandler($reflectionClass);
312 }
313
314 if ($autoWiredHandler && $reflectionClass)
315 {
316 $primaryId = null;
317 if($autoWiredHandler['onConstructIdParameterName'] !== self::ANY_PARAMETER_NAME)
318 {
319 $parameterName = call_user_func_array($autoWiredHandler['onConstructIdParameterName'], array($parameter));
320 $primaryId = $this->findParameterInSourceList($parameterName, $status);
321 if ($status === self::STATUS_NOT_FOUND)
322 {
323 if ($parameter->isDefaultValueAvailable())
324 {
325 return $parameter->getDefaultValue();
326 }
327
328 throw new ArgumentException(
329 "Could not find value for parameter {{$parameterName}} to build auto wired argument {{$reflectionClass->name} {$parameter->getName()}}",
330 $parameter
331 );
332 }
333 }
334
335 return call_user_func_array(
336 $autoWiredHandler['onConstructObjectByClassAndId'],
337 array($reflectionClass->getName(), $primaryId)
338 );
339 }
340
341 $value = $this->findParameterInSourceList($parameter->getName(), $status);
342 if ($status === self::STATUS_NOT_FOUND)
343 {
344 if ($parameter->isDefaultValueAvailable())
345 {
346 $value = $parameter->getDefaultValue();
347 }
348 else
349 {
350 throw new ArgumentException(
351 "Could not find value for parameter {{$parameter->getName()}}",
352 $parameter
353 );
354 }
355 }
356
357 if ($parameter->isArray())
358 {
359 $value = (array)$value;
360 }
361
362 return $value;
363 }
364
368 public function setListSourceParameters($listSourceParameters)
369 {
370 $this->listSourceParameters = $listSourceParameters;
371 $this->args = $this->methodParams = null;
372
373 return $this;
374 }
375
376 private function findParameterInSourceList($name, &$status)
377 {
378 $status = self::STATUS_FOUND;
379 foreach ($this->listSourceParameters as $source)
380 {
381 if (isset($source[$name]))
382 {
383 return $source[$name];
384 }
385
386 if (($source instanceof \ArrayAccess) && $source->offsetExists($name))
387 {
388 return $source[$name];
389 }
390 if (is_array($source) && array_key_exists($name, $source))
391 {
392 return $source[$name];
393 }
394 }
395
396 $status = self::STATUS_NOT_FOUND;
397
398 return null;
399 }
400
401 private function bindParams()
402 {
403 $this->args = $this->methodParams = array();
404
405 foreach ($this->reflectionFunctionAbstract->getParameters() as $param)
406 {
407 $value = $this->getParameterValue($param);
408 $this->args[] = $this->methodParams[$param->getName()] = $value;
409 }
410
411 return $this->args;
412 }
413
414 private function getAutoWiredHandler(\ReflectionClass $class)
415 {
416 foreach (self::$autoWiredHandlers as $autoWiredClass => $handler)
417 {
418 if ($class->isSubclassOf($autoWiredClass) || $class->name === ltrim($autoWiredClass, '\\'))
419 {
420 return $handler;
421 }
422 }
423
424 return null;
425 }
426
427 private function isAutoWiredClass(\ReflectionClass $class)
428 {
429 return (bool)$this->getAutoWiredHandler($class);
430 }
431}
__construct($instance, $method, array $listSourceParameters)
Definition binder.php:45
static buildForFunction($callable, array $listSourceParameters)
Definition binder.php:83
static registerParameter($className, \Closure $constructObjectByClassAndId)
Definition binder.php:95
static registerParameterDependsOnName($className, \Closure $constructObjectByClassAndId, \Closure $constructIdParameterName=null)
Definition binder.php:108
static buildForMethod($instance, $method, array $listSourceParameters)
Definition binder.php:89
setListSourceParameters($listSourceParameters)
Definition binder.php:368
const EVENT_ON_BUILD_AUTO_WIRED_CLASSES
Definition binder.php:25
setMethodParams(array $params)
Definition binder.php:206