fork(1) download
  1. <?php
  2.  
  3. class Invoker
  4. {
  5. public static function invoke(callable $callable, &$p1 = null, &$p2 = null)
  6. {
  7. if (is_string($callable) && strpos($callable, '::')) {
  8. // Strings are usually free function names, but they can also
  9. // specify a static method with ClassName::methodName --
  10. // if that's the case, convert to array form
  11. $callable = explode('::', $callable);
  12. }
  13.  
  14. // Get a ReflectionFunctionAbstract instance that will give us
  15. // information about the invocation target's parameters
  16. if (is_string($callable)) {
  17. // Now we know it refers to a free function
  18. $reflector = new ReflectionFunction($callable);
  19. }
  20. else if (is_array($callable)) {
  21. list ($class, $method) = $callable;
  22. $reflector = new ReflectionMethod($class, $method);
  23. }
  24. else {
  25. // must be an object -- either a closure or a functor
  26. $reflector = new ReflectionObject($callable);
  27. $reflector = $reflector->getMethod('__invoke');
  28. }
  29.  
  30. $forwardedArguments = [];
  31. $incomingArgumentCount = func_num_args() - 1;
  32. $paramIndex = 0;
  33.  
  34. foreach($reflector->getParameters() as $param) {
  35. if ($paramIndex >= $incomingArgumentCount) {
  36. if (!$param->isOptional()) {
  37. // invocation target requires parameter that was not passed,
  38. // perhaps we want to handle the error right now?
  39. }
  40.  
  41. break; // call target will less parameters than it can accept
  42. }
  43.  
  44. $forwardedArguments[] = &${'p'.(++$paramIndex)};
  45. }
  46.  
  47. return call_user_func_array($callable, $forwardedArguments);
  48. }
  49. }
  50.  
  51. // free function
  52. function test(&$x, $y) { $x = 'foo'; $y = 'bar'; }
  53.  
  54. // method
  55. class dummy {
  56. public static function test(&$x, $y) { $x = 'foo'; $y = 'bar'; }
  57. }
  58.  
  59. // functor
  60. class test {
  61. public function __invoke(&$x, $y) { $x = 'foo'; $y = 'bar'; }
  62. }
  63.  
  64. // closure
  65. $closure = function(&$x, $y) { $x = 'foo'; $y = 'bar'; };
  66.  
  67. $x = 'x'; $y = 'y';
  68. Invoker::invoke('test', $x, $y);
  69. echo "After invoking: \$x = $x, \$y = $y\n";
  70.  
  71. $x = 'x'; $y = 'y';
  72. Invoker::invoke(['dummy', 'test'], $x, $y);
  73. echo "After invoking: \$x = $x, \$y = $y\n";
  74.  
  75. $x = 'x'; $y = 'y';
  76. Invoker::invoke(new test(), $x, $y);
  77. echo "After invoking: \$x = $x, \$y = $y\n";
  78.  
  79. $x = 'x'; $y = 'y';
  80. Invoker::invoke($closure, $x, $y);
  81. echo "After invoking: \$x = $x, \$y = $y\n";
  82.  
Success #stdin #stdout 0.01s 20520KB
stdin
Standard input is empty
stdout
After invoking: $x = foo, $y = y
After invoking: $x = foo, $y = y
After invoking: $x = foo, $y = y
After invoking: $x = foo, $y = y