Просматривая код многих PHP приложений, я замечаю, что функция method_exists() часто используется неуместно, и думаю, что стоит написать об этом пару слов.
Вот типичный пример того, о чем пойдет речь:
if (method_exists($object, 'SomeMethod')) { $object->SomeMethod($this, TRUE); }
Цель этого кода очень легко понять (хоть я и не советую делать так): мы проверяем, имеет ли объект “$object” метод “SomeMethod”, и если имеет, вызываем его с нужными аргументами.
Да, но...
Этот код будет, вероятно, работать очень хорошо в своей области видимости, но что если методы объекта не видно (т.е. это частные или защищенные методы)? PHP функция method_exists() ведет себя соответственно названию: проверяет, имеет ли класс или объект метод с таким именем, и возвращает True, если это так, или False, в противоположном случае; видимость метода не учитывается. Итак, если вы проверите при помощи method_exists() существование частного или защищенного метода (не доступные в текущей области видимости), при вызове его вы получите ошибку: “Fatal error: Call to private method…”, которая сразу завершит работу приложения.
Применяйте инструменты по их предназначению
Истинное назначение предыдущего кода, на самом деле – узнать, может ли приложение вызвать метод у объекта.
По этому (среди других причин) is_callable() одна из встроенных в PHP функций.
Как это работает?
Первым параметром в is_callable() передается переменная псевдотипа callback, в нашем случае - массив из двух элементов: объект или строка с названием класса и строка с названием метода. Если в текущей области видимости метод может быть вызван - is_callable() вернет True, если же нет - False.
if (is_callable(array($object, 'SomeMethod'))) { $object->SomeMethod($this, TRUE); }
Этот код показывает различия между method_exists() и is_callable():
class Foo { public function PublicMethod() {} private function PrivateMethod() {} public static function PublicStaticMethod() {} private static function PrivateStaticMethod() {} } $foo = new Foo(); $callbacks = array( array($foo, 'PublicMethod'), array($foo, 'PrivateMethod'), array($foo, 'PublicStaticMethod'), array($foo, 'PrivateStaticMethod'), array('Foo', 'PublicMethod'), array('Foo', 'PrivateMethod'), array('Foo', 'PublicStaticMethod'), array('Foo', 'PrivateStaticMethod'), ); foreach ($callbacks as $callback) { var_dump($callback); var_dump(method_exists($callback[0], $callback[1])); // 0: объект/имя класса, 1: имя метода var_dump(is_callable($callback)); echo str_repeat('-', 40), "\n"; }
Запустите этот код, и вы увидите, что каждый тест возвращает True для method_exists(), даже для защищенных методов, в отличие от is_callable().
Дополнительная информация
is_callable() имеет и другие применения, например, проверка синтаксиса без проверки реального существования функции или метода.
Так же как и method_exists(), is_callable() может инициализировать процесс автоматической загрузки класса, если он еще не загружен.
Если же объект имеет "магический метод" __call(), то is_callable() вернет True, в отличае от method_exists(), который вернет False. В php 5.3.0 есть метод __callStatic(), и если описать его для объекта, is_callable() тоже будет работать верно.
Все остальное можно найти в документации PHP.


А интерфейс Reflection не лучше ли использовать для классов и их методов?
web-junior, да, возможно, так будет лучше
@web-junior: не всегда нужна вся та мощь, которую предоставляет рефлексия;)
Полностью согласен с web-junior, использование для классов и методов обеспечить более функциональную работу.
@hazzik: ИМХО, мощь рефлексии не нужна только в одном случае - в функциях (и вообще в процедурно-ориентированном подходе к программированию;)). Здесь можно обойтись функциями method_exists() и is_callable().
А для классов рефлексия как раз то, что нужно.
@web-junior
Рефлексия нужна только в исключительных случаях. Активное ее использование в приложении (не во фреймворке) говорит о плохом дизайне.
@hazzik
Может быть объясните, как рефлексия портит дизайн приложения?
Я тоже согласен с web-junior, рефлекцию надо юзать, почему кто-то против внутренностей самого ядра? Я также юзаю эксепшены, dir, итераторы и т.д.
Даже сам Zend юзает ReflectionClass, чё плохой тон? ФВ Кохана в классе request юзает ReflectionClass, FirePHP - тоже юзает его и тд. У них тоже плохой дзайн????
Однозначно, если дело дошло до использования Reflection в бизнес-логике - у вас проблемы в дизайне проекта или вы не знаете назначения этого инструмента (это опять же проблема и опять же у вас).
Zend здесь неудачный пример. Во первых - с какими целями они это используют? Во вторых Zend всего лишь имя и там работают тоже люди и они тоже способны загнуть проект. (За одно лягну ZendFw - этот код далеко не пример для подражания, кто разобрался в их в коде - согласится).
Вернёмся к делу.
У вас все переменные-члены/методы в классах public? Нет? А чего ж так?
Использование Reflection в бизнес-логике отменяет понятия protected и private поскольку в Reflection легально извне устанавливать уровень доступа. Просто добавь ReflectionMethod::setAccessible().
See: http://ru2.php.net/manual/en/reflectionmethod.setaccessible.php
А так же. Reflection легальный инструмент со своей областью назначения, а не "внутренность самого ядра". К внутренностям самого ядра у вас точно нет доступа.
Более того, ограничения есть даже на низкоуровневые абстракции.
К примеру: Traversable interface.
Вы не можете его имплементировать напрямую, хотя
instanceof Traversable
доступно.
See: http://php.net/manual/en/class.traversable.php