
method_exists или is_callable?
28.01.2019 21:51 | PHP
Речь пойдёт о нюансах, которые по неким причинам явно не упомянуты в официальной документации. Если быть точнее, в документации - нет, но в комментариях к докам - да. Как бы то ни было, во избежание логических ошибок в коде, знать описанное ниже не помешает.
method_exists
Создадим простейший класс:
class Test
{
public function foo()
{
return 'You called: ' . __METHOD__;
}
}
Попробуем использовать функцию method_exists
:
$test = new Test;
$method = 'foo';
if (method_exists($test, $method)) {
echo $test->$method();
}
Вроде всё хорошо, на выходе получаем:
You called: Test::foo
Теперь поменяем public
на private
или protected
. Сюрприз - fatal error! Т.е., неплохо было бы добавить пару строк в доках:
method_exists
- проверяет, существует ли метод в данном классе, но не учитывает область видимость. Использованиеmethod_exists
вне класса может привести к фатальным ошибкам.
К слову, на php.net приведены примеры использования именно вне класса. С другой стороны, применение данной функции в классе вполне приемлемо. Например, так:
class Test
{
public function __get($property)
{
$method = "get{$property}";
if (method_exists($this, $method)) {
return $this->$method();
}
}
public function getFoo()
{
return 'foo';
}
}
$test = new Test;
echo $test->foo;
И для полноты картины:
- что со статическим методами? То же самое - если метод определён в классе,
method_exists()
будет возвращатьtrue
- что, если метод определён в родителе? Пусть и в родителе, но определён, а значит получим
true
- что, если метода как такового нет, но определён магический
__call()
? А вот тут всё равно: метод существует? Нет. Значитfalse
.
is_callable
Тут с областями видимости всё в порядке - если метод определён как private
или protected
, и Вы пытаетесь его вызвать вне класса - получите отказ (false
). А теперь давайте представим ситуацию, что у нас есть некая иерархия классов:
class A
{
public function __call($name, $args)
{
return 'You can call me!';
}
}
class B extends A {}
class C extends B {}
$test = new C;
var_dump(is_callable([$test, 'foo']));
Какой результат получим? true
! Следовательно, можно дополнить существующее определение:
is_callable
проверяет, может ли значение переменной быть вызвано в качестве функции в текущем контексте. Следует учитывать, что если в классе или его родителе определён метод__call
, данная функция всегда будет возвращатьtrue
.
И опять-таки:
- проверка статических методов ведёт себя таким же образом
- если метод можно вызвать из родителя, значит его можно вызвать и из дочернего класса
- если определены
__call
(__callStatic
для статическихх методов) - всегда будем получатьtrue
Вывод
Используйте данные методы отталкиваясь от cвоих целей конкретной ситуации и контекста. Думайте, когда предпочтительней использовать method_exists
, а когда - is_callable
. Однако с последним следует быть аккуратным и учитывать, что в каком-то из предков могут быть определены магические методы.