Skip to content
Anonymous Classes
step 1/6

Reading — step 1 of 6

Learn

~2 min readFunctional Patterns

PHP 7+ supports anonymous classes — classes defined and instantiated in one expression. Useful for one-off implementations of interfaces, simple test doubles, and small inline objects.

Basic syntax

$logger = new class {
    public function log(string $msg): void {
        echo "[LOG] $msg\n";
    }
};

$logger->log('hello');

The class has no name in the program — but a generated one for type-checking purposes.

With constructor arguments

$counter = new class(10) {
    public function __construct(private int $start) {}
    public function next(): int {
        return $this->start++;
    }
};

echo $counter->next();   // 10
echo $counter->next();   // 11

Arguments after new class are passed to the constructor.

Implementing interfaces

Commonly used to satisfy a small interface inline:

interface Greeter {
    public function greet(string $name): string;
}

function useGreeter(Greeter $g, string $name): void {
    echo $g->greet($name) . "\n";
}

useGreeter(new class implements Greeter {
    public function greet(string $name): string {
        return "Hi, $name!";
    }
}, 'Ada');

Great for tests where you don't want a top-level mock class.

Extending classes

abstract class Shape {
    abstract public function area(): float;
}

$square = new class(5.0) extends Shape {
    public function __construct(private float $side) {}
    public function area(): float {
        return $this->side ** 2;
    }
};

echo $square->area();   // 25

When to use them

Good fits:

  • Inline test doubles
  • Small adapters / decorators that only one place needs
  • Implementing a method-rich interface for one specific call site
  • Wrapping a single closure in object form

Bad fits:

  • Anything that needs a name elsewhere — debugging shows generated names like class@anonymous
  • Anything used in more than one place — extract to a regular class
  • Things that need a stable serialize/unserialize identity

Common mistakes

  • Using anonymous classes for things that need names — error messages show class@anonymous and the file/line, hard to read.
  • Reaching for them when a closure would suffice — if you only need one method, fn(...) => ... or a closure is lighter.
  • Trying to typehint the type — there's no name to use, so you typehint the interface it implements.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…