Anonymous Classes

PHP 7 introduces anonymous classes, which have similar advantages in object-oriented code. To understand the benefits of this feature, we have to look at a rather complicated example, however. Let us assume we have a vehicle that can be accelerated. We make Vehicle an abstract class and leave it up the concrete implementation how (fast) the acceleration will be:

abstract class Vehicle
{
    private float $speed;

    public function __construct(float $speed)
    {
        $this->speed = $speed;
    }

    public function getSpeed(): float
    {
        return $this->speed;
    }

    protected function setSpeed(float $speed): void
    {
        $this->speed = $speed;
    }

    abstract public function accelerate(): void;
}

To create an actual vehicle, we must extend Vehicle and implement an accelerate() method in the subclass. In this example, by the way, the new scalar type declarations might be really handy. But then again, a value object might even be a better choice.

When writing a unit test (which we hope you always do), we have to create a subclass, since we cannot instantiate an abstract class directly:

use PHPUnit\Framework\TestCase;

final class VehicleTest extends TestCase
{
    public function testCanAccelerate(): void
    {
        $vehicle = new TestVehicle(10);

        $vehicle->accelerate();

        $this->assertSame(11.0, $vehicle->getSpeed());
    }
}

final class TestVehicle extends Vehicle
{
    public function accelerate(): void
    {
        $this->setSpeed($this->getSpeed() * 1.1);
    }
}

In this example, we have manually created a test stub TestVehicle. We could have used PHPUnit’s test double functionality, but this would not have given us shorter, or more readable code. Now should we put the class TestVehicle into the same file as the test itself (which most coding standards forbid), or into a separate file? This would create an external dependency to the test that needs to be autoloaded, which would require us to write an autoloader not just for our production code, but also for the test stubs. We would, of course, have to keep those autoloaders separate, since the unit tests and stubs are not production code.

While all this can be done, it is additional work that can be avoided by using an anonymous class:

use PHPUnit\Framework\TestCase;

final class VehicleTest extends TestCase
{
    public function testCanAccelerate(): void
    {
        $vehicle = new class(10) extends Vehicle {
            public function accelerate(): void
            {
                $this->setSpeed($this->getSpeed() * 1.1);
            }
        };

        $this->assertSame(11.0, $vehicle->getSpeed());
    }
}

Using an anonymous class, we can define our test stub in-place, starting with new class. Anonymous classes, by definition, have no name. You should use them just like anonymous functions, but please do not over-use them. There is a reason why most coding standards encourage one class per file, and short classes. Cluttering up a number of nested anonymous classes will certainly not make your code more readable.

Using the Reflection API, you can find out whether an object is an instance of an anonymous class:

$reflectionClass = new ReflectionClass(new stdClass);
var_dump($reflectionClass->isAnonymous());

$reflectionClass = new ReflectionClass(new class {});
var_dump($reflectionClass->isAnonymous());

The result, unsurprisingly, is:

bool(false)
bool(true)