マジックメソッド使ってますか?
私はあまり好きではありませんでした。意図せず呼ばれてしまい、デバッグが困難になってしまうイメージが強かったからです。
しかし、Laravel(Lumen)を使用し、__call() と __callStatic()に触れ、興味が湧いてきました。
マジックメソッドの公式ドキュメント。
__call()
__call()の公式ドキュメント。
このメソッドは、未定義のインスタンスメソッドを実行した時に呼び出されます。
例を記します。
1 2 3 4 5 6 7 8 9 10 11 12 | <?php class Foo { public function __call($name, $arguments) { echo "Method name: " . $name; } } $foo = new Foo(); $foo->bar(); ?> |
__call()メソッドはインスタンス メソッドとして定義しなければなりません。
実行結果は以下となります。
1 | Method name: bar |
このように未定義のインスタンス メソッドを実行した際、__call()メソッドが実行されます。
__callStatic
__callStatic()の公式ドキュメント。
このメソッドは未定義のクラス メソッドを実行した際に実行されます。
例を記します。
1 2 3 4 5 6 7 8 9 | <?php class Foo { public static function __callStatic() { echo "Method name: " . $name; } } Foo::bar(); ?> |
__callStatic()メソッドはクラス メソッドとして定義しなければなりません。
このように未定義のクラス メソッドを実行した際、callStatic()メソッドが実行されます。
Laravel(Lumen)での実装例
Illuminate\Database\Eloquent\Model
https://github.com/laravel/framework/blob/5.6/src/Illuminate/Database/Eloquent/Model.php#L1573-L1583
1 2 3 4 5 6 7 8 9 10 11 | /** * Handle dynamic static method calls into the method. * * @param string $method * @param array $parameters * @return mixed */ public static function __callStatic($method, $parameters) { return (new static)->$method(...$parameters); } |
自身のインスタンスを生成し、引数$methodが示すインスタンス メソッドの実行を試みます。
こうする事で、インスタンス メソッドをクラス メソッドのように実行しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /** * Handle dynamic method calls into the model. * * @param string $method * @param array $parameters * @return mixed */ public function __call($method, $parameters) { if (in_array($method, ['increment', 'decrement'])) { return $this->$method(...$parameters); } return $this->newQuery()->$method(...$parameters); } |
__call()では、まずメソッド名が’increment’、または’decrement’ と一致するか判定します。
一致する場合、increment、またはdecrementメソッドが実行されます。
一致しなかった場合、newQuery()メソッドを実行し、Illuminate\Database\Eloquent\Builderクラスのインスタンスを生成します。そのインスタンスの$methodが示すメソッドを実行します。
これらの実装により、Illuminate\Database\Eloquent\Modelクラスのクラスメソッド としてwhere()、insert()を実行した場合、Illuminate\Database\Eloquent\Builderクラスのインスタンス メソッドが実行されます。
Illuminate\Support\Facades\Facade
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /** * Handle dynamic, static calls to the object. * * @param string $method * @param array $args * @return mixed * * @throws \RuntimeException */ public static function __callStatic($method, $args) { $instance = static::getFacadeRoot(); if (! $instance) { throw new RuntimeException('A facade root has not been set.'); } return $instance->$method(...$args); } |
Facadeは__callStatic()メソッドを利用して、実体となるインスタンスのメソッドを実行しています。
この仕組みを利用して、実体となるクラスを継承し、メソッドを追加、そのクラスのインスタンスをファサードの実体とすることで、ファサード経由で追加したメソッドを呼び出せるようになります。
まとめ
正しく使えば、抽象化にずいぶん貢献できるように感じました。一つの実装案として頭の片隅に記憶しておくとよいでしょう。
宮崎県在住。
プログラムを書いて暮らしています。趣味はルアーフィッシング、ギター。