PHPの無名クラスについて

採用はこちら

PHPの無名クラスとは、名前を付けずにその場で定義し、そのままインスタンス化できるクラスのことです。

通常のクラスでは、まずクラス名を決めて定義し、そのクラス名を使ってオブジェクトを作成します。

class Logger
{
    public function log(string $message): void
    {
        echo $message;
    }
}

$logger = new Logger();
$logger->log('ログを出力します');

一方、無名クラスでは、クラス名を付けずに次のように書けます。

$logger = new class {
    public function log(string $message): void
    {
        echo $message;
    }
};

$logger->log('ログを出力します');

new class { ... } と書くことで、その場で名前のないクラスを定義し、同時にオブジェクトを作成できます。

無名クラスは、名前付きクラスとして切り出すほどではない、小さな一時的な実装を作りたいときに便利です。

特に、テスト用のスタブ、簡単なインターフェース実装、一度だけ使う補助的なオブジェクトなどで活用できます。

目次

無名クラスの基本構文

無名クラスの基本構文は次の通りです。

$object = new class {
    // プロパティ
    // メソッド
};

たとえば、簡単なメソッドを持つ無名クラスは次のように書けます。

$object = new class {
    public function hello(): string
    {
        return 'Hello';
    }
};

echo $object->hello();

このコードでは、名前のないクラスをその場で定義し、$object に代入しています。

通常のクラスと違い、class Sample { ... } のようなクラス名はありません。

そのため、別の場所からクラス名で再利用することは基本的にできません。

通常のクラスとの違い

通常のクラスでは、クラス名を付けて定義します。

class UserFormatter
{
    public function format(string $name): string
    {
        return strtoupper($name);
    }
}

$formatter = new UserFormatter();
echo $formatter->format('tanaka');

同じような処理を無名クラスで書くと、次のようになります。

$formatter = new class {
    public function format(string $name): string
    {
        return strtoupper($name);
    }
};

echo $formatter->format('tanaka');

無名クラスは、通常のクラスと同じようにプロパティやメソッドを持てます。

また、コンストラクタ、継承、インターフェース実装、トレイト、クラス定数なども使えます。

ただし、最大の違いは明示的なクラス名がないことです。

そのため、次のように無名クラスそのものを型として指定することは、通常はできません。

function handle(UserFormatter $formatter): void
{
    // ...
}

無名クラスにも内部的なクラス名は割り当てられますが、開発者が通常のクラス名として明示的に使うものではありません。

そのため、型として扱いたい場合は、インターフェースや親クラスを使うのが一般的です。

インターフェースを実装できる

無名クラスは、通常のクラスと同じようにインターフェースを実装できます。

interface LoggerInterface
{
    public function log(string $message): void;
}

$logger = new class implements LoggerInterface {
    public function log(string $message): void
    {
        echo '[LOG] ' . $message;
    }
};

$logger->log('処理が完了しました');

このように書くと、無名クラスのオブジェクトを LoggerInterface の実装として扱えます。

実務では、あるクラスがインターフェースを要求している場面で、その場限りの簡単な実装を渡したいときに便利です。

interface LoggerInterface
{
    public function log(string $message): void;
}

class UserService
{
    public function __construct(private LoggerInterface $logger)
    {
    }

    public function createUser(string $name): void
    {
        $this->logger->log($name . ' を作成しました');
    }
}

$service = new UserService(
    new class implements LoggerInterface {
        public function log(string $message): void
        {
            echo $message;
        }
    }
);

$service->createUser('山田');

この例では、UserServiceLoggerInterface を受け取ります。

そのため、無名クラス自体に名前がなくても、LoggerInterface の実装として問題なく利用できます。

コンストラクタに引数を渡せる

無名クラスでも、通常のクラスと同じように __construct() を使えます。

$greeting = new class('田中') {
    private string $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function say(): string
    {
        return 'こんにちは、' . $this->name . 'さん';
    }
};

echo $greeting->say();

new class('田中') のように、class の直後に引数を書きます。

引数を渡さない場合は、次のように書けます。

$object = new class {
    public function hello(): string
    {
        return 'Hello';
    }
};

また、必要に応じて次のように () を付けて書くこともあります。

$object = new class() {
    public function hello(): string
    {
        return 'Hello';
    }
};

ただし、引数がない場合は new class { ... } の形がシンプルです。

継承もできる

無名クラスは、既存のクラスを継承できます。

class BaseController
{
    protected function response(string $message): array
    {
        return ['message' => $message];
    }
}

$controller = new class extends BaseController {
    public function index(): array
    {
        return $this->response('Hello');
    }
};

print_r($controller->index());

extends を使えば、親クラスのメソッドやプロパティを利用できます。

$object = new class extends SomeClass {
    // ...
};

このように、無名クラスは単なる簡易オブジェクトではなく、通常のクラスと同じように継承関係を持てます。

トレイトも使える

無名クラスの中では、トレイトも利用できます。

trait TimestampTrait
{
    public function now(): string
    {
        return date('Y-m-d H:i:s');
    }
}

$object = new class {
    use TimestampTrait;
};

echo $object->now();

通常のクラスと同じように、use TraitName; と書けばトレイトのメソッドを取り込めます。

そのため、既存の共通処理を一時的なクラスに組み込みたい場合にも使えます。

クラス定数も定義できる

無名クラスでも、通常のクラスと同じようにクラス定数を定義できます。

$object = new class {
    public const TYPE = 'anonymous';

    public function getType(): string
    {
        return self::TYPE;
    }
};

echo $object->getType(); // anonymous

このように、無名クラスはプロパティやメソッドだけでなく、クラス定数も持てます。

つまり、名前がないだけで、クラスとしての基本的な機能は通常のクラスとほぼ同じです。

無名クラスの使いどころ

無名クラスは、どのような場面で使うとよいのでしょうか。

代表的な使いどころを紹介します。

一時的な小さな実装を作りたいとき

無名クラスは、名前付きクラスとして別ファイルに切り出すほどではない実装に向いています。

$sorter = new class {
    public function sort(array $items): array
    {
        sort($items);
        return $items;
    }
};

print_r($sorter->sort([3, 1, 2]));

このような小さな処理のために、わざわざ Sorter クラスを作るのは大げさな場合があります。

一度しか使わない補助的なオブジェクトであれば、無名クラスにするとコードをコンパクトにできます。

ただし、後から複数箇所で使う可能性がある場合は、最初から名前付きクラスにした方が管理しやすいです。

テスト用のスタブやフェイクを作りたいとき

無名クラスは、テストコードで特に便利です。

たとえば、メール送信処理のテストで、本物のメール送信クラスではなく、テスト用の簡単な実装を使いたい場合があります。

interface Mailer
{
    public function send(string $to, string $body): bool;
}

class UserRegistration
{
    public function __construct(private Mailer $mailer)
    {
    }

    public function register(string $email): bool
    {
        return $this->mailer->send($email, '登録ありがとうございます');
    }
}

$mailer = new class implements Mailer {
    public array $sent = [];

    public function send(string $to, string $body): bool
    {
        $this->sent[] = compact('to', 'body');
        return true;
    }
};

$registration = new UserRegistration($mailer);
$registration->register('test@example.com');

var_dump($mailer->sent);

この例では、Mailer インターフェースを実装した無名クラスを作っています。

実際にはメールを送らず、送信内容を $sent に保存するだけです。

このようなシンプルなスタブやフェイクであれば、無名クラスを使うとテストコードを簡潔に書けます。

一方で、呼び出し回数の検証や複雑な条件分岐、例外の発生などを細かく制御したい場合は、PHPUnitやMockeryなどのモック機能を使った方が分かりやすい場合もあります。

インターフェースの簡単な実装を作りたいとき

無名クラスは、インターフェースの一時的な実装にも向いています。

interface Formatter
{
    public function format(string $value): string;
}

$formatter = new class implements Formatter {
    public function format(string $value): string
    {
        return trim(mb_strtoupper($value));
    }
};

echo $formatter->format(' php ');

このように、インターフェースに沿った簡単な処理をその場で書けます。

ただし、この実装を複数箇所で使う場合や、名前を付けた方が意図が伝わる場合は、通常のクラスに切り出すべきです。

class UpperCaseFormatter implements Formatter
{
    public function format(string $value): string
    {
        return trim(mb_strtoupper($value));
    }
}

無名クラスは便利ですが、名前を付けることで設計意図が明確になる場合も多いです。

クロージャでは表現しづらい処理を書きたいとき

単純な処理であれば、無名関数、つまりクロージャで十分なこともあります。

$double = function (int $number): int {
    return $number * 2;
};

echo $double(5);

しかし、状態を持たせたい場合や、複数のメソッドを持たせたい場合は、無名クラスの方が自然です。

$formatter = new class {
    private int $count = 0;

    public function format(string $value): string
    {
        $this->count++;
        return strtoupper($value);
    }

    public function getCount(): int
    {
        return $this->count;
    }
};

echo $formatter->format('apple');
echo $formatter->format('banana');

echo $formatter->getCount(); // 2

この例では、format() が呼ばれた回数を $count に保存しています。

単発の処理だけならクロージャで十分ですが、状態や複数の振る舞いをまとめたい場合は、無名クラスが役立ちます。

無名クラスと無名関数の違い

無名クラスと無名関数は、名前は似ていますが別の機能です。

無名関数は、名前のない関数です。

$double = function (int $number): int {
    return $number * 2;
};

echo $double(5);

無名クラスは、名前のないクラスです。

$calculator = new class {
    public function double(int $number): int
    {
        return $number * 2;
    }
};

echo $calculator->double(5);

違いを整理すると、次のようになります。

種類主な用途
無名関数単発の処理、コールバック
無名クラス状態や複数メソッドを持つ一時的なオブジェクト
名前付きクラス再利用する重要なオブジェクト

たとえば、配列の値を変換するだけなら無名関数で十分です。

$items = ['apple', 'banana'];

$result = array_map(function (string $item): string {
    return strtoupper($item);
}, $items);

一方で、処理の状態を持ったり、複数のメソッドをまとめたりしたい場合は、無名クラスの方が扱いやすくなります。

無名クラスと stdClass の違い

PHPでは、簡単なオブジェクトを作る方法として stdClass もあります。

$user = new stdClass();
$user->name = '田中';
$user->age = 30;

echo $user->name;

stdClass は、動的にプロパティを追加できる汎用的なオブジェクトです。

一方、無名クラスでは、プロパティだけでなくメソッドも定義できます。

$user = new class('田中', 30) {
    public function __construct(
        private string $name,
        private int $age
    ) {
    }

    public function label(): string
    {
        return $this->name . '(' . $this->age . '歳)';
    }
};

echo $user->label();

また、new class {} で作ったオブジェクトは stdClass のインスタンスではありません。

$object = new class {
};

var_dump($object instanceof stdClass); // false

無名クラスは stdClass の省略形ではなく、あくまで名前のない独自クラスです。

違いをまとめると、次のようになります。

種類特徴
配列データのまとまりを扱いやすい
stdClass動的にプロパティを持たせられる簡易オブジェクト
無名クラスメソッド、コンストラクタ、インターフェース、継承などを使える
名前付きクラス再利用しやすく、設計意図を表しやすい

単なるデータなら配列やDTOで十分なこともあります。

振る舞いを持つ一時的なオブジェクトが必要な場合に、無名クラスが選択肢になります。

無名クラスの型判定

無名クラスには明示的なクラス名がありませんが、オブジェクトであることに変わりはありません。

インターフェースを実装していれば、instanceof で判定できます。

interface Exporter
{
    public function export(): string;
}

$object = new class implements Exporter {
    public function export(): string
    {
        return 'data';
    }
};

var_dump($object instanceof Exporter); // true

このように、無名クラスを実務で扱う場合は、インターフェースや親クラスを通して型を表現するのが基本です。

戻り値の型としても、無名クラスそのものではなくインターフェースを指定すると扱いやすくなります。

interface MessageFormatter
{
    public function format(string $message): string;
}

function createFormatter(): MessageFormatter
{
    return new class implements MessageFormatter {
        public function format(string $message): string
        {
            return '[INFO] ' . $message;
        }
    };
}

このようにすれば、呼び出し側は具体的なクラス名を知らなくても、MessageFormatter として安全に利用できます。

同じ無名クラス宣言から作ったオブジェクトは同じクラスになる

無名クラスは名前がありませんが、PHP内部ではクラスとして扱われます。

同じ無名クラス宣言から作られたオブジェクトは、同じクラスのインスタンスになります。

function createObject()
{
    return new class {
    };
}

$a = createObject();
$b = createObject();

var_dump(get_class($a) === get_class($b)); // true

この例では、createObject() の中にある同じ new class {} から $a$b が作られています。

そのため、内部的には同じ無名クラスのインスタンスです。

一方、見た目が同じでも、別々の場所に書かれた無名クラスは別のクラスです。

$a = new class {
};

$b = new class {
};

var_dump(get_class($a) === get_class($b)); // false

どちらも空の無名クラスですが、宣言された場所が違うため、別のクラスとして扱われます。

外側のクラスの private / protected に自動アクセスできるわけではない

無名クラスを別のクラスの中で定義しても、それだけで外側のクラスの privateprotected メンバーに自動アクセスできるわけではありません。

class Outer
{
    private string $name = 'outer';

    public function create()
    {
        return new class {
            public function show(): string
            {
                // Outer::$name に直接アクセスできるわけではない
                return 'show';
            }
        };
    }
}

外側の private プロパティを使いたい場合は、コンストラクタなどで値を渡します。

class Outer
{
    private string $name = 'outer';

    public function create()
    {
        return new class($this->name) {
            public function __construct(private string $name)
            {
            }

            public function show(): string
            {
                return $this->name;
            }
        };
    }
}

$object = (new Outer())->create();

echo $object->show(); // outer

ただし、無名クラスが外側のクラスを extends すれば、protected メンバーにはアクセスできます。

class Outer
{
    private int $privateValue = 1;
    protected int $protectedValue = 2;

    protected function protectedMethod(): int
    {
        return 3;
    }

    public function create()
    {
        return new class($this->privateValue) extends Outer {
            public function __construct(private int $receivedPrivateValue)
            {
            }

            public function total(): int
            {
                return $this->receivedPrivateValue
                    + $this->protectedValue
                    + $this->protectedMethod();
            }
        };
    }
}

echo (new Outer())->create()->total(); // 6

この例では、無名クラスが Outer を継承しているため、protectedValueprotectedMethod() にアクセスできます。

一方、privateValue には直接アクセスできないため、コンストラクタで値を渡しています。

PHP 8.3以降では readonly 無名クラスも使える

PHP 8.3以降では、無名クラスにも readonly を付けられます。

$config = new readonly class('production') {
    public function __construct(private string $env)
    {
    }

    public function getEnv(): string
    {
        return $this->env;
    }
};

echo $config->getEnv();

readonly クラスでは、インスタンス化後にプロパティを書き換えられないため、変更されない値を持つ一時的なオブジェクトを作るときに役立ちます。

たとえば、一時的な設定オブジェクトや、読み取り専用の値オブジェクトのような用途で使えます。

無名クラスのメリット

無名クラスには、主に次のようなメリットがあります。

小さな実装をその場に閉じ込められる

無名クラスを使うと、別ファイルにクラスを作るほどではない処理を、その場にまとめられます。

$validator = new class {
    public function validate(string $value): bool
    {
        return $value !== '';
    }
};

var_dump($validator->validate('test'));

このような小さな処理であれば、無名クラスにすることでコードの見通しがよくなる場合があります。

テストコードを書きやすい

テスト用の簡単な依存オブジェクトを、その場で作れます。

$repository = new class {
    public function find(int $id): array
    {
        return ['id' => $id, 'name' => 'テストユーザー'];
    }
};

テストの中だけで使う実装であれば、専用のクラスファイルを作らなくても済みます。

インターフェースの簡易実装として使える

interface Notifier
{
    public function notify(string $message): void;
}

$notifier = new class implements Notifier {
    public function notify(string $message): void
    {
        echo $message;
    }
};

このように、インターフェースに沿った簡単な実装をすぐに作れます。

無名クラスのデメリット

無名クラスは便利ですが、使いすぎるとコードが読みにくくなることがあります。

再利用しにくい

無名クラスには明示的なクラス名がないため、別の場所からクラス名で再利用するのには向いていません。

同じ処理を複数箇所で使うなら、名前付きクラスにした方がよいです。

class FileLogger implements LoggerInterface
{
    public function log(string $message): void
    {
        file_put_contents('app.log', $message . PHP_EOL, FILE_APPEND);
    }
}

再利用性がある処理には、意味のある名前を付けたクラスの方が適しています。

コードが長くなると読みにくい

無名クラスの中身が長くなると、呼び出し元のコードが読みにくくなります。

$service = new SomeService(
    new class implements LoggerInterface {
        // ここに長い処理が続く
    }
);

このようなコードは、一見しただけでは何をしているのか分かりにくくなります。

無名クラスは、あくまで小さな実装に使うのが基本です。

処理が長くなる場合や、重要なロジックを持つ場合は、名前付きクラスに切り出しましょう。

デバッグ時に分かりにくいことがある

無名クラスは明示的な名前がないため、エラーメッセージやデバッグ出力で少し分かりにくい場合があります。

$object = new class {
};

echo get_class($object);

実行環境やPHPのバージョンによって、class@anonymous のような内部的な名前が表示されます。

これは通常のクラス名のように意味を持つ名前ではないため、デバッグ時には読みづらくなることがあります。

無名クラスを使うべきケース

無名クラスは、次のようなケースで使うと効果的です。

ケース理由
その場で一時的な実装を作りたい別クラスにする手間を減らせる
テスト用の簡単なスタブを作りたいテストコード内で完結できる
インターフェースの簡易実装が必要小さな実装をすぐに用意できる
状態や複数メソッドを持たせたいクロージャより整理しやすい
名前付きクラスにするほどではないコードをコンパクトにできる

無名クラスを使わない方がよいケース

一方で、次のような場合は無名クラスではなく、通常の名前付きクラスを使う方がよいです。

ケース理由
複数箇所で再利用する名前付きクラスの方が管理しやすい
処理が長い無名クラスだと可読性が落ちやすい
ビジネスロジックが多い責務が分かりにくくなる
ドメイン上重要な概念を表す名前を付けた方が意図が伝わる
DIコンテナで管理するクラス名がある方が扱いやすい
チーム開発で共有する重要な部品明示的なクラス名があった方が理解しやすい

無名クラスは便利な機能ですが、名前付きクラスの代替として何でも置き換えるものではありません。

実務での判断基準

実務では、次のように判断するとよいです。

判断基準おすすめ
その場だけで使う小さな実装無名クラス
テスト用の簡単なフェイク無名クラス
インターフェースの一時的な実装無名クラス
複数箇所で使う名前付きクラス
処理が長い名前付きクラス
重要なビジネスロジックを含む名前付きクラス
意味のある名前を付けたい名前付きクラス
チームで共有する部品名前付きクラス

ポイントは、名前を付ける価値があるかどうかです。

一時的で小さな実装なら無名クラスが便利です。

一方、再利用性がある、意味を明確にしたい、設計上重要である、といった場合は名前付きクラスを使うべきです。

まとめ

PHPの無名クラスは、new class { ... } という構文で、その場で名前のないクラスを定義し、インスタンス化できる機能です。

無名クラスでは、通常のクラスと同じように、プロパティ、メソッド、コンストラクタ、継承、インターフェース実装、トレイト、クラス定数などを使えます。

特に、次のような場面で便利です。

用途内容
テスト用のスタブその場だけの簡単な実装を作れる
インターフェースの簡易実装小さな実装をすぐに渡せる
一時的な補助オブジェクト名前付きクラスを作らずに済む
状態を持つ小さな処理クロージャより整理しやすい

ただし、無名クラスは再利用しにくく、処理が長くなると読みにくくなります。

そのため、重要なビジネスロジックや複数箇所で使う処理には、通常の名前付きクラスを使う方が適しています。

無名クラスは、名前付きクラスとして切り出すほどではない、その場限りの小さな実装を作るための機能です。

使いどころを見極めれば、テストコードや一時的な実装をすっきり書ける便利な機能です。

以上、PHPの無名クラスについてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次