__call & __callStatic
CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 47/91。
__get() と __set() がプロパティへのアクセスをインターセプトするのと同様に、__call() と __callStatic() メソッドはメソッド呼び出しをインターセプトします。これらは、オブジェクトやクラスに存在しない、あるいはアクセスできないメソッドを呼び出したときにトリガーされます。
__call() メソッドは、インスタンスメソッドの呼び出しを処理します。このメソッドは、メソッド名と引数の配列を受け取ります:
<?php
class Messenger {
public function __call(string $name, array $arguments): string {
return "Called '$name' with: " . implode(", ", $arguments);
}
}
$msg = new Messenger();
echo $msg->sendEmail("Hello", "World");
出力:
Called 'sendEmail' with: Hello, World静的メソッドの呼び出しには、__callStatic() を使用します。static として宣言する必要があることに注意してください:
<?php
class QueryBuilder {
public static function __callStatic(string $name, array $arguments): string {
if (str_starts_with($name, "findBy")) {
$field = substr($name, 6);
return "Finding by $field: " . $arguments[0];
}
return "Unknown method: $name";
}
}
echo QueryBuilder::findByEmail("test@example.com");
出力:
Finding by Email: test@example.comこれらのマジックメソッドは、流れるようなAPIの作成、メソッド転送の実装、あるいはメソッド名がデータベース操作に変換されるような動的クエリビルダーの構築に強力な威力を発揮します。これらを使用することで、オブジェクトは、明示的に定義されていないものも含め、あらゆるメソッド呼び出しに対してインテリジェントに応答できるようになります。
チャレンジ
簡単マジックメソッドを使用してメソッド呼び出しを柔軟に処理する、動的なAPIクライアントを構築しましょう。各メソッドを明示的に定義することなく、インスタンスメソッドと静的メソッドの両方の呼び出しをインターセプトし、それらを意味のあるAPIのようなレスポンスに変換するクラスを作成します。
コードは2つのファイルに分けて構成します:
ApiClient.php— マジックメソッドを使用してAPIインタラクションをシミュレートするApiClientクラスを作成します。クラスには、コンストラクタを通じて設定されるプライベートな$baseUrlプロパティを持たせる必要があります。次の2つのマジックメソッドを実装してください:__call()— インスタンスメソッドの呼び出しを処理します。getで始まるメソッド(getUsersやgetPostsなど)が呼び出された場合、リソース名("get" の後の部分)を抽出し、"GET [baseUrl]/[resource]: [arguments joined by comma]"を返します。リソース名は小文字に変換する必要があります。メソッドが "get" で始まらない場合は、"Unknown action: [methodName]"を返します。__callStatic()— 静的メソッドの呼び出しを処理します。createで始まるメソッド(createUserなど)が呼び出された場合、リソース名を抽出し、"POST /[resource]: [arguments joined by comma]"を返します。リソース名は小文字にする必要があります。deleteで始まるメソッドの場合は、"DELETE /[resource]: [first argument]"を返します。それ以外のメソッドの場合は、"Static action not supported: [methodName]"を返します。
main.php— ApiClientファイルをインクルードします。ベースURL、リソース識別子、および値の3つの入力を受け取ります。ベースURLを使用してApiClientインスタンスを作成します。その後、次の4行を出力してください:- リソース識別子をメソッド名のサフィックスとして使用し(例:リソースが "Users" の場合は
getUsersを呼び出す)、値を引数として動的なgetメソッドを呼び出します。 getProductsを2つの引数"electronics"と"active"で呼び出します。- 静的メソッド
createOrderを値を引数として呼び出します。 - 静的メソッド
deleteItemをリソース識別子を引数として呼び出します。
- リソース識別子をメソッド名のサフィックスとして使用し(例:リソースが "Users" の場合は
マジックメソッドを使用すると、APIクライアントはあらゆるメソッド呼び出しに動的に応答できるようになります。getUsers()、getPosts()、getProducts() など、数十ものメソッドを個別に定義する代わりに、単一の __call() メソッドがメソッド名を調べて関連情報を抽出することで、それらすべてを処理します。
チートシート
__call() メソッドは、存在しない、またはアクセスできないインスタンスメソッドの呼び出しをインターセプトします。このメソッドは、メソッド名と引数の配列を受け取ります:
<?php
class Messenger {
public function __call(string $name, array $arguments): string {
return "Called '$name' with: " . implode(", ", $arguments);
}
}
$msg = new Messenger();
echo $msg->sendEmail("Hello", "World");
// 出力: Called 'sendEmail' with: Hello, World
__callStatic() メソッドは、静的メソッドの呼び出しをインターセプトします。このメソッドは static として宣言される必要があります:
<?php
class QueryBuilder {
public static function __callStatic(string $name, array $arguments): string {
if (str_starts_with($name, "findBy")) {
$field = substr($name, 6);
return "Finding by $field: " . $arguments[0];
}
return "Unknown method: $name";
}
}
echo QueryBuilder::findByEmail("test@example.com");
// 出力: Finding by Email: test@example.com
これらのマジックメソッドは、流れるようなAPI(fluent API)の作成、メソッド転送の実装、またはメソッド名が操作に変換される動的なクエリビルダーの構築に役立ちます。
自分で試してみよう
<?php
require_once 'ApiClient.php';
// 入力を読み込む
$baseUrl = trim(fgets(STDIN));
$resource = trim(fgets(STDIN));
$value = trim(fgets(STDIN));
// TODO: ベースURLを使用してApiClientインスタンスを作成する
// TODO: リソースをメソッド名のサフィックスとして使用して、動的なgetメソッドを呼び出す
// ヒント: $api->{"get" . $resource}($value) のような可変メソッド呼び出しを使用する
// TODO: 2つの引数 "electronics" と "active" を指定して getProducts を呼び出す
// TODO: 値を引数として静的メソッド createOrder を呼び出す
// ヒント: ApiClient::createOrder($value) を使用する
// TODO: リソースを引数として静的メソッド deleteItem を呼び出す
?>このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。