PHP8 Classes - What's New
In this post I'm going to cover over new functionality in PHP81 pertaining to classes2
Nullsafe operator3
Let's say you want to call a method or fetch a property on a result that may be null:
class Bar
{
public function excellent(): string
{
return "excellent";
}
}
class Foo
{
public function getBarOrNull(): ?Bar
{
return rand(0, 1) === 1 ? new Bar() : null;
}
}
$foo = new Foo();
echo $foo->getBarOrNull()->excellent();
There's a 50/50 chance that the above code with cause an error.
Pre php8 solution
class bar
{
public function excellent(): string
{
return "excellent";
}
}
class foo
{
public function getBarOrNull(): ?bar
{
return rand(0, 1) === 1 ? new bar() : null;
}
}
$foo = new foo();
$barOrNull = $foo->getBarOrNull();
echo $barOrNull ? $barOrNull->excellent() : "This is printed when getBarOrNull() returns null";
php8 solution
PHP8 has added a nullsafe operator, huzzah!
class bar
{
public function excellent(): string
{
return "excellent";
}
}
class foo
{
public function getBarOrNull(): ?bar
{
return rand(0, 1) === 1 ? new bar() : null;
}
}
$foo = new foo();
echo $foo->getBarOrNull()?->excellent();
If php getBarOrNull()
returns null then php ->excellent()
isn't called and null is returned.
Attributes4
Attributes are a way to add metadata to classes. Stitcher has written great in depth rundown5 about the options (I'm lazy and anything I write here would just be regurgatating the article). The syntax in php8 is as follows:
use App\Attributes\ExampleAttribute;
#[ExampleAttribute]
class Foo
{
#[ExampleAttribute]
public const FOO = 'foo';
#[ExampleAttribute]
public $x;
#[ExampleAttribute]
public function foo(#[ExampleAttribute] $bar) { }
}
#[Attribute]
class ExampleAttribute
{
public $value;
public function __construct($value)
{
$this->value = $value;
}
}
Constructor property promotion6
There's now syntactic sugar to create value & data transfer objects. This is easier to read, saves lines of pointless code and should have been implemented a long time ago.
Pre php8
class Money
{
public Currency $currency;
public int $amount;
public function __construct(
Currency $currency,
int $amount,
) {
$this->currency = $currency;
$this->amount = $amount;
}
}
php8
class Money
{
public function __construct(
public Currency $currency,
public int $amount,
) {}
}
Static return type7
class Foo
{
public function tralala(): static
{
return $this;
}
}
static
guarantees that tralala()
will always create an instance of Foo
, If self
was used a parent class would also have been valid.
Remove inappropriate inheritance signature checks on private methods8
Pre php8 the following code does not compile and generates an Cannot override final method A::finalPrivate()
error:
class A
{
final private function finalPrivate()
{
echo __METHOD__ . PHP_EOL;
}
}
class B extends A
{
private function finalPrivate()
{
echo __METHOD__ . PHP_EOL;
}
}
The PHP people are of the opinion that since this is a private method, that inheritance checks should not be executed. So in php8 the previous code would compile correctly.
Allow php :class on objects9
This is a nice alternative to using get_class()
$foo = new Foo();
var_dump($foo:class)
Better validation for abstract trait methods10
Pre php8 the following is valid:
trait Test {
abstract public function test(int $input): int;
}
class UsesTrait
{
use Test;
public function test($input)
{
return $input;
}
}
PHP8 performs proper validation. The above code has to be rewritten properly to compile:
class UsesTrait
{
use Test;
public function test(int $input): int
{
return $input;
}
}
Stringable interface11
The Stringable interface is automatically added to classes that implement the __toString()
method in php8
class Foo
{
public function __toString(): string
{
return 'foo';
}
}
function bar(string|Stringable $stringable) { /* … */ }
bar(new Foo());
bar('abc');
Weak maps12
Weak maps allow creating a map from objects to arbitrary values (similar to SplObjectStorage) without preventing the objects that are used as keys from being garbage collected. If an object key is garbage collected, it will simply be removed from the map. This will save a lot of headaches for people writing things like ORMs.
class Foo
{
private WeakMap $cache;
public function getSomethingWithCaching(object $obj): object
{
return $this->cache[$obj]
??= $this->computeSomethingExpensive($obj);
}
}
- https://wiki.php.net/todo/php80↩
- https://www.php.net/manual/en/language.oop5.php↩
- https://wiki.php.net/rfc/nullsafe_operator↩
- https://wiki.php.net/rfc/attributes↩
- https://stitcher.io/blog/attributes-in-php-8↩
- https://wiki.php.net/rfc/constructor_promotion↩
- https://wiki.php.net/rfc/static_return_type↩
- https://wiki.php.net/rfc/inheritance_private_methods↩
- https://wiki.php.net/rfc/class_name_literal_on_object↩
- https://wiki.php.net/rfc/abstract_trait_method_validation↩
- https://wiki.php.net/rfc/stringable↩
- https://wiki.php.net/rfc/weak_maps↩