Особенности магического метода __call в PHP

Не всё работает так, как заявлено в документации

Те, кто хорошо знают 5, наверняка знакомы или хотя бы раз использовали такой мощный инструмент, как магические методы.

Один из методов, __call(), согласно документации используется при попытке вызова недоступного метода в контексте объекта.

Иными словами, в следующем фрагменте кода

[-]
View Code PHP
<?php
    class A {
        public function __call($method, $params)
        {
            print "Attempt to call {$method}\n";
        }
    }

    $a = new A();
    $a->someMethod();
?>

будет вызван магический метод A::__call("someMethod", array()), который напечатает

[-]
View Code Text
Attempt to call someMethod

С несуществующими методами всё ясно, но в документации упоминается слово «недоступные» (inaccessible).

А с недоступными методами, к сожалению, не всё так гладко. Рассмотрим пример:

[-]
View Code PHP
<?php
    class A {
        public function get()
        {
            return array(&$this, 'test');
        }

        public function __call($method, $params)
        {
            if (true == method_exists($this, $method)) {
                call_user_func_array(array(&$this, $method), $params);
            }
        }

        protected function test()
        {
            echo "It makes me money and that's all right\n";
        }
    }

    $a = new A();
    $callback = $a->get();
    call_user_func($callback);
?>

Метод A::test() является доступным в контексте класса A, но недоступным в контексте объекта $a. Следуя документации, ожидаемым результатом был бы вызов A::test() из A::__call(). Но в действительности всё не так, как на самом деле:

[-]
View Code Text
Warning: call_user_func(A::test): First argument is expected to be a valid callback in test-visibility.php on line 23

А метод __call() даже не вызывается.

В принципе, я не обратил бы на это внимание, если бы __get()/__set()/__isset()/__unset() работали точно так же. Но я знаю, что это не так:

[-]
View Code PHP
<?php
    class A
    {
        private $x;

        public function __get($name)
        {
            print $name . "\n";
            return null;
        }
    }

    $a = new A();
    $a->x;
?>

В этом случае, хотя $a->x и недоступен в контексте объекта $a, метод A::__get() будет вызван.

Я не знаю, считать ли политику (не)вызова __call() в PHP ошибкой или нет, ясно одно: подобное поведение не согласуется с политикой вызовов других магических методов.

Автор: ; опубликовано в: PHP; метки: PHP, магические методы, ООП
7
Июн
2009

RSS Комментарии к статье «Особенности магического метода __call в PHP» (3)  »

  1. Исправлено в PHP 5.3

  2. Ivan

    Проверил ваш пример с __call на версии пхп 5.2.12 все работает, ошибок не возникает.
    В каких версиях пхп у вас были ошибка
    Warning: call_user_func(A::test): First argument is expected to be a valid callback in test-visibility.php on line 23
    ?

Пожалуйста, не используйте эту форму для комментирования! Данная форма предназначена исключительно для ботов.

Оставить комментарий к записи «Особенности магического метода __call в PHP»

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Оставляя комментарий, вы выражаете своё согласие с Правилами комментирования.

Подписаться, не комментируя

गते गते पारगते पारसंगते बोधि स्वाहा