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

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

Один из методов, __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 ошибкой или нет, ясно одно: подобное поведение не согласуется с политикой вызовов других магических методов.

Добавить в закладки

Связанные записи

7
Июнь
2009

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

  1. Vladimir says:

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

Подписаться на RSS-ленту комментариев к статье «Особенности магического метода __call в PHP» Trackback URL: http://blog.sjinks.org.ua/php/573-about-call-magic-method/trackback/

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

Вы можете использовать данные тэги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

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

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