PHP
interface
名前

PHPのInterfaceで__invokeを使うべきか?

PHPのinterfaceを使う場合、__invokeメソッドを使うことができます。これを使うとメリット・デメリットは何だろう?と(どうでもよさそうな)ことを考えたので書いてみます。

例えば、

interface DoItInterface {

public function __invoke(): DoneInterface;
}

とします。このinterfaceを実装したDoItクラスがあるとすると、こんな風に使えます。

$do = new DoIt();

$done = $do();

ほんのちょっとコードが減ります。便利なような、そうでもないような… メインのメソッドが一つしかない場合に、__invokeを使うのはメリットはあるのでしょう?


TL;DR

いきなり結論を書くと、interfaceなどで__invokeは使わないほうがいい、と考えます。


メリット


メソッドの名前を考えなくていい

すでにinterface名で体を表しているのであれば、メソッド名を考えなくてすみます。これは大きい。


callableとして使える

Closureなどと混ぜて使える。


デメリット


クラスのプロパティに使うと残念な感じになる

こういうコードになります。

class DoAll {

/**
* @var DoItInterface
*/

private $doIt;
public function do() {
// $this->doIt(); // <- そのまま呼べない!
$this->doIt->__invoke(); // <- なんか格好悪い!!
}
}

PHP7.2でも、まだ対応できてません。パーサー変えるとき、対応するという話を聞いた記憶がかすかにありますが、まだみたいです。結局__invokeを使うことになるなら、普通にメソッド名をつけたくなります。


IDEで検索に引っかからない

IDEというか「PhpStorm 2019.1」での話ですが、DoItInterface::__invokeを「Find Usage」で検索した場合、検索対象になりませんでした。このコードです。

$do(); // <- 検索で出てこない!

これはPHP自体の問題ではないですが、開発から見ると結構大きな問題と思います。リファクタリングするときに修正箇所を見落としたり、自動で修正できないことになります。

この一点だけですが、__invokeを使わないことにしました。


メソッド名は?

では、メソッドの名前は何がいいのでしょう?


interface名と似た名前を使う

例えばSendMailInterface::send()のように、インターフェース名と同じような名前を使うことです。インターフェースだけを見ると妙な感じがしますが、クラス名が正しくつけられていれば、問題ないはず… 例えば、$notifyUser->send()であれば違和感はないです。


英語でよくある言葉を使う

こういうメソッドでよく使われてる動詞を使います。


  • handle

  • apply

  • invoke


  • executeあるいはexec

  • do


PHPで「do」は見たことないかも…

ともあれ、インターフェースに合わせて、うまく動詞を選ぶ必要はありそう。Filterならapplyとか。英語のセンスが必要ですね。