Jest to błąd, który pojawia się wahały się i znikają na długi czas (patrz testy @ deceze w komentarzach do pytania). Możliwe jest "naprawienie" tego problemu - to jest zapewnienie spójnego zachowania w różnych wersjach PHP - używanie reflection:
Działa w PHP 5.3.2 i późniejszych ze względu na zależność od ReflectionMethod::setAccessible()
, aby wywołać metody prywatne/chronione. Dodam dalsze wyjaśnienie tego kodu, co może i czego nie może zrobić i jak działa bardzo krótko.
Niestety, nie jest możliwe przetestowanie tego bezpośrednio na 3v4l.org, ponieważ kod jest zbyt duży, jednak jest to pierwsza prawdziwa przypadek użycia dla kodu PHP minifying - działa to na 3v4l, jeśli to zrobisz, więc nie krępuj się bawić się i zobaczyć, czy możesz go złamać. Jedyny problem, o którym wiem, to że obecnie nie rozumie parent
. Ogranicza się również brakiem wsparcia w zamknięciu przed 5.4, ale nie można nic na to poradzić.
<?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':
case $func[0] === 'static':
if (!isset($backtrace[$contextKey]['object'])) {
throw new Exception('Use of self:: in an invalid context');
}
$contextClass = new ReflectionClass($backtrace[$contextKey][$func[0] === 'self' ? 'class' : 'object']);
$contextClassName = $contextClass->getName();
$method = $contextClass->getMethod($func[1]);
$ownerClassName = $method->getDeclaringClass()->getName();
if (!$method->isStatic()) {
throw new Exception('Attempting to call instance method in a static context');
}
$invokeContext = null;
if ($method->isPrivate()) {
if ($ownerClassName !== $contextClassName
|| !method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call private method in an invalid context');
}
$method->setAccessible(true);
} else if ($method->isProtected()) {
if (!method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call protected method in an invalid context');
}
while ($contextClass->getName() !== $ownerClassName) {
$contextClass = $contextClass->getParentClass();
}
if ($contextClass->getName() !== $ownerClassName) {
throw new Exception('Attempting to call protected method in an invalid context');
}
$method->setAccessible(true);
}
break;
case is_object($func[0]):
$contextClass = new ReflectionClass($func[0]);
$contextClassName = $contextClass->getName();
$method = $contextClass->getMethod($func[1]);
$ownerClassName = $method->getDeclaringClass()->getName();
if ($method->isStatic()) {
$invokeContext = null;
if ($method->isPrivate()) {
if ($ownerClassName !== $contextClassName || !method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call private method in an invalid context');
}
$method->setAccessible(true);
} else if ($method->isProtected()) {
if (!method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call protected method in an invalid context');
}
while ($contextClass->getName() !== $ownerClassName) {
$contextClass = $contextClass->getParentClass();
}
if ($contextClass->getName() !== $ownerClassName) {
throw new Exception('Attempting to call protected method in an invalid context');
}
$method->setAccessible(true);
}
} else {
$invokeContext = $func[0];
}
break;
default:
$contextClass = new ReflectionClass($backtrace[$contextKey]['object']);
$method = new ReflectionMethod($func[0], $func[1]);
$ownerClassName = $method->getDeclaringClass()->getName();
if (!$method->isStatic()) {
throw new Exception('Attempting to call instance method in a static context');
}
$invokeContext = null;
if ($method->isPrivate()) {
if (empty($backtrace[$contextKey]['object'])
|| $func[0] !== $contextClass->getName()
|| !method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call private method in an invalid context');
}
$method->setAccessible(true);
} else if ($method->isProtected()) {
$contextClass = new ReflectionClass($backtrace[$contextKey]['object']);
if (empty($backtrace[$contextKey]['object']) || !method_exists($method, 'setAccessible')) {
throw new Exception('Attempting to call protected method outside a class context');
}
while ($contextClass->getName() !== $ownerClassName) {
$contextClass = $contextClass->getParentClass();
}
if ($contextClass->getName() !== $ownerClassName) {
throw new Exception('Attempting to call protected method in an invalid context');
}
$method->setAccessible(true);
}
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;
}
}
Dlaczego funkcja getName() jest prywatna w samochodzie i publicznym w Toyota? – 1615903
Którą wersję php używasz? Ponieważ w PHP 5.4 echo "Toyota" dwa razy zamiast "Samochód". Jeśli dobrze zrozumiałem w twoim przypadku, dzieje się to inaczej. – Leri
Wydaje się, że różni się znacznie w różnych wersjach PHP: http://3v4l.org/ekaEs - Błędy w PHP! Świat naprawdę musi się skończyć. – deceze