Я havinh проблема на PHP 5.3. Мне нужно вызвать метод с помощью __callStatic
, но если я использую его в объекте __call
, PHP вызовет __call
.
Выше примера реальной жизни:
<?php class A { function __call($method, $args){ // Note: $this is defined! echo "Ops! Don't works. {$this->c}"; } static function __callStatic($method, $args){ echo 'Fine!'; } } class B extends A { public $c = 'Real Ops!'; function useCallStatic(){ static::foo(); // === A::foo(); // === B::foo(); } } $foo = new B(); $foo->useCallStatic(); // This works: // B::foo(); ?>
Печать : Ops! Не работает. Реальные игры!
Обратите внимание, что я вызываю useCallStatic
из new B()
. Если я вызываю, то работает отлично, как B::foo()
.
Что я могу с этим поделать?
Подумав об этом, это не ошибка. Но это определенно неинтуитивно.
<?php class A { public function foo() { static::bar(); } public function bar() { echo "bar()\n"; } } $a = new A(); $a->foo();
Это действительно, и вызывает bar (). Это не означает, что столбец статический. Это означает поиск текущего имени класса и bar
функций вызова, если он существует, независимо от того, является ли это статической функцией.
Чтобы уточнить немного больше: вы думаете, что parent::bar()
означает вызов статической функции, называемой bar()
? Нет, это означает вызов любой функции с именем bar()
.
Рассматривать:
<?php class A { function __call($name, $args) { echo "__call()\n"; } static function __callStatic($name, $ags) { echo "__callStatic()\n"; } function regularMethod() { echo "regularMethod()\n"; } static function staticMethod() { echo "staticMethod()\n"; } } class B extends A { function foo() { parent::nonExistant(); static::nonExistant(); parent::regularMethod(); parent::staticMethod(); } } $b = new B(); $b->foo();
Метод parent::nonExistant()
вызывает A::__call()
, как и static::nonExistant()
. Вызов A::__callStatic()
для любого вызова будет одинаково справедливым! PHP не знает, какой из них вы хотите назвать. Однако по дизайну PHP дает __call
приоритет при вызове из такого контекста.
parent::regularMethod()
вызывает regularMethod()
функцию regularMethod()
. (Опять же, оператор ::
не означает «вызов этой статической функции».) Аналогично parent::staticMethod()
вызывает A::staticMethod()
как и следовало ожидать.
Чтобы решить вашу проблему:
Вы можете вручную вызвать self::__callStatic('foo')
в унаследованном классе.
Или в методе __call
фильтра класса A против известного списка и вызовите self::__callStatic
.
function __call($f, $a) { if ($f == 'foo') return self::__callStatic($f, $a); else { } }
Это уродливо, но, по крайней мере, люди, которые расширяют класс, не должны делать ничего особенного.
Вероятно, это связано с стандартной записью вызова родительских методов, независимо от состояния. Вы можете проверить, установлено ли $this
ключевое слово $this
в методе __call
а если нет, сделать вызов __callStatic
?
EDIT: Это не ошибка сама по себе, статический вызов выполняется из контекста объекта. Взгляните на эту тему с ошибкой на php .
Во-первых, убедитесь, что вы используете PHP 5.3.0 или новее, так как это было реализовано __callStatic. Кажется, все работает так, как ожидалось, см. Этот пример:
<?php class A { public function __call($method, $args) { echo 'A::__call, ' . var_export(array($method, $args), true), PHP_EOL; } /** As of PHP 5.3.0 */ public static function __callStatic($method, $args) { echo 'A::__callStatic, ' . var_export(array($method, $args), true), PHP_EOL; } } $a = new A; $a->foo('abc'); A::bar('123'); class B extends A { function invokeStatic($args) { echo 'B::invokeStatic', PHP_EOL; self::someStatic($args); } } $b = new B; $b->invokeStatic('456'); ?>
Выход должен быть (или, по крайней мере, для меня):
A::__call, array ( 0 => 'foo', 1 => array ( 0 => 'abc', ), ) A::__callStatic, array ( 0 => 'bar', 1 => array ( 0 => '123', ), ) B::invokeStatic A::__callStatic, array ( 0 => 'someStatic', 1 => array ( 0 => '456', ), )
Когда я запускаю ваш пример выше, я просто получаю «Отлично!». как единственный выход, что я и ожидал.
Какая версия PHP вы используете? Вы можете проверить это:
$ php -r 'echo phpversion(), PHP_EOL;' 5.3.3-7+squeeze1
Наследование статических методов в PHP немного странно. Я подозреваю, что вам нужно переопределить __callStatic
в B
Хорошие новости
Я делаю BIG work arround, но он работает очень хорошо. Это код:
class NoContext { public static function call($callback, $args = null){ return call_user_func_array($callback, $args ?: array()); } }
Вам нужно вызвать NoContext::call
(статически), как вы вызываете call_user_func
или подобное. Он должен удалить $this
контекст. Я все еще думаю, что это ошибка PHP, но …
Чтобы решить мою проблему, я делаю:
class B extends A { function useCallStatic(){ NoContext::call('A::foo'); } }
И он будет печатать: Fine!
,
Спасибо всем, кто мне помогает. Я очень многому научился с вашими ответами, и я жду, что мои советы будут полезны вам когда-нибудь.