profileRyan KesPGP keyI build stuffEmailGithubTwitterLast.fmMastodonMatrix

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);
    }
}