/** * Parse the given string as a function call. That is, it has the format 'functionName(arg1, arg2, ...)' where * the argument list is parsed by the parseArguments() method. * * @param string $call Function call to parse. * * @return array * @throws \InvalidArgumentException */ public static function parseFunctionCall($call) { $matches = array(); if (preg_match(self::REGEX_FUNCTION_CALL, $call, $matches) && sizeof($matches) > 1) { return array('name' => $matches[1], 'arguments' => sizeof($matches) > 2 ? self::parseArguments($matches[2]) : array()); } else { throw new \InvalidArgumentException(sprintf('Cannot parse function call [%s] not of the format name(arg1, arg2, ..)', TypeUtilities::describe($call))); } }
/** * Determine the argument list for the given callable method, depending on the given values. This allows for a * combination of fixed values, type hinting detection, and declared defaults. * * The fixed values are used first. These are required arguments that are assumed to exist in any implementation, * in the order given. This is effectively the starting point for the resulting argument list. * * When all the fixed values are exhausted, the remaining values are filled by first checking the type against each * of the given typed values. Any matching typed value ias appended to the resulting argument list. Typed values * are used once only. If there are multiple arguments of the same type, then two values should be supplied in the * typed values argument list. * * If there is no matching typed value, then the remaining values will be used. When the remaining values are * exhausted, then the parameter's default value will be used. * * If any parameter has no matching typed arguments, and there are no fixed values or remaining values left to * process, and does not have a default value, then a runtime exception is issued. * * @param callable|\ReflectionMethod|\Closure|object|string $callable Method or function reference, name or * reflection. * @param array|null $fixedValues Values that are required at the start of the argument list. These will be used * before any other values in determining the argument list. If there are more elements than the number of * arguments specified by the callable, then not all elements will be used. * @param array|null $typedValues Values that are detected by type hinting. Not all these values are necessarily * used, only those that are matched by type against the callable's argument list. * @param array|null $remainingValues Values that are used when all the fixed values are used, and there are no * matching typed values. Remaining values are used in preference to the parameter default values. * * @return array Array of arguments to pass to the method call, e.g. using call_user_fuc_array(). * * @throws \RuntimeException If there are insufficient matching values provided to pass for all required arguments. */ public static function getArguments($callable, array $fixedValues = null, array $typedValues = null, array $remainingValues = null) { $arguments = array(); foreach ($parameters = self::getParameters($callable) as $parameterIndex => $parameter) { if (sizeof($fixedValues) > 0) { $arguments[] = array_shift($fixedValues); } else { $matchedParameter = false; foreach ($typedValues ?: array() as $typedValueIndex => $typedValue) { if ($matchedParameter === false && $parameter->getClass() && $parameter->getClass()->isInstance($typedValue)) { $matchedParameter = $typedValueIndex; } } if ($matchedParameter !== false) { $match = array_splice($typedValues, $matchedParameter, 1); $arguments[] = $match[0]; } else { if (!empty($remainingValues)) { $arguments[] = array_shift($remainingValues); } elseif ($parameter->isDefaultValueAvailable()) { $arguments[] = $parameter->getDefaultValue(); } else { throw new \RuntimeException(sprintf('TypeUtilities cannot determine arguments for callable [%s] based on supplied values: %d unhandled parameters and %d arguments processed', TypeUtilities::describe($callable), sizeof($parameters) - $parameterIndex, sizeof($arguments))); } } } } return $arguments; }