👏

一日一処: 初心者が陥りがちなPHPの頭がおかしいメソッドを参照する方法

2024/02/22に公開

PHP

いまでは、サーバーサイドの開発で人気を誇るプログラム言語だ。初心者にも優しい上、関連する記事が非常に多いので、新規学習者にとってはストレスのない言語かもしれない。
ただ、それは、PHPの仕様にまだ歩み寄っていないからだと思う。
他言語でもあるが、traitという機能があまり好きではない。使い方次第で協力ではあるものの、継承関係にない機能を外部から我が物顔でいるクラスはあまり好きにはなれない。

目を疑うようなメソッド参照

PHPでは、クラスの記述が可能だ。たとえば、以下のようなコードだ。

class Human {
  function greet() {
    echo 'Hello';
  }
}

$bob = new Human();
$bob->greet();

ただ、ボブがこちらを見て、挨拶してくれているだけの簡単なクラスだ。多くのPHPのフレームワークでは、この様にクラスを組み合わせて、システムを実現しているが、その中で、マジックメソッドを用いている場合もある。

class Human {
  function greet() {
    echo "Hello\n";
  }
  function __call($name, $args) {
    $name();
  }
}

function apologize() {
  echo "I'm sorry.\n";
}

$bob = new Human();
$bob->greet();     // Hello
$bob->apologize(); // I'm sorry.

本当に、意味不明すぎて草生える。という具合だが、マジックメソッドは文字通り、魔法のようなメソッド定義で、上記のように、クラスそのものに定義のないメソッドについては、__callメソッドに到達し、処理を行う。ここも実はあまり好みではないが、$name();というふうに関数を実行している。これは、変数$nameが展開される。この変数の中には、呼び出し元であるapologizeが入っているので、真ん中で定義しているapologize関数が呼び出されていることとなる。愉快極まりない。

更に混乱するメソッド呼び出し

class Human {
  function greet() {
    echo "Hello\n";
  }
  function __call($name, $args) {
    $name();
  }
}

function apologize() {
  echo "I'm sorry.\n";
}

$method = 'apologize';
$bob = new Human();
$bob->greet();   // Hello
$bob->$method(); // I'm sorry.

先程とほとんど変わらないが、最後のメソッド呼び出しを$bob->$method();に変更した。だが、結果は変わらない。これは、__callの内部で行っている関数の呼び出しと同様のことをメソッド呼び出して行っている。つまり、$methodが展開され、代入されている文字列apologizeが実際のメソッド名として扱われ、__call$name引数へ渡される。
シンプルに書けば問題ないと思うが、なぜか、フレームワークなどでこのような記述を偶に見るが、正直、分かりづらいと思う。ただ、このような機能があるのは、面白いし、利用の価値はあると思うが、瞬間的に意味が分かりづらいため、個人的には、あまり使いたくない手法だ。

Discussion