Reputation: 3565
Lets say I have an arbitrary function taking some parameters and returning say an array (the return and what the method does doesn't matter)
$callable = fn (MyClass $myClass, string $id, bool $rec) => []
I would like to build a new function from this closure so that this new function takes the exact same parameters in a way that the Reflection of this function will have the same parameters as the original
What I mean:
$clonedCallable = someMagic($callable);
assertEquals( // Equal but obviously not the same ref
(new ReflectionFunction($callable))->getParameters(),
(new ReflectionFunction($clonedCallable))->getParameters()
);
$callable(); // []
$clonedCallable(); // Wrapper([])
I'm basically trying to wrap a closure, in a way that would not lose reflection information
fn (...$args) => $callable(...$args); // This would not work
fn (MyClass $myClass, string $id, bool $rec) => $callable($myClass, $id, $rec); // I need to generate this
Is there a way in PHP to build this kind of method without using some hackjob eval (which is the only idea I have right now) as Reflection doesn't have some kind of magic setParameters()
?
Upvotes: 0
Views: 55
Reputation: 592
<?php
class Foo { }
$add = function (int $a, Foo $foo): array {
return [$a * 2];
};
function wrapAction(string|callable|array $action, callable $wrapper = null): callable
{
$rc = is_array($action) ?
new ReflectionMethod($action[0], $action[1]) :
new ReflectionFunction($action);
$args = [];
$action = $rc->getClosure();
if (!$wrapper) {
$wrapper = fn ($res) => $res;
}
foreach ($rc->getParameters() as $arg) {
$args[] = $arg->getType() . ' $' . $arg->getName();
}
$args = join(',', $args);
return eval("return fn ($args) => \$wrapper(\$action(...func_get_args()));"); // I know, but I can't think of any way other than this ;)
}
// test
$clone = wrapAction($add, fn ($res) => $res + 2);
$orgRef = new ReflectionFunction($add);
$cloneRef = new ReflectionFunction($clone);
assert($orgRef->getParameters() == $cloneRef->getParameters(), "Not the same");
$foo = new Foo();
print_r($add(5, $foo)); // 10
print_r($clone(5, $foo)); // 12
Upvotes: 1