Menu
Coddy logo textTech

__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の作成、メソッド転送の実装、あるいはメソッド名がデータベース操作に変換されるような動的クエリビルダーの構築に強力な威力を発揮します。これらを使用することで、オブジェクトは、明示的に定義されていないものも含め、あらゆるメソッド呼び出しに対してインテリジェントに応答できるようになります。

challenge icon

チャレンジ

簡単

マジックメソッドを使用してメソッド呼び出しを柔軟に処理する、動的なAPIクライアントを構築しましょう。各メソッドを明示的に定義することなく、インスタンスメソッドと静的メソッドの両方の呼び出しをインターセプトし、それらを意味のあるAPIのようなレスポンスに変換するクラスを作成します。

コードは2つのファイルに分けて構成します:

  • ApiClient.php — マジックメソッドを使用してAPIインタラクションをシミュレートする ApiClient クラスを作成します。クラスには、コンストラクタを通じて設定されるプライベートな $baseUrl プロパティを持たせる必要があります。次の2つのマジックメソッドを実装してください:
    • __call() — インスタンスメソッドの呼び出しを処理します。get で始まるメソッド(getUsersgetPosts など)が呼び出された場合、リソース名("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 をリソース識別子を引数として呼び出します。

マジックメソッドを使用すると、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 を呼び出す

?>
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン