[PHP]__METHOD__とは?クラス名を含むメソッド名を取得するマジック定数を徹底解説

PHP

はじめに

これまでの記事で、__FUNCTION__(関数・メソッド名のみ)、__CLASS__(クラス名)、__TRAIT__(トレイト名)という、それぞれ単体の情報を取得するマジック定数を解説してきました。今回取り上げる __METHOD__ は、これらの「いいとこ取り」とも言える定数で、「クラス名::メソッド名」という、より文脈の分かりやすい形式で現在のメソッド情報を取得します。

__FUNCTION__ の記事で触れた通り、__FUNCTION__ はクラスメソッド内で使っても「メソッド名のみ」しか返さず、「どのクラスのメソッドか」が分かりません。__METHOD__ はこの不足を補い、ログ出力やデバッグにおいて、より実用的で読みやすい識別子を提供してくれます。本記事では基本的な使い方から、__FUNCTION__・__CLASS__ との組み合わせとの違い、実践的な活用例まで詳しく解説します。


定数概要

項目内容
定数名__METHOD__
種別マジック定数
値の型文字列(string)
値の内容"クラス名::メソッド名"(グローバル関数の場合は関数名のみ)
対応バージョンPHP 5.0以降
継承時の挙動__CLASS__と同様、「定義元」のクラス名が使われる
関連するマジック定数__FUNCTION__(メソッド名のみ)、__CLASS__(クラス名のみ)

__FUNCTION__との比較(イメージ図)

  class OrderService {
      public function process() {
          echo __FUNCTION__;  → "process"
          echo __METHOD__;    → "OrderService::process"
                                 ★この記事の対象(クラス名を含む)
      }
  }

  function standaloneFunction() {
      echo __FUNCTION__;  → "standaloneFunction"
      echo __METHOD__;    → "standaloneFunction"
      (グローバル関数ではクラス名部分がないため、両者は同じ値になる)
  }

ポイントは、__METHOD__ がクラスメソッド内では "クラス名::メソッド名" という形式になる一方、グローバル関数の中では __FUNCTION__ と同じ値になるという点です。この一貫性のある挙動により、__METHOD__ は「クラスの内外を問わず安全に使える、より情報量の多い識別子」として扱うことができます。


実践サンプル7選

例1:基本的な使い方

<?php

class UserService
{
    public function register(): void
    {
        echo '現在実行中: ' . __METHOD__ . PHP_EOL;
    }
}

$service = new UserService();
$service->register(); // "現在実行中: UserService::register"

例2:デバッグログに統一的な識別子を含めるロガー

<?php

class DebugLogger
{
    public static function trace(string $location, string $message): void
    {
        echo "[{$location}] {$message}" . PHP_EOL;
    }
}

class PaymentProcessor
{
    public function charge(float $amount): void
    {
        DebugLogger::trace(__METHOD__, "{$amount}円の決済処理を開始");
        // 何らかの処理...
        DebugLogger::trace(__METHOD__, '決済処理が完了');
    }
}

(new PaymentProcessor())->charge(5000);

例3:抽象メソッドの未実装を検出するパターン

<?php

abstract class AbstractRepository
{
    /**
     * __METHOD__を使うことで、エラーメッセージに
     * "どのクラスのどのメソッド"かが一目で分かる形式で出力される
     */
    protected function requireImplementation(): never
    {
        throw new \LogicException(__METHOD__ . 'を呼び出す前に、サブクラスでオーバーライドしてください');
    }
}

class DummyRepository extends AbstractRepository
{
    public function find(int $id): mixed
    {
        $this->requireImplementation();
    }
}

try {
    (new DummyRepository())->find(1);
} catch (\LogicException $e) {
    echo $e->getMessage() . PHP_EOL;
}

例4:静的メソッド内での挙動確認

<?php

class MathUtils
{
    public static function square(int $n): int
    {
        // 静的メソッドでも通常のメソッドと同様に"クラス名::メソッド名"形式になる
        echo __METHOD__ . "が呼ばれました(引数: {$n})" . PHP_EOL;
        return $n * $n;
    }
}

echo MathUtils::square(4) . PHP_EOL;

例5:継承時の挙動を確認するデモ(static::classとの組み合わせ)

<?php

class BaseController
{
    public function handleRequest(): void
    {
        // __METHOD__は常に"BaseController::handleRequest"(定義元のクラス名)
        echo '定義元: ' . __METHOD__ . PHP_EOL;

        // 実行時の実際のクラス名が欲しい場合はstatic::classと組み合わせる
        echo '実行時: ' . static::class . '::handleRequest' . PHP_EOL;
    }
}

class AdminController extends BaseController
{
}

(new AdminController())->handleRequest();

例6:パフォーマンス計測のラベルとして使う

<?php

class PerformanceTimer
{
    public static function measure(string $label, callable $callback): mixed
    {
        $start = microtime(true);
        $result = $callback();
        $elapsed = round((microtime(true) - $start) * 1000, 2);
        echo "{$label}: {$elapsed}ms" . PHP_EOL;
        return $result;
    }
}

class ReportGenerator
{
    public function generate(): array
    {
        return PerformanceTimer::measure(__METHOD__, function () {
            usleep(30000); // 重い処理のシミュレーション
            return ['status' => 'completed'];
        });
    }
}

(new ReportGenerator())->generate();

例7:インターフェース実装クラスでの一貫したログ出力

<?php

interface PaymentGateway
{
    public function process(float $amount): bool;
}

trait LoggableGateway
{
    protected function logTransaction(float $amount): void
    {
        echo __METHOD__ . "(実装: " . static::class . "): {$amount}円を処理" . PHP_EOL;
    }
}

class StripeGateway implements PaymentGateway
{
    use LoggableGateway;

    public function process(float $amount): bool
    {
        $this->logTransaction($amount);
        return true;
    }
}

(new StripeGateway())->process(3000);

関連機能との比較

機能役割__METHOD__との違い
__METHOD__“クラス名::メソッド名”形式で現在のメソッド情報を取得本記事の対象。クラスと関数の両方の文脈で一貫して使える
__FUNCTION__現在の関数・メソッド名のみを取得クラス名を含まない、より限定的な情報
__CLASS__現在のクラス名のみを取得メソッド名を含まない、クラス名単体の情報
static::class遅延静的束縛による実行時クラス名の取得__METHOD__の静的な「定義元」クラス名とは異なり、動的な実行時のクラス名を返す
(new \ReflectionMethod(...))->getName()リフレクションAPIでメソッド情報を取得より高機能だが、記述量が多くオーバーヘッドもある

よくある落とし穴(注意点)

  1. 継承時は「定義元」のクラス名が使われる __CLASS__ と同様、__METHOD__ もコンパイル時に決定する静的な値であり、そのメソッドが「どのクラスで定義されているか」を基準にします。サブクラスから呼び出した場合でも、親クラスでメソッドが定義されていれば、親クラスの名前が使われます(例5を参照)。実行時のクラス名も併せて記録したい場合は、static::class と組み合わせましょう。
  2. グローバル関数内では__FUNCTION__と同じ値になる クラスの外側にある通常の関数内で __METHOD__ を使うと、クラス名の部分がないため __FUNCTION__ と同じ値になります。この一貫した挙動のおかげで、どちらのコンテキストでも安全に __METHOD__ を使うことができます。
  3. トレイト内での挙動は__CLASS__と同様 トレイト内に書かれた __METHOD__ は、そのトレイトを使用しているクラスの名前と組み合わされます("使用クラス名::メソッド名")。トレイト自身の名前を含めたい場合は、前回解説した __TRAIT__ との組み合わせを検討しましょう。
  4. 名前空間を含む完全修飾名になる 名前空間を使っているプロジェクトでは、__METHOD__ が返すクラス名部分は App\Services\UserService::register のような完全修飾名になります。ログが長くなりすぎる場合は、適宜短縮処理を加えることも検討しましょう。
  5. 匿名クラス内での挙動 PHP 7.0以降で使える匿名クラスの中で __METHOD__ を使うと、class@anonymous のような特殊な文字列を含む値が返ります。匿名クラスを多用するコードでは、ログの可読性に注意が必要です。

まとめ

観点まとめ
何をする定数か現在のメソッドを「クラス名::メソッド名」形式で、またはグローバル関数名として取得する
主な用途デバッグログの識別子、未実装メソッドのエラーメッセージ、パフォーマンス計測のラベル
__FUNCTION__との違いクラスメソッド内ではクラス名も含めて取得できる、より情報量の多い定数
継承時の挙動__CLASS__と同様、定義元のクラス名を使う(動的な実行時クラス名ではない)
注意点継承時の静的な値であること、トレイト内での挙動、名前空間を含む完全修飾名になること

__METHOD__ は、__FUNCTION__ と __CLASS__ を組み合わせたような、実用性の高いマジック定数です。クラスの内外を問わず一貫して使えるため、ログ出力やエラーメッセージの識別子として、多くの場面で __FUNCTION__ よりも優先して使われる、日常的に便利な存在です。

タイトルとURLをコピーしました