<?php
function call_user_func_fixed()
{
$args = func_get_args();
$callable = array_shift($args);
return call_user_func_array_fixed($callable, $args);
}
function call_user_func_array_fixed($callable, $args)
{
$isStaticMethod = false;
$expr = '/^([a-z_\x7f-\xff][\w\x7f-\xff]*)::([a-z_\x7f-\xff][\w\x7f-\xff]*)$/i';
// Extract the callable normalized to an array if it looks like a method call
if (is_string($callable) && preg_match($expr, $callable, $matches)) {
$func = array($matches[1], $matches[2]);
} else if (is_array($callable)
&& count($callable) === 2
&& isset($callable[0], $callable[1])
&& (is_string($callable[0]) || is_object($callable[0]))
&& is_string($callable[1])) {
$func = $callable;
}
// If we're not interested in it use the regular mechanism
if (!isset($func)) {
return call_user_func_array($func, $args);
}
$backtrace = debug_backtrace(); // passing args here is fraught with complications for backwards compat :-(
if ($backtrace[1]['function'] === 'call_user_func_fixed') {
$called = 'call_user_func_fixed';
$contextKey = 2;
} else {
$called = 'call_user_func_array_fixed';
$contextKey = 1;
}
try {
// Get a reference to the target static method if possible
switch (true) {
case $func[0] === 'self':
if (!isset($backtrace[$contextKey]['object'])) {
throw new Exception('Use of self:: in an invalid context');
}
$method = new ReflectionMethod($backtrace[$contextKey]['class'], $func[1]);
$invokeContext = $backtrace[$contextKey]['object'];
break;
case $func[0] === 'static':
if (!isset($backtrace[$contextKey]['object'])) {
throw new Exception('Use of static:: in an invalid context');
}
$method = new ReflectionMethod($backtrace[$contextKey]['object'], $func[1]);
$invokeContext = $backtrace[$contextKey]['object'];
break;
case $func[0] === 'parent':
if (!isset($backtrace[$contextKey]['object'])) {
throw new Exception('Use of static:: in an invalid context');
}
$method = new ReflectionMethod($backtrace[$contextKey]['object'], $func[1]);
if ($method->getDeclaringClass()->getName() === $backtrace[$contextKey]['class']) {
$method = $method->getPrototype();
}
$invokeContext = $backtrace[$contextKey]['object'];
break;
case is_object($func[0]):
$method = new ReflectionMethod($func[0], $func[1]);
$invokeContext = $func[0];
break;
default:
$method = new ReflectionMethod($func[0], $func[1]);
$invokeContext = !empty($backtrace[$contextKey]['object']) ? $backtrace[$contextKey]['object'] : null;
break;
}
// Invoke the method with the passed arguments and return the result
return $method->invokeArgs($invokeContext, $args);
} catch (Exception $e) {
trigger_error($called . '() expects parameter 1 to be a valid callback: ' . $e->getMessage(), E_USER_ERROR);
return null;
}
}
class Car {
public function run() {
return call_user_func_fixed(array('Toyota', 'getName')); // should call toyota
}
private static function getName() {
return 'Car';
}
}
class Toyota extends Car {
public static function getName() {
return 'Toyota';
}
}
$car = new Car();
echo $car->run(); //Car instead of Toyota
$toyota = new Toyota();
echo $toyota->run(); //Car instead of Toyota