19 private $configuration = [];
21 private static $globalAutoWiredParameters;
23 private $autoWiredParameters = [];
25 private $reflectionFunctionAbstract;
27 private $methodParams =
null;
31 public function __construct($instance, $method, $configuration = [])
33 $this->instance = $instance;
34 $this->method = $method;
35 $this->configuration = $configuration;
37 if ($this->instance ===
null)
39 $this->buildReflectionFunction();
43 $this->buildReflectionMethod();
50 return new static(
null, $callable, $configuration);
53 final public static function buildForMethod($instance, $method, $configuration = [])
55 return new static($instance, $method, $configuration);
58 private function buildReflectionMethod()
60 $this->reflectionFunctionAbstract = new \ReflectionMethod($this->instance, $this->method);
61 $this->reflectionFunctionAbstract->setAccessible(
true);
64 private function buildReflectionFunction()
66 $this->reflectionFunctionAbstract = new \ReflectionFunction($this->method);
90 return $this->configuration;
100 $this->configuration = $configuration;
112 $this->autoWiredParameters = [];
114 foreach ($parameters as $parameter)
124 $this->autoWiredParameters[] = $parameter;
136 if (self::$globalAutoWiredParameters ===
null)
138 self::$globalAutoWiredParameters = new \SplObjectStorage();
141 if (!self::$globalAutoWiredParameters->contains($parameter))
143 self::$globalAutoWiredParameters[$parameter] = $parameter;
153 if (self::$globalAutoWiredParameters ===
null)
158 if (self::$globalAutoWiredParameters->contains($parameter))
160 self::$globalAutoWiredParameters->detach($parameter);
164 private function getPriorityByParameter(
Parameter $parameter)
174 return $this->autoWiredParameters;
179 $this->configuration[
'sourceParameters'] = $parameters;
186 return $this->configuration[
'sourceParameters']?: [];
191 if (!isset($this->configuration[
'sourceParameters']))
193 $this->configuration[
'sourceParameters'] = [];
196 $this->configuration[
'sourceParameters'][] = $parameter;
209 if ($this->reflectionFunctionAbstract instanceof \ReflectionMethod)
211 return $this->reflectionFunctionAbstract->invokeArgs($this->instance, $this->
getArgs());
214 if ($this->reflectionFunctionAbstract instanceof \ReflectionFunction)
216 return $this->reflectionFunctionAbstract->invokeArgs($this->
getArgs());
219 catch (\TypeError $exception)
223 catch (\ErrorException $exception)
237 if ($this->methodParams ===
null)
242 return $this->methodParams;
254 $this->args = array_values(
$params);
265 if ($this->args ===
null)
273 private function bindParams()
275 $this->args = $this->methodParams = [];
277 foreach ($this->reflectionFunctionAbstract->getParameters() as $param)
279 $value = $this->getParameterValue($param);
280 $this->args[] = $this->methodParams[$param->getName()] = $value;
291 private function getAutoWiredByClass(\ReflectionParameter $reflectionParameter)
293 $result = new \SplPriorityQueue();
294 foreach ($this->getAllAutoWiredParameters() as $parameter)
296 if ($parameter->match($reflectionParameter))
298 $result->insert($parameter, $this->getPriorityByParameter($parameter));
308 private function getAllAutoWiredParameters()
311 if (self::$globalAutoWiredParameters)
313 foreach (self::$globalAutoWiredParameters as $globalAutoWiredParameter)
315 $list[] = $globalAutoWiredParameter;
326 $constructedValue = $autoWireParameter->
constructValue($parameter, $captureResult);
329 'value' => $constructedValue,
340 private function getParameterValue(\ReflectionParameter $parameter)
344 $reflectionType = $parameter->getType();
346 ($reflectionType instanceof \ReflectionUnionType)
347 || ($reflectionType instanceof \ReflectionIntersectionType)
351 "Currently there is no support for union or intersection types {{$parameter->getName()}}",
356 if (($reflectionType instanceof \ReflectionNamedType) && !$reflectionType->isBuiltin())
358 foreach ($this->getAutoWiredByClass($parameter) as $autoWireParameter)
360 $result = $autoWireParameter->captureData($parameter, $sourceParameters, $this->getAllAutoWiredParameters());
366 $constructedValue =
null;
368 if ($constructResult->isSuccess())
370 [
'value' => $constructedValue] = $constructResult->getData();
373 if ($constructedValue ===
null)
375 if ($parameter->allowsNull())
380 if ($parameter->isDefaultValueAvailable())
382 return $parameter->getDefaultValue();
385 throw new BinderArgumentException(
386 "Could not construct parameter {{$parameter->getName()}}",
388 $constructResult->getErrors(),
392 (
new ValidationChecker($parameter, $constructedValue))->check();
394 return $constructedValue;
397 $reflectionClass = new \ReflectionClass($reflectionType->getName());
398 if ($reflectionClass->isEnum())
400 $enum = new \ReflectionEnum($reflectionType->getName());
401 $backedType = $enum->getBackingType();
402 if ($backedType && $enum->isBacked())
404 $backedValue = $this->findParameterInSourceList($parameter->getName(),
$status);
405 if (
$status === self::STATUS_NOT_FOUND)
407 if ($parameter->isDefaultValueAvailable())
409 return $parameter->getDefaultValue();
412 throw new BinderArgumentException(
413 "Could not find value for parameter {{$parameter->getName()}}",
417 $declarationChecker =
new TypeDeclarationChecker($backedType, $backedValue);
418 if (!$declarationChecker->isSatisfied())
420 throw new BinderArgumentException(
421 "Invalid value {{$backedValue}} to match with parameter {{$parameter->getName()}}. Should be value of type {$backedType->getName()}.",
426 $enumValue = $reflectionType->getName()::tryFrom($backedValue);
434 if ($parameter->isDefaultValueAvailable())
436 return $parameter->getDefaultValue();
441 if (class_exists($reflectionType->getName()))
443 if ($object = ServiceLocator::getInstance()->
get($reflectionType->getName()))
449 catch (ObjectNotFoundException $exception)
451 throw new BinderArgumentException(
452 $exception->getMessage(),
457 $exceptionMessage =
"Could not find value for parameter to build auto wired argument {{$reflectionType->getName()} \${$parameter->getName()}}";
460 $exceptionMessage =
$result->getErrorMessages()[0];
463 throw new BinderArgumentException(
469 $value = $this->findParameterInSourceList($parameter->getName(),
$status);
470 if (
$status === self::STATUS_NOT_FOUND)
472 if ($parameter->isDefaultValueAvailable())
474 return $parameter->getDefaultValue();
477 throw new BinderArgumentException(
478 "Could not find value for parameter {{$parameter->getName()}}",
482 if ($reflectionType instanceof \ReflectionNamedType)
484 $declarationChecker =
new TypeDeclarationChecker($reflectionType, $value);
485 if (!$declarationChecker->isSatisfied())
487 throw new BinderArgumentException(
488 "Invalid value {{$value}} to match with parameter {{$parameter->getName()}}. Should be value of type {$reflectionType->getName()}.",
492 if ($declarationChecker->isArray())
497 (
new ValidationChecker($parameter, $value))->check();
503 private function findParameterInSourceList(
$name, &
$status)
508 if (isset($source[
$name]))
510 return $source[
$name];
513 if ($source instanceof \ArrayAccess && $source->offsetExists(
$name))
515 return $source[
$name];
518 if (is_array($source) && array_key_exists(
$name, $source))
520 return $source[
$name];
524 $status = self::STATUS_NOT_FOUND;
__construct($instance, $method, $configuration=[])
static buildForMethod($instance, $method, $configuration=[])
constructValue(\ReflectionParameter $parameter, Parameter $autoWireParameter, Result $captureResult)
constructValue(\ReflectionParameter $parameter, Result $captureResult, $newThis=null)