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)