PHP - - By Younes Rafie

What’s New and Exciting in PHP 7.1?

This article was peer reviewed by Thomas Punt. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!


The PHP community has gone viral with the latest PHP 7 announcement and all the goodies it brings to the language. The PHP 7.1 release has more good news and features to offer. This article highlights the most important ones, and you should check PHP RFC for the full list.

Flying baloon-tied elephpant

ArgumentCountError Exception

Earlier versions of PHP allowed for function calls with fewer arguments than specified, emitting a warning when you have a missing argument.

// PHP 5.6

function sum($a, $b)
{
    return $a + $b;
}

sum(); 
// Warning: Missing argument 1 for sum()
// Warning: Missing argument 2 for sum()

sum(3);
// Warning: Missing argument 2 for sum()

sum(3, 4);

Warnings are not useful for this case, and the developer should check if all arguments are set correctly. In PHP 7.1, those warnings have been converted into an ArgumentCountError exception.

// PHP 7.1

function sum($a, $b)
{
    return $a + $b;
}

sum(); 
// Fatal error: Uncaught ArgumentCountError: Too few arguments to function sum(), 0 passed in /vagrant/index.php on line 18 and exactly 2 expected in /vagrant/index.php:13

sum(3); // skipped

sum(3, 4); // skipped 

Nullable Types

PHP 7 added type declarations for parameters and return types, but it seemed that something was missing! Nullable types enable for the specified type to take either that type or null. Here’s an example:

function sum(int $a, int $b): ?int
{
    return $a + $b;
}

The above function may return an integer or a null value, so if something wrong happens in the function’s logic you won’t be returning null as the wrong type. You can do the same for arguments, too!

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

    return $a + $b;
}

The unexpected thing is that calling the function without arguments will throw an exception!

var_dump(sum(null, null)); // NULL
var_dump(sum()); // throw ArgumentCountError exception

This means that you should explicitly specify arguments when they don’t have a default value.

Another thing you should keep in mind is that when overriding or implementing methods, you can’t add the nullable type to the return type, but you’re allowed to remove it. And the reverse is true for arguments, you can’t remove the nullable type from arguments, but you’re allowed to add it!

interface Fooable {
    function foo(): ?Fooable;
}
interface StrictFooable extends Fooable {
    function foo(): Fooable; // valid
}

interface Fooable {
    function foo(): Fooable;
}
interface LooseFooable extends Fooable {
    function foo(): ?Fooable; // invalid
}

Destructuring Arrays

We used to do the following when destructuring arrays.

list($a, $b, $c) = [1, 2, 3];
var_dump($a, $b, $c); // int(1) int(2) int(3)

But trying this would fail, because we couldn’t specify keys to extract and the function tried to use index keys.

list($a, $b, $c) = ["a" => 1, "b" => 2, "c" => 3];
var_dump($a, $b, $c); // NULL NULL NULL

This RFC provides more control over array destructuring. The above code could be changed to this.

list("a" => $a, "b" => $b, "c" => $c) = ["a" => 1, "b" => 2, "c" => 3];
var_dump($a, $b, $c); // int(1) int(2) int(3)

The list function now accepts keys for array destructuring, and because this is used a lot, a new compact syntax has been introduced. Take this as an example.

["a" => $a, "b" => $b, "c" => $c] = ["a" => 1, "b" => 2, "c" => 3];
var_dump($a, $b, $c); // int(1) int(2) int(3)

Cool, isn’t it? And it also works on multidimensional arrays.

[[$a, $b], [$c, $d]] = [[1, 2], [3, 4]];
var_dump($a, $b, $c, $d); // int(1) int(2) int(3) int(4)

[["b" => $b], ["c" => $c]] = [["a" => 1, "b" => 2], ["c" => 3, "d" => 4]];
var_dump($b, $c); // int(2) int(3)

Iterable Type

The iterable pseudo-type is added to join the array primitive type and the Traversable interface which is used to make a value iterable. Take the below code as an example.

// PHP 5.6

function dump(array $items)
{
    var_dump($items);
}

dump([2, 3, 4]);
dump(new Collection());
array(3) {
  [0]=>
  int(2)
  [1]=>
  int(3)
  [2]=>
  int(4)
}

Catchable fatal error: Argument 1 passed to dump() must be of the type array, object given...

But in this case, the function won’t accept an iterable value and will throw an error. This new change lets you use iterable to describe an iterable value instead of asserting it manually.

// PHP 7.1

function dump(iterable $items)
{
    var_dump($items);
}

dump([2, 3, 4]);
dump(new Collection());
array(3) {
  [0]=>
  int(2)
  [1]=>
  int(3)
  [2]=>
  int(4)
}
object(Collection)#2 (0) {
}

Closure From Callable Function

The new fromCallable method provides a performant and compact way of creating Closure objects from callables. Here’s an example:

$callback = Closure::fromCallable([$this, 'fn']);

Void Return Type

This is one of my favorite new features. It completes the return types feature introduced in PHP 7 where functions may not return anything and thus will be forced to return null!

function dump($object): void
{
    var_dump($object);
}

A void function can omit the return statement or add an empty one (return;).

Class Constant Visibility

This is a small change, but an essential part in OOP that enforces encapsulation. It adds visibility modifiers to class constants.

class Post
{
    protected const PUBLISHED = 1;
    protected const DRAFT = 2;
    protected const TRASHED = 3;

    // ...
}

Catching Multiple Exception Types

Other languages like Java provide the ability to catch multiple exception types inside the same catch block, thus removing the need to duplicate code. Take the below code as an example:

// ...

try {
    $user->payMonth($month);
} catch (UserSuspendedException $ex) {
    DB::rollBack();

    return redirect()
            ->back()
            ->withInput()
            ->withErrors([$ex->getMessage()]);
} catch (PaidMonthException $e) {
    DB::rollBack();

    return redirect()
            ->back()
            ->withInput()
            ->withErrors([$ex->getMessage()]);
}

// ...

You can remove the duplication by using the new catch block syntax:

// ...

try {
    $user->payMonth($month);
} catch (PaidMonthException | UserSuspendedException $ex) {
    DB::rollBack();

    return redirect()
            ->back()
            ->withInput()
            ->withErrors([$ex->getMessage()]);
}

// ...

Invalid String Arithmetics

As you start learning PHP, you get an aha moment when doing arithmetics and you learn that strings are allowed! This is because PHP usually deals with the web and values are coming in as strings.

// PHP 5.6

var_dump("1" + "2");
var_dump("1" + "a string");
var_dump("1" + "2  with a string");
int(3)
int(1)
int(3)
// PHP 7.1

var_dump("1" + "2");
var_dump("1" + "a string");
var_dump("1" + "2  with a string");
int(3)

Warning: A non-numeric value encountered in /vagrant/index.php on line 17
int(1)

Notice: A non well formed numeric value encountered in /vagrant/index.php on line 18
int(3)

Now we get a warning if a non numeric value is found, and a notice if the value is not well formatted (2 a string = 2).

Conclusion

This list does not cover all the new changes, so be sure to check the RFC list for the full list. If you have a question or comment about the new PHP 7.1 features or PHP in general, please post them below!

Sponsors