マジックメソッド使ってますか?
私はあまり好きではありませんでした。意図せず呼ばれてしまい、デバッグが困難になってしまうイメージが強かったからです。
しかし、Laravel(Lumen)を使用し、__call() と __callStatic()に触れ、興味が湧いてきました。
マジックメソッドの公式ドキュメント。
__call()
__call()の公式ドキュメント。
このメソッドは、未定義のインスタンスメソッドを実行した時に呼び出されます。
例を記します。
<?php
class Foo {
public function __call($name, $arguments) {
echo "Method name: " . $name;
}
}
$foo = new Foo();
$foo->bar();
?>__call()メソッドはインスタンス メソッドとして定義しなければなりません。
実行結果は以下となります。
Method name: bar
このように未定義のインスタンス メソッドを実行した際、__call()メソッドが実行されます。
__callStatic
__callStatic()の公式ドキュメント。
このメソッドは未定義のクラス メソッドを実行した際に実行されます。
例を記します。
<?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
/**
* 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が示すインスタンス メソッドの実行を試みます。
こうする事で、インスタンス メソッドをクラス メソッドのように実行しています。
/**
* 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
/**
* 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()メソッドを利用して、実体となるインスタンスのメソッドを実行しています。
この仕組みを利用して、実体となるクラスを継承し、メソッドを追加、そのクラスのインスタンスをファサードの実体とすることで、ファサード経由で追加したメソッドを呼び出せるようになります。
まとめ
正しく使えば、抽象化にずいぶん貢献できるように感じました。一つの実装案として頭の片隅に記憶しておくとよいでしょう。
安達棒とアンバサダーで色々釣りたいおじさん。
Macでプログラムを書いて暮らしています。 趣味はルアーフィッシング、ギター、アクアリウムとストリートファイター(格ゲー) 。
宮崎県在住。



