はじめに
これまでの記事で __CLASS__ や __NAMESPACE__ といったマジック定数を解説してきた中で、度々 ClassName::class という構文が比較対象として登場しました。今回は、この ::class 構文そのものを正面から取り上げます。
::class は、PHP 5.5で導入された、クラス名(名前空間を含む完全修飾名)を文字列として取得するための構文です。$obj instanceof ClassName のような型チェック、is_a() や class_exists() のような文字列を引数に取る関数への受け渡し、属性(Attribute)の指定など、現代的なPHPコードのいたるところで目にする、非常に重要な構文です。本記事では基本的な使い方から、動的なクラス名解決との違い、実践的な活用例まで詳しく解説します。
構文概要
| 項目 | 内容 |
|---|---|
| 構文 | ClassName::class |
| 種別 | クラス名解決演算子(Class Name Resolution) |
| 戻り値の型 | 文字列(string) |
| 値の内容 | クラスの完全修飾名(名前空間がある場合はそれを含む) |
| 対応バージョン | PHP 5.5.0以降 |
| 評価タイミング | コンパイル時に静的に解決される |
| 特徴的な挙動 | インスタンス変数に対しても使用可能($obj::class、PHP 8.0以降) |
文字列リテラルとの決定的な違い(イメージ図)
namespace App\Services;
use App\Models\User;
class UserService
{
public function demonstrate(): void
{
echo User::class; → "App\Models\User"
echo 'User::class'; → これは単なる文字列 "User::class"(評価されない)
echo "App\\Models\\User"; → 手入力の文字列、タイプミスのリスクがある
}
}
ポイントは、::class が単なる文字列リテラルではなく、use 文によるエイリアス解決を含めて、PHP自身がクラス名を正確に解決してくれるという点です。クラス名を手でタイプミスする心配がなく、クラスがリネームされた際もIDEのリファクタリング機能で追従させやすいという、大きな利点があります。
実践サンプル7選
例1:基本的な使い方
<?php
namespace App\Models;
class User
{
}
echo User::class . PHP_EOL; // "App\Models\User"(名前空間込みの完全修飾名)
例2:use文によるエイリアス解決の確認
<?php
namespace App\Controllers;
use App\Models\User as UserModel;
class UserController
{
public function getClassName(): string
{
// エイリアス(UserModel)で書いても、::classは元の完全修飾名を正しく返す
return UserModel::class;
}
}
echo (new UserController())->getClassName() . PHP_EOL; // "App\Models\User"
例3:instanceofと組み合わせた型チェック
<?php
interface PaymentMethod
{
}
class CreditCard implements PaymentMethod
{
}
class BankTransfer implements PaymentMethod
{
}
function describe(PaymentMethod $method): string
{
// クラス名を直接タイプするよりも安全な比較方法
return match (true) {
$method instanceof CreditCard => CreditCard::class . 'が選択されました',
$method instanceof BankTransfer => BankTransfer::class . 'が選択されました',
default => '不明な支払い方法',
};
}
echo describe(new CreditCard()) . PHP_EOL;
例4:依存性注入コンテナでのサービス登録
<?php
interface LoggerInterface
{
public function log(string $message): void;
}
class FileLogger implements LoggerInterface
{
public function log(string $message): void
{
echo "[FILE] {$message}" . PHP_EOL;
}
}
class SimpleContainer
{
private array $bindings = [];
public function bind(string $abstract, string $concrete): void
{
$this->bindings[$abstract] = $concrete;
}
public function resolve(string $abstract): object
{
$concrete = $this->bindings[$abstract] ?? $abstract;
return new $concrete();
}
}
$container = new SimpleContainer();
// クラス名を::classで指定することで、タイプミスを防ぎIDE補完も効く
$container->bind(LoggerInterface::class, FileLogger::class);
$logger = $container->resolve(LoggerInterface::class);
$logger->log('コンテナからロガーを解決しました');
例5:PHP 8.0以降のインスタンス変数に対する::class
<?php
class Product
{
}
$product = new Product();
/**
* PHP 8.0以降、インスタンス変数に対しても::classが使える
* これはget_class($product)の簡潔な代替になる
*/
echo $product::class . PHP_EOL; // "Product"
var_dump($product::class === get_class($product)); // true
例6:属性(Attribute)やリフレクションでのクラス名指定
<?php
#[\Attribute]
class Route
{
public function __construct(public string $path)
{
}
}
class HomeController
{
#[Route('/home')]
public function index(): string
{
return 'ホームページ';
}
}
$reflection = new \ReflectionMethod(HomeController::class, 'index');
foreach ($reflection->getAttributes(Route::class) as $attribute) {
$instance = $attribute->newInstance();
echo "ルート: {$instance->path}" . PHP_EOL;
}
例7:例外クラスをキーにしたエラーハンドリングマップ
<?php
class ValidationError extends \Exception
{
}
class NotFoundError extends \Exception
{
}
class ErrorHandler
{
private array $handlers;
public function __construct()
{
/**
* クラス名を文字列キーとして使う配列マップにも、
* ::classを使うことでタイプミスを防ぎつつ構築できる
*/
$this->handlers = [
ValidationError::class => fn ($e) => "検証エラー: {$e->getMessage()}",
NotFoundError::class => fn ($e) => "見つかりません: {$e->getMessage()}",
];
}
public function handle(\Throwable $e): string
{
$handler = $this->handlers[get_class($e)] ?? fn ($e) => "未知のエラー: {$e->getMessage()}";
return $handler($e);
}
}
$handler = new ErrorHandler();
echo $handler->handle(new ValidationError('メールアドレスが不正です')) . PHP_EOL;
関連機能との比較
| 機能 | 役割 | ClassName::classとの違い |
|---|---|---|
ClassName::class | クラス名をコンパイル時に文字列として解決 | 本記事の対象。use文のエイリアスも正しく解決される |
__CLASS__ | 現在のクラス(コードが書かれている場所)の名前を取得 | 「今いる場所」のクラス名を返す点が異なり、他のクラス名は指定できない |
get_class($obj) | インスタンスから実行時のクラス名を取得する関数 | 実行時に動的に評価される点が、静的な::classと異なる |
$obj::class | インスタンス変数からクラス名を取得(PHP 8.0以降) | get_class($obj)とほぼ同義の、より簡潔な構文 |
文字列リテラル('App\Models\User') | 手動でクラス名を文字列として記述する | タイプミスのリスクがあり、IDEのリファクタリング機能の恩恵も受けにくい |
よくある落とし穴(注意点)
- 存在しないクラスに対しても構文エラーにはならない
::classは多くの場合、単なる文字列への置き換えとして解決されるため、NonExistentClass::classのように実在しないクラス名に対して使っても、その場では即座にエラーになりません(実際に使われるタイミングでclass_exists()等による検証が必要です)。 use文のエイリアスを正しく解決してくれる利点を活かす エイリアスを使っている場合、手動で完全修飾名をタイプするより::classを使う方が、常に正しい値を得られて安全です(例2を参照)。- PHP 8.0未満ではインスタンス変数に
::classを使えない$obj::classという構文はPHP 8.0で追加されたものです。それより前のバージョンとの互換性が必要な場合は、従来通りget_class($obj)を使う必要があります(例5を参照)。 ::classは値を返すだけで、オートロードを発生させないClassName::classという式自体は、そのクラスファイルを実際に読み込む(オートロードする)ことはありません。単に文字列を生成するだけなので、クラスの存在確認が必要な場面では、別途class_exists()などを使う必要があります。- トレイトやインターフェースに対しても使用可能
::classはクラスだけでなく、インターフェース名やトレイト名に対しても同様に使用でき、それぞれの完全修飾名を取得できます。
まとめ
| 観点 | まとめ |
|---|---|
| 何をする構文か | クラス名(名前空間を含む完全修飾名)を、コンパイル時に安全な文字列として取得する |
| 主な用途 | 型チェック、依存性注入コンテナでのサービス登録、属性やリフレクションでのクラス指定 |
| 最大の利点 | use文のエイリアスを正しく解決し、タイプミスやリファクタリング漏れを防げる |
__CLASS__との違い | __CLASS__は「現在のコンテキスト」のクラス名、::classは任意のクラス名を指定できる |
| 注意点 | 存在確認はしてくれないこと、PHP 8.0未満でのインスタンス変数利用の制約 |
ClassName::class は、文字列としてクラス名を扱う必要があるあらゆる場面において、単なる文字列リテラルよりも安全で保守性の高い、現代的なPHP開発に欠かせない構文です。特に use 文によるエイリアスが絡む場面や、IDEの補完・リファクタリング機能を活用したい場合に、その真価を発揮します。
