In PHP, what is an anonymous class created with new class, and when is it a better fit than a named class?
answer
- declared and instantiated in one expression
- constructor args, extends, implements, use
- one class per declaration site
- no access to the outer class's privates
- readonly anonymous classes since 8.3
basics
~20 sAn anonymous class is declared and instantiated in one new class expression; it can take constructor arguments, extend, implement and use traits. It suits one-off implementations such as test doubles or small adapters, not reusable types.
solid answer
~50 s`new class(...) extends Base implements Iface { ... }` declares a class without a name and immediately creates an instance. It takes the same building blocks as a named class: constructor arguments, a parent class, interfaces and traits. Each declaration site is **one class**, so every object created by the same expression shares a class, and the engine-generated name (starting with the parent or first interface name, then `@anonymous`) is an implementation detail you must not rely on. An anonymous class nested in a method does **not** see the outer class's private or protected members; pass values through its constructor. Since PHP 8.3 it can be `readonly`. Use it where a type is needed exactly once: a test double implementing an interface, a small adapter, or a listener. Prefer a named class when the type is reused, named in type declarations, or must be serialized, since anonymous classes cannot be.
code
php · 24 lines<?php
declare(strict_types=1);
interface QuoteClock
{
public function now(): DateTimeImmutable;
}
function isExpired(DateTimeImmutable $validUntil, QuoteClock $clock): bool
{
return $clock->now() > $validUntil;
}
$fixed = new readonly class(new DateTimeImmutable('2026-10-02')) implements QuoteClock {
public function __construct(private DateTimeImmutable $at) {}
public function now(): DateTimeImmutable
{
return $this->at;
}
};
var_dump(isExpired(new DateTimeImmutable('2026-10-01'), $fixed)); // bool(true)
var_dump((new ReflectionClass($fixed))->isAnonymous()); // bool(true)go deeper
Recall the new class syntax and that it can take constructor arguments, extend a class and implement interfaces.
Explain that each declaration is one class, that the generated name is not reliable, and that it has no access to the outer class's private scope.
Judge when an anonymous class keeps a test or adapter simple and when serialization, reuse or naming needs push it into a named class.
Set expectations for where anonymous classes are acceptable in production code versus tests, balancing locality against discoverability and tooling.
## What the syntax does An **anonymous class** is a class declaration without a name, written directly inside a `new` expression: ```php $notifier = new class($mailer) implements QuoteNotifier { public function __construct(private Mailer $mailer) {} public function quoteExpired(Quote $q): void { /* ... */ } }; ``` The expression declares the class and returns an instance of it. Everything after `class` works as in a named class: - **constructor arguments** in the parentheses after `class`; - `extends` one parent and `implements` any number of interfaces; - `use` of traits, constants, static members, typed and promoted properties; - the `readonly` modifier, since **PHP 8.3** (`new readonly class(...) { ... }`). ## One class per declaration The engine compiles each anonymous class declaration **once**. Every time the expression runs you get a new **instance** of the same class, so `get_class()` returns the same name for all of them. That name is generated: it starts with the parent class name, or the first interface name, or `class`, followed by `@anonymous` and an internal suffix (file, line, counter). The manual calls the name an implementation detail; never compare against it. To detect one, `ReflectionClass::isAnonymous()` returns `true`. ## Scope rules An anonymous class written inside a method is **not a closure**. It does not capture variables, and it has **no access** to the private or protected members of the class it is written in. The documented options: 1. pass values in through its constructor; 2. extend the outer class to reach its protected members. `$this` inside the anonymous class refers to the anonymous instance, never to the outer object. ## When it fits — and when it does not | Situation | Anonymous class | Named class | |---|---|---| | Test double for one interface in one test | good fit | fine, but more files | | Small adapter used at a single call site | good fit | fine | | Type reused in several places | no | yes | | Needs to appear in a type declaration or `instanceof` by its own name | no, it has no usable name | yes | | Must be serialized (queues, sessions, caches) | no, serialization is refused | yes | | Registered by name in a container or configuration | no | yes | Typical good uses: - **Hand-written test doubles**: an in-memory `QuoteRepository` inside a single test, without a mocking library. - **Inline strategy objects** passed to a function that expects an interface. - **Stubs in examples and scripts**, where a separate file adds noise. The costs are readability and reuse. A long anonymous class buried in a method hides a real type; stack traces and logs show a generated name; and anything that needs to name the type, store it or serialize it needs a named class instead. Serializing an instance fails with `Serialization of 'class@anonymous' is not allowed` (or the parent's name in place of `class`). ## Anonymous classes versus closures and stdClass - A **closure** is a single callable and can capture variables with `use` or arrow-function auto-capture. An anonymous class is a full type: it can implement an interface with several methods and hold typed state. - A **stdClass** is a data bag with no methods or type. An anonymous class has behaviour and can satisfy an interface type declaration. ## A quick decision checklist 1. Is the type used at exactly one site? If not, name it. 2. Will anything store, queue or cache the object? If so, name it. 3. Does the body fit on one screen? If not, a named class is easier to read and to find. 4. Will on-call engineers need a meaningful class name in stack traces? Then name it. When all four answers favour locality, an anonymous class keeps the throwaway type next to its only use and saves a file nobody else would open. ## Summary An anonymous class is a named class minus the name: same features, one class per declaration site, no captured scope. It is the right tool when an interface needs exactly one throwaway implementation, and the wrong one as soon as the type needs a name.
- Inside a method of `QuoteService`, can an anonymous class read `$this->secret`, a private property of `QuoteService`?No. An anonymous class does not inherit the enclosing class's scope, and `$this` inside it is the anonymous instance. Pass the value through the anonymous class's constructor, or extend the outer class if protected members are enough.
- If a factory method returns `new class {}` twice, are the two objects instances of the same class?Yes. The class is declared once per declaration site, so both calls produce instances of one class and `get_class()` returns the same generated name. They are still two different instances, so `===` between them is false.
saying these in an interview costs you the question
- Every evaluation of new class creates a brand-new class.
- An anonymous class can read the private properties of the class it is written in.
- Anonymous classes cannot have constructors or implement interfaces.
- The generated class name is stable and safe to compare against.
- Anonymous class instances serialize like any other object.