[PHP]__CLASS__とは?現在のクラス名を取得するマジック定数を徹底解説

PHP

はじめに

前回の記事では、現在の関数・メソッド名を取得する __FUNCTION__ を解説し、その中で「クラス名が含まれない」という特性に触れました。今回取り上げる __CLASS__ は、それを補う形で、現在のクラスの名前(名前空間を含む完全修飾名)を取得するためのマジック定数です。

__CLASS__ は、それが記述されているクラス定義自身の名前に、コンパイル時に置き換えられます。エラーメッセージへのクラス名の埋め込み、ロギング、自己参照的な処理(new static() の代替としての利用など)において活用されます。本記事では基本的な使い方から、継承時の挙動、static::class との違いまで詳しく解説します。


定数概要

項目内容
定数名__CLASS__
種別マジック定数
値の型文字列(string)
値の内容現在のクラスの名前(名前空間がある場合は完全修飾名)
対応バージョンPHP 4.3.0以降
トレイト内での挙動そのトレイトが使用されているクラスの名前になる
関連する構文PHP 5.5以降は ClassName::class という、より汎用的な構文も利用可能

継承時の挙動(イメージ図)

  class Animal {
      public function identify() {
          echo __CLASS__;  ← これは常に "Animal" を返す
      }
  }

  class Dog extends Animal {
      // identify()はオーバーライドしていない
  }

  $dog = new Dog();
  $dog->identify();
              │
              ▼
  出力: "Animal"
  (実際のインスタンスがDogであっても、
   __CLASS__はメソッドが"定義されている"クラス名を返す)

  ※動的な実行時クラス名("Dog")が欲しい場合は
    static::class や get_class($this) を使う必要がある

ポイントは、__CLASS__ がコンパイル時に決定する静的な値であり、「そのコードが書かれているクラス」を表すという点です。継承関係がある場合、実際に実行されているインスタンスのクラス(遅延静的束縛でいう static)とは異なる場合がある点を理解しておく必要があります。


実践サンプル7選

例1:基本的な使い方

<?php

class Product
{
    public function describe(): string
    {
        return '現在のクラス: ' . __CLASS__;
    }
}

$product = new Product();
echo $product->describe() . PHP_EOL;

例2:名前空間を含む完全修飾名の確認

<?php

namespace App\Models;

class User
{
    public function getClassName(): string
    {
        // __CLASS__は名前空間込みの完全修飾名を返す
        return __CLASS__;
    }
}

$user = new User();
echo $user->getClassName() . PHP_EOL; // "App\Models\User"

例3:継承時の挙動を比較するデモ(static::classとの違い)

<?php

class Base
{
    public function showClassConstant(): string
    {
        return __CLASS__; // 常に定義元の"Base"を返す
    }

    public function showLateStaticBinding(): string
    {
        return static::class; // 実際の実行時クラス名を返す
    }
}

class Derived extends Base
{
}

$derived = new Derived();
echo $derived->showClassConstant() . PHP_EOL;       // "Base"
echo $derived->showLateStaticBinding() . PHP_EOL;    // "Derived"

例4:例外メッセージにクラス名を自動的に含める

<?php

class ConfigurationException extends \RuntimeException
{
    public function __construct(string $reason)
    {
        // __CLASS__を使うことで、将来クラス名をリネームしても
        // メッセージ内のクラス名表記が自動的に追従する
        parent::__construct(__CLASS__ . ": {$reason}");
    }
}

try {
    throw new ConfigurationException('設定ファイルが見つかりません');
} catch (ConfigurationException $e) {
    echo $e->getMessage() . PHP_EOL;
}

例5:トレイト内での__CLASS__の挙動を確認する

<?php

trait Loggable
{
    public function logSelf(): void
    {
        // トレイトはそれ自体ではインスタンス化されないため、
        // 使用しているクラスの名前が返る
        echo __CLASS__ . 'がログ出力を行いました' . PHP_EOL;
    }
}

class OrderProcessor
{
    use Loggable;
}

class PaymentProcessor
{
    use Loggable;
}

(new OrderProcessor())->logSelf();   // "OrderProcessor"
(new PaymentProcessor())->logSelf(); // "PaymentProcessor"

例6:静的コンテキストでのファクトリーメソッドの実装

<?php

class SimpleFactory
{
    /**
     * 静的メソッド内でも__CLASS__は利用できるが、
     * 継承先での動的な生成にはstatic::class(new static())が適する
     */
    public static function createWithClassConstant(): self
    {
        $className = __CLASS__;
        return new $className();
    }

    public static function createWithLateBinding(): static
    {
        return new static(); // 継承先のクラスがそのままインスタンス化される
    }
}

class ExtendedFactory extends SimpleFactory
{
}

$a = ExtendedFactory::createWithClassConstant();
$b = ExtendedFactory::createWithLateBinding();
echo get_class($a) . PHP_EOL; // "SimpleFactory"(__CLASS__は定義元を返すため)
echo get_class($b) . PHP_EOL; // "ExtendedFactory"(遅延静的束縛で正しく継承先になる)

例7:ClassName::class構文との比較

<?php

namespace App\Services;

class PaymentService
{
    public function compareClassReferences(): array
    {
        return [
            'magic_constant' => __CLASS__,          // 現在実行中のコンテキストのクラス名
            'class_syntax'   => self::class,         // 同様にコンパイル時に解決される完全修飾名
            'explicit_name'  => PaymentService::class, // クラス名を明示的に指定した場合
        ];
    }
}

$service = new PaymentService();
print_r($service->compareClassReferences());
// すべて同じ値 "App\Services\PaymentService" になる

関連機能との比較

機能役割__CLASS__との違い
__CLASS__現在のクラス名を取得(マジック定数)本記事の対象。コンパイル時に決定する静的な値
static::class遅延静的束縛による実行時クラス名の取得継承先での実際の呼び出しクラス名を動的に反映する
self::classクラス定義時点でのクラス名を取得__CLASS__とほぼ同義だが、PHP 5.5以降のより統一的な構文
get_class($this)インスタンスから実行時のクラス名を取得$thisが必要なインスタンスメソッド限定だが、実行時の実際のクラスを返す
__FUNCTION__ / __METHOD__関数・メソッド名を取得クラス名ではなく、関数やメソッドの名前の情報を提供する(前回記事を参照)

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

  1. 継承時は「定義元のクラス名」を返す これが最も重要な注意点です。__CLASS__ は、そのコードが「どのクラスに書かれているか」を表すものであり、実際に実行されているインスタンスのクラス名(サブクラス)とは異なる場合があります。実行時のクラス名が必要な場合は static::class や get_class($this) を使いましょう(例3・例6を参照)。
  2. self::classとの違いはほぼない PHP 5.5以降で使える self::class は、__CLASS__ とほぼ同じ値を返します。新しいコードでは、より統一感のある self::class の記法が好まれる傾向にありますが、どちらを使っても機能的な違いはほとんどありません。
  3. グローバルスコープでは空文字列になる クラス定義の外側(通常の関数やスクリプトのトップレベル)で __CLASS__ を使うと、空文字列が返ります。
  4. トレイト内では「使用しているクラス」の名前になる トレイトはそれ自体が独立してインスタンス化されることはないため、トレイト内に書かれた __CLASS__ は、そのトレイトを use しているクラスの名前を返します(例5を参照)。これは直感に反すると感じる人もいるため、押さえておくべき挙動です。
  5. 名前空間を含む完全修飾名が返ることを前提に処理する 名前空間を使っているプロジェクトでは、__CLASS__ は App\Models\User のような完全修飾名を返します。クラス名の「短い部分」だけが欲しい場合は、basename(str_replace('\\', '/', __CLASS__)) のような追加処理や、(new \ReflectionClass(__CLASS__))->getShortName() を使う必要があります。

まとめ

観点まとめ
何をする定数かそれが記述されているクラスの名前(名前空間込みの完全修飾名)に置き換わる
主な用途エラーメッセージへのクラス名埋め込み、ロギング、トレイト内での呼び出し元クラスの特定
継承時の挙動常に「定義元」のクラス名を返す(実行時の実際のクラスとは異なる場合がある)
動的な代替実行時のクラス名が必要な場合はstatic::classやget_class($this)を使う
注意点継承時の静的な値であること、トレイト内での挙動、グローバルスコープでは空文字列になること

__CLASS__ は、コードが「定義された」時点でのクラス名を静的に取得するためのマジック定数です。継承関係において実行時の動的なクラス名が必要な場面では、遅延静的束縛を使った static::class との使い分けを正しく理解することが、意図通りに動作するコードを書く上で重要です。

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