PHP 7.1: changes to types

September 08, 2016php, php-7.1, english
 This has been written a few years ago and might not be up-to-date…

Cet article est aussi disponible en français.
This is the 4th post in a series about PHP 7.1.


One of the most important changes PHP 7.0 brought us last year was about typing, with the introduction of scalar type-declarations for functions/methods parameters and their return value.

PHP 7.1 adds to those type-declarations, with several points that were missing in the previous version of the language.


Nullable Types

The type-declaration mechanism was suffering one great limitation in PHP 7.0: we couldn’t declare a parameter could be null without making it optional at the same time.

For example, with the following function declaration, the second call was invalid, as null is not an integer:

function fonc01(int $a) {
    var_dump($a);
}

fonc01(100); // int(100)
fonc01(null); // TypeError: Argument 1 passed to fonc01() must be of the type integer, null given

Being able to pass null, often meaning “this is a not-specified value”, was a need expressed a few times when PHP 7.0 was being developed and after its release.

To answer this request, PHP 7.1 introduces nullables types. By positioning a ? at the beginning of a type’s name, like ?int, we indicate the null value is accepted:

function fonc01(?int $a) {
    var_dump($a);
}

fonc01(100); // int(100)
fonc01(null); // NULL

Note using a nullable type doesn’t mean the parameter is optional nor it is null by default: an Error will still be thrown if an expected parameter has not been given when calling the function:

fonc01();
// Uncaught Error: Too few arguments to function fonc01(), 0 passed

This nullables types idea also works for the return value:

function fonc02(?int $a, ?int $b) : ?int {
    if ($a === null || $b === null) {
        return null;
    }
    return $a + $b;
}

var_dump( fonc02(10, 20) ); // int(30)
var_dump( fonc02(10, null) ); // NULL

‣ The RFC: Nullable Types


Void Return Type

In PHP, a function can explicitly return a value, using the return keyword and that value, or not return any value — either using the return keyword alone, or simply not using it at all:

function returns_a_value()
{
    // This function returns a value
    return 42;
}

function returns_nothing()
{
    // This function returns null
    return;
}

function does_not_return()
{
    // This function return null too!
}

Type-declarations, as added to PHP 7.0, allowed us to specify of which type a return value should be, but we couldn’t indicate a function or method must not return anything.

As an answer, PHP 7.1 introduces a new pseudo-type we can use as a return type for a function or method:void. A function using this pseudo-type cannot return any value. This means these two syntax are valid:

function returns_nothing() : void
{
    // This function returns null
    return;
}

function does_not_return() : void
{
    // This function returns null too
}

These two functions don’t return any value and respect the void return type-declaration. On the other way, the following function is invalid:

function returns_a_value() : void
{
    return 42;
}

Here, we are trying to return a value. This violates the void type-declaration, so we’ll get a fatal error:

Fatal error: A void function must not return a value in .../demo-03.php on line 16

Note this error occurs during the script’s compilation and not when it’s being executed: PHP detects, as soon as it compiles the code, the return keyword is used with a value.

‣ The RFC: Void Return Type


Iterable

The third and last addition to PHP’s type-declaration mechanism is a new pseudo-type, called iterable. It regroups all kind of data on which we can iterate: arrays and traversables data, like iterators (and generators, as a consequence).

This keyword is used as any other type-declaration:

function fonc01(iterable $data) {
    foreach ($data as $key => $val) {
        var_dump($val);
        // ...
    }
}

This declaration guarantees we can loop over the received data. For example, we can loop on the content of an array:

fonc01([10, 20, 30]);

In this specific case, we already had the array type-declaration. But, contrary to iterable, it didn’t allow us to give an instance of object implementing the Iterator interface (which, in turns, extends Traversable):

fonc01(new SplFixedArray(5));

Of course, as a generator is an iterator, we can also use one here:

function my_generator() {
    yield 100;
    yield 200;
    yield 300;
}

fonc01(my_generator());

For an example that doesn’t work, as we cannot loop over a string, the iterable type-declaration will reject any data of that kind:

fonc01("plop");
// TypeError: Argument 1 passed to fonc01() must be iterable, string given

With this new pseudo-type, PHP 7.1 has an answer to a frequently expressed need: being able to specify an iterable piece of data is expected, but accepting both an array or an object implementing Traversable.

‣ The RFC: Iterable