profileRyan KesPGP keyI build stuffEmailGithubTwitterLast.fmMastodonMatrix

PHP8 - Union Types

In the spirit of the upcoming PHP 8.0 release3 I've decided to write a few articles about features that I'm looking forward to. In this first part we're going to cover Union Types2. Yes, at long last PHP is getting proper support for Unions!

So what are unions

The tldr version of this, Unions allow you to declare multiple types for arguments, return types and class properties.

Before PHP 8.0

Before PHP8, Union types already existed to some hacky degree.

Nullable types

By prefixing a ? a type could also be nullable

function printMe(?string $foo) {
    echo $foo;
}


printMe('tralala');
printMe(null);

iterable type

Iterables4 are array|Traversable

function foo(iterable $iterable) {
    foreach ($iterable as $value) {
        // ...
    }
}

PHPDoc

Union Types can be declared in PHPDoc5. Of course the downside to this is that PHP will run your code no matter what nonsense you pass

class Example {
    /**
     * @var int|float
     */
    private $foo;

    /**
     * @var int|float $foo
     */
    public function __construct($foo) {
        $this->foo = $foo;
    }

    /**
     * @var int|float bar
     * @return int|float
     */
    public function doSomethingWithMultipleTypes($bar) {
        return ($bar + $this->foo) * 2;
    }
}

$example = new Example(2);
echo $example->doSomethingWithMultipleTypes(3.0); // PHP will run this
echo $example->doSomethingWithMultipleTypes('tralala'); // PHP will also happily run this

PHP 8.0

Implementation example

class Example {
    private int|float $foo;

    public function __construct(int|float $foo) {
        $this->foo = $foo;
    }

    public function doSomethingWithMultipleTypes(float|int $bar): int|float {
        return ($bar + $this->foo) * 2;
    }
}

Type caveats

Void

Void cannot be used with another type as that makes no sense. A void function isn't allowed to return anything

function foo(): void|null {} // This is invalid

false type

False is allowed to be used as a return type to support legacy code. There is no sane reason to do this for new code. This is bad form and imho shouldn't have been included in the spec. The following code is therefore valid:

function tralala(): User|false {}

Inheritance

When extending another class, Union types can be added. The following is valid:

class A{
    public function foo(string|int $foo): string|int {}
}

class B extends A{
    public function foo(string|int|float $foo): string {}
}

However, you can't change types. The following with generate an error:

class A{
    public function foo(string|int $foo): string|int {}
}

class B extends A{
    public function foo(string|float $foo): string|int {}
}

And that's all I have to say about Unions in PHP8.