はじめに
これまでの記事で、__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でメソッド情報を取得 | より高機能だが、記述量が多くオーバーヘッドもある |
よくある落とし穴(注意点)
- 継承時は「定義元」のクラス名が使われる
__CLASS__と同様、__METHOD__もコンパイル時に決定する静的な値であり、そのメソッドが「どのクラスで定義されているか」を基準にします。サブクラスから呼び出した場合でも、親クラスでメソッドが定義されていれば、親クラスの名前が使われます(例5を参照)。実行時のクラス名も併せて記録したい場合は、static::classと組み合わせましょう。 - グローバル関数内では
__FUNCTION__と同じ値になる クラスの外側にある通常の関数内で__METHOD__を使うと、クラス名の部分がないため__FUNCTION__と同じ値になります。この一貫した挙動のおかげで、どちらのコンテキストでも安全に__METHOD__を使うことができます。 - トレイト内での挙動は
__CLASS__と同様 トレイト内に書かれた__METHOD__は、そのトレイトを使用しているクラスの名前と組み合わされます("使用クラス名::メソッド名")。トレイト自身の名前を含めたい場合は、前回解説した__TRAIT__との組み合わせを検討しましょう。 - 名前空間を含む完全修飾名になる 名前空間を使っているプロジェクトでは、
__METHOD__が返すクラス名部分はApp\Services\UserService::registerのような完全修飾名になります。ログが長くなりすぎる場合は、適宜短縮処理を加えることも検討しましょう。 - 匿名クラス内での挙動 PHP 7.0以降で使える匿名クラスの中で
__METHOD__を使うと、class@anonymousのような特殊な文字列を含む値が返ります。匿名クラスを多用するコードでは、ログの可読性に注意が必要です。
まとめ
| 観点 | まとめ |
|---|---|
| 何をする定数か | 現在のメソッドを「クラス名::メソッド名」形式で、またはグローバル関数名として取得する |
| 主な用途 | デバッグログの識別子、未実装メソッドのエラーメッセージ、パフォーマンス計測のラベル |
__FUNCTION__との違い | クラスメソッド内ではクラス名も含めて取得できる、より情報量の多い定数 |
| 継承時の挙動 | __CLASS__と同様、定義元のクラス名を使う(動的な実行時クラス名ではない) |
| 注意点 | 継承時の静的な値であること、トレイト内での挙動、名前空間を含む完全修飾名になること |
__METHOD__ は、__FUNCTION__ と __CLASS__ を組み合わせたような、実用性の高いマジック定数です。クラスの内外を問わず一貫して使えるため、ログ出力やエラーメッセージの識別子として、多くの場面で __FUNCTION__ よりも優先して使われる、日常的に便利な存在です。
