可変関数を初めてちゃんと使ったので、記録。
概要
PHPには「可変関数」という概念がある。
https://www.php.net/manual/ja/functions.variable-functions.php
PHP は可変関数(variable functions)の概念をサポートします。 これにより、変数名の後に括弧が付いている場合、その値が何であろうと PHPは、同名の関数を探し実行を試みます。 この機能は、コールバック、関数テーブル等を実装するために使用可能です。
とのこと。
共通部品に関数名の文字列を渡し、共通部品側では外部から受け取った変数名で関数を実行!なんてことができる。
同じクラス内で使う場合
関数名の文字列を変数として受け取り、その変数を用いて実行することができる。
class MyClass {
public function execute($functionName) {
$this->$functionName();
}
private function method1() {
$this->execute('method2');
}
private function method2() {
// 処理
}
}
異なるクラス同士で使う場合
外部クラスに存在する関数を実行する場合は、関数名単体の文字列指定では使えない。
完全修飾名(例:MyClass::myMethod
)で指定するか、クロージャーを使う必要がある。
完全修飾名で関数名を受け渡しする
public static メソッド名の文字列を受け取り、call_user_func(メソッド名の文字列, 引数) で実行する。
呼び出される側(関数を定義しているクラス)
public function handle($data) {
$xxxClass = new xxxClass();
$xxxClass->execute(
$data,
'App\Http\Controllers\XXXController::myMethod' // public staticメソッド
);
}
呼び出し側
class xxxClass
{
public function execute($data, $method) {
$result = call_user_func($method, $data);
// 処理
}
}
クロージャーを使う
関数をクロージャーとして渡すこともできる。
呼び出される側(関数を定義しているクラス)
public function handle($data) {
$xxxClass = new xxxClass();
$xxxClass->execute(
$data,
function ($params) { // クロージャーで formatData を渡す
return $this->formatData($params);
}
);
}
private function formatData($params)
{
// コントローラー独自の処理
}
呼び出し側
class xxxClass
{
public function execute($data, $method) {
$result = $method($data);
// 処理
}
}
なお、無名関数で渡すこともできる。
$xxxClass->execute(
$data,
function ($params) {
// 処理
}
);
使ってみた
事例:取得したUserデータを1,000件ずつchunkしてCSVに書き込みたい。
書き込み処理は他の機能でも使えるようにUseCaseに切り出したい。
行フォーマットは該当のコントローラでしか利用しないので、privateメソッドにしておきたい。
Controller
public function handle() {
// ファイルパスなどを準備
$useCase = new xxxUseCase();
$useCase->execute(
User::query(),
$filePath,
function ($model) {
return $this->rowFormatter($model);
}
);
// 作成したCSVをダウンロード
}
private function rowFormatter(User $user): array
{
return [
$user->id,
$user->name,
$user->email,
// 他の加工処理など
];
}
UseCase
function execute($query, $filePath, $rowFormatter) {
$fp = fopen($filePath, 'a');
$query->chunk(1000, function ($models) use ($fp, $rowFormatter) {
foreach ($models as $model) {
$row = $rowFormatter($model); // 関数を呼び出す
$this->writeRow($fp, $row);
}
});
fclose($fp);
}