How to get the number of parameters of a run-time determined callable?
callback, php, reflection
Solution
TL;DR I don't think so. You need to check for all callable types if you want a generic solution.
The following function can be used to get a `ReflectionFunctionAbstract` instance for any generic callable in PHP:
function reflectionOf(callable $callable): ReflectionFunctionAbstract
{
if ($callable instanceof Closure) {
return new ReflectionFunction($callable);
}
if (is_string($callable)) {
$pcs = explode('::', $callable);
return count($pcs) > 1 ? new ReflectionMethod($pcs[0], $pcs[1]) : new ReflectionFunction($callable);
}
if (!is_array($callable)) {
$callable = [$callable, '__invoke'];
}
return new ReflectionMethod($callable[0], $callable[1]);
}
Then it is possible to get the number of parameters as follows:
reflectionOf($func)->getNumberOfParameters();
Hope this helps to someone. This answer might be late to the party, but none of the other solutions provide a full coverage for generic callables.
Problem
NOTE: By virtue of writing this quesiton, I've already figured out that I was being overly enthousiastic about using a new language feature. The far cleaner solution was using a Strategy Pattern instead... still, I'm curious if there's a proper way to go about this problem. TL;DR: Can you reflect on a generic Callable in PHP without resorting to manually typechecking all kinds of callable? In PHP 5.4 we've got a new typehint: callable. This seems like a lot of fun. I thought I'd make use of this through the following: ``` <?php public function setCredentialTreatment(callable $credentialTreatment) { // Verify $credentialTreatment can be used (ie: accepts 2 params) ... magic here ... } ?> ``` So far my line of thought has been to do a series of type-checks on the callable, and inferring from that which Reflection* class to use: ``` <?php if(is_array($callable)) { $reflector = new ReflectionMethod($callable[0], $callable[1]); } elseif(is_string($callable)) { $reflector = new ReflectionFunction($callable); } elseif(is_a($callable, 'Closure') || is_callable($callable, '__invoke')) { $objReflector = new ReflectionObject($callable); $reflector = $objReflector->getMethod('__invoke'); } // Array of ReflectionParameters. Yay! $parameters = $reflector->getParameters(); // Inspect parameters. Throw invalidArgumentException if not valid. ?> ``` Now, to me, this feels overly complicated. Am I missing some kind of shortcut way to achieving what I'm trying to do here? Any insight would be welcomed :)