PHP неправильно обрабатывает мой статический вызов

Я 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! ,

Спасибо всем, кто мне помогает. Я очень многому научился с вашими ответами, и я жду, что мои советы будут полезны вам когда-нибудь.