
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
Висновок
Використовуйте дані методи відштовхуючись від своїх цілей, конкретної ситуації і контексту. Думайте, коли краще використовувати method_exists
, а коли - is_callable
. Однак з останнім слід бути акуратним і враховувати, що в якомусь із предків можуть бути визначені магічні методи.