ArgumentsResolver.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. <?php
  2. namespace Drupal\Component\Utility;
  3. /**
  4. * Resolves the arguments to pass to a callable.
  5. */
  6. class ArgumentsResolver implements ArgumentsResolverInterface {
  7. /**
  8. * An associative array of parameter names to scalar candidate values.
  9. *
  10. * @var array
  11. */
  12. protected $scalars;
  13. /**
  14. * An associative array of parameter names to object candidate values.
  15. *
  16. * @var array
  17. */
  18. protected $objects;
  19. /**
  20. * An array object candidates tried on every parameter regardless of name.
  21. *
  22. * @var array
  23. */
  24. protected $wildcards;
  25. /**
  26. * Constructs a new ArgumentsResolver.
  27. *
  28. * @param array $scalars
  29. * An associative array of parameter names to scalar candidate values.
  30. * @param object[] $objects
  31. * An associative array of parameter names to object candidate values.
  32. * @param object[] $wildcards
  33. * An array object candidates tried on every parameter regardless of its
  34. * name.
  35. */
  36. public function __construct(array $scalars, array $objects, array $wildcards) {
  37. $this->scalars = $scalars;
  38. $this->objects = $objects;
  39. $this->wildcards = $wildcards;
  40. }
  41. /**
  42. * {@inheritdoc}
  43. */
  44. public function getArguments(callable $callable) {
  45. $arguments = [];
  46. foreach ($this->getReflector($callable)->getParameters() as $parameter) {
  47. $arguments[] = $this->getArgument($parameter);
  48. }
  49. return $arguments;
  50. }
  51. /**
  52. * Gets the argument value for a parameter.
  53. *
  54. * @param \ReflectionParameter $parameter
  55. * The parameter of a callable to get the value for.
  56. *
  57. * @return mixed
  58. * The value of the requested parameter value.
  59. *
  60. * @throws \RuntimeException
  61. * Thrown when there is a missing parameter.
  62. */
  63. protected function getArgument(\ReflectionParameter $parameter) {
  64. $parameter_type_hint = $parameter->getClass();
  65. $parameter_name = $parameter->getName();
  66. // If the argument exists and is NULL, return it, regardless of
  67. // parameter type hint.
  68. if (!isset($this->objects[$parameter_name]) && array_key_exists($parameter_name, $this->objects)) {
  69. return NULL;
  70. }
  71. if ($parameter_type_hint) {
  72. // If the argument exists and complies with the type hint, return it.
  73. if (isset($this->objects[$parameter_name]) && is_object($this->objects[$parameter_name]) && $parameter_type_hint->isInstance($this->objects[$parameter_name])) {
  74. return $this->objects[$parameter_name];
  75. }
  76. // Otherwise, resolve wildcard arguments by type matching.
  77. foreach ($this->wildcards as $wildcard) {
  78. if ($parameter_type_hint->isInstance($wildcard)) {
  79. return $wildcard;
  80. }
  81. }
  82. }
  83. elseif (isset($this->scalars[$parameter_name])) {
  84. return $this->scalars[$parameter_name];
  85. }
  86. // If the callable provides a default value, use it.
  87. if ($parameter->isDefaultValueAvailable()) {
  88. return $parameter->getDefaultValue();
  89. }
  90. // Can't resolve it: call a method that throws an exception or can be
  91. // overridden to do something else.
  92. return $this->handleUnresolvedArgument($parameter);
  93. }
  94. /**
  95. * Gets a reflector for the access check callable.
  96. *
  97. * The access checker may be either a procedural function (in which case the
  98. * callable is the function name) or a method (in which case the callable is
  99. * an array of the object and method name).
  100. *
  101. * @param callable $callable
  102. * The callable (either a function or a method).
  103. *
  104. * @return \ReflectionFunctionAbstract
  105. * The ReflectionMethod or ReflectionFunction to introspect the callable.
  106. */
  107. protected function getReflector(callable $callable) {
  108. return is_array($callable) ? new \ReflectionMethod($callable[0], $callable[1]) : new \ReflectionFunction($callable);
  109. }
  110. /**
  111. * Handles unresolved arguments for getArgument().
  112. *
  113. * Subclasses that override this method may return a default value
  114. * instead of throwing an exception.
  115. *
  116. * @throws \RuntimeException
  117. * Thrown when there is a missing parameter.
  118. */
  119. protected function handleUnresolvedArgument(\ReflectionParameter $parameter) {
  120. $class = $parameter->getDeclaringClass();
  121. $function = $parameter->getDeclaringFunction();
  122. if ($class && !$function->isClosure()) {
  123. $function_name = $class->getName() . '::' . $function->getName();
  124. }
  125. else {
  126. $function_name = $function->getName();
  127. }
  128. throw new \RuntimeException(sprintf('Callable "%s" requires a value for the "$%s" argument.', $function_name, $parameter->getName()));
  129. }
  130. }