Skip to main content

PHP8 Classes - What's New

·702 words·4 mins·
Table of Contents

In this post I’m going to cover over new functionality in PHP81 pertaining to classes2

Null safe 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 null safe 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 regurgitating 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);
    }
}