Instantiating objects from classes is a pretty straightforward action. We use the new keyword, followed by a class name and possible constructor parameters. The class name part implies the existence of a previously defined class. Though rare, there are cases where classes are only used during execution. These rare cases make it verbose to force a class definition separately when we know that the class is only being used once. To address this verbosity challenge, PHP introduced a new functionality called anonymous classes. While the concept of anonymous classes has been around for quite some time in other languages, PHP only got to it in the PHP 7 release.
The syntax of anonymous classes is pretty straightforward, which is as follows:
$obj = new class() {};
$obj2 = new class($a, $b) {
private $a;
private $b;
public function __construct($a, $b) {
$this->a = $a;
$this->b = $b;
}
};
We use the new keyword , followed by the class keyword, followed by optional constructor parameters, and finally the body of the class packed in curly braces. Both objects are instantiated as a class@anonymous type. The functionality of objects instantiated through anonymous classes is no different from those instantiated via named classes.
Compared to named classes, anonymous classes are pretty much equal, in that, they can pass contractor parameters, extend other classes, implement interfaces, and use traits. However, anonymous classes cannot be serialized. Trying to serialize an instance of an anonymous class, as shown in the following code snippet, throws a fatal Serialization of class@anonymous is not allowed… error.
There are few other caveats to keep in mind when using anonymous classes. Nesting an anonymous class within another class hides the private and protected methods or properties of that outer class. To circumvent the limitation, we can pass the outer class' private and protected properties into an anonymous class constructor, as follows:
interface Salary {
public function pay();
}
trait Util {
public function format(float $number) {
return number_format($number, 2);
}
}
class User {
private $IBAN;
protected $salary;
public function __construct($IBAN, $salary) {
$this->IBAN = $IBAN;
$this->salary = $salary;
}
function salary() {
return new class($this->IBAN, $this->salary) implements Salary {
use Util;
private $_IBAN;
protected $_salary;
public function __construct($IBAN, $salary) {
$this->_IBAN = $IBAN;
$this->_salary = $salary;
}
public function pay() {
echo $this->_IBAN . ' ' . $this->format($this->_salary);
}
};
}
}
$user = new User('GB29NWBK60161331926819', 4500.00);
$user->salary()->pay();
In this strip down User class example, we have a salary method that returns an anonymous class. To showcase the more robust use of anonymous classes, we make it implement the Salary interface and use the Util trait. The Salary interface forces the anonymous class to implement the pay method. Our implementation of pay method requires IBAN and salary member values from the outer class. Since an anonymous class does not allow access to private and protected members of the outer class, we pass those through anonymous class constructors. While the overall example certainly does not reflect notions of a good class design, it does showcase how to bypass the member visibility limitation.
There is also an option for an anonymous class to fetch the private and protected members of the outer class by extending the outer class itself. However, this requires the anonymous class constructor to properly instantiate the outer class; otherwise, we might end up with a warning, such as a missing argument, for User::__construct().
Even though they are namelessly defined, anonymous classes still get an internal name. Using the core PHP get_class method on an instance of an anonymous class, gets us that name, as shown in the following examples:
class User {}
class Salary {}
function gen() {
return new class() {};
}
$obj = new class() {};
$obj2 = new class() {};
$obj3 = new class() extends User {};
$obj4 = new class() extends Salary {};
$obj5 = gen();
$obj6 = gen();
echo get_class($obj); // class@anonymous/var/www/index.php0x27fe03a
echo get_class($obj2); // class@anonymous/var/www/index.php0x27fe052
echo get_class($obj3); // class@anonymous/var/www/index.php0x27fe077
echo get_class($obj4); // class@anonymous/var/www/index.php0x27fe09e
echo get_class($obj5); // class@anonymous/var/www/index.php0x27fe04f
echo get_class($obj6); // class@anonymous/var/www/index.php0x27fe04f
for ($i=0; $i<=5; $i++) {
echo get_class(new class() {}); // 5 x
class@anonymous/var/www/index.php0x27fe2d3
}
Observing these outputs, we see that the anonymous classes created in the same position (function or a loop) will yield the same internal name. Those with the same name return true for the equal (==) operator and false for the identity operator (===), an important consideration in order to avoid potential bugs.
Support for an anonymous classes opens a door to some interesting use cases, such as mocking tests and doing the inline class overrides, both of which, when used wisely, can improve code quality and readability.