PHP 7.1, the new minor version of PHP is finally here, with a number of new features, changes, and bug fixes. In this article, let's look at some of the awesome features in PHP 7.1. Like,

  • iterable pseudo type
  • Closure from callable
  • Square bracket syntax for list()
  • Support for keys in list
  • Class constant visibility
  • Nullable types
  • Void functions
  • Catching Multiple Exception Types
  • Too few arguments exception

I will also link to the corresponding RFCs for your reference.

Before we start: Would you like to try out PHP 7.1 without messing up the existing environment? Docker is your best friend! PHP 7.1 can be easily set up using Docker with one of these images.

To start a php 7.1 container:

docker run -v /Users/apple/Workplace/php/:/php -it php:7.1-rc bash

This will download (if not locally existing) the docker image for php:7.1-rc, start the container and take you inside the container using bash prompt. -v /Users/apple/Workplace/php/:/php option maps /Users/apple/Workplace/php directory of the host machine to /php directory inside the docker container. So everything inside /Users/apple/Workplace/php will be available in /php directory of the container.

Now we will move inside the /php directory.

cd /php

Cool! We are now ready to try out the new features in PHP 7.1.

1. iterable pseudo type

Let's say we have a function getUserNames, which accepts a list of users and returns their names as a list.

function getUserNames($users)
{
    return nikic\map(function($user){
        return $user->name;
    }, $users); 
}

Note: We are using nikic/iter library here.

The parameter $users could be an array or an object that implements Traversable. Till PHP 7, it was not possible to type hint this function as there is no common type that represents both array and Traversable.

To solve this problem, PHP 7.1 is introducing a new iterable pseudo type, which we can use to type hint anything that can be iterated using foreach.

function getUserNames(iterable $users): iterable
{
    return nikic\map(function($user){
        return $user->name;
    }, $users); 
}

Now this function can accept arrays, iterable objects, and generators.

Read RFC

2. Closure from callable

This feature allows us to create a closure from any callable function. Let's look at an example to understand this.

class MyClass
{
  public function getCallback() {
    return Closure::fromCallable([$this, 'callback']);
  }

  public function callback($value) {
    echo $value . PHP_EOL;
  }
}

$callback = (new MyClass)->getCallback();
$callback('Hello World');

This is probably not a great example to explain why we need this feature but shows how we can use it. There are a couple of advantages of using Closure::fromCallable over the traditional way.

Better error handling - When using Closure::fromCallable, it shows errors in the right place instead of showing it where we are using the callable. This makes debugging a lot easier.

Wrapping scopes - The above example will work fine even if the callback is a private/protected method of MyClass.

Performance - This can also improve the performance by avoiding the overhead of checking if the given callable is actually a callable.

Read RFC

3. Square bracket syntax for list() aka Symmetric array destructuring

Short array syntax was one of the awesome feature introduced in PHP 5.4, which made working with arrays so much easier. Now, short list syntax enables us to use square brackets instead of list(), a construct to assign variables from an array.

//prior to php 7.1
list($firstName, $lastName) = ["John", "Doe"];
// php 7.1
[$firstName, $lastName] = ["John", "Doe"];

Read RFC

4. Support for keys in list

Until PHP7, list() only supported numerically indexed arrays with index starting from zero. We were not able to use it with associative arrays.

Now, this feature allows us to specify keys when destructuring arrays.

$user = [ "first_name" => "John", "last_name" => "Doe"];
["first_name" => $firstName, "last_name" => $lastName]= $user;

As an interesting side effect, now we can assign only the required keys from the array.

["last_name" => $lastName] = $user;

It's also possible to destructure multidimensional arrays by using lists inside list.

$users = [
    ["name" => "John Doe", "age" => 29],
    ["name" => "Sam", "age" => 36]
];
[["name" => $name1, "age" => $age1],["name" => $name2, "age" => $age2]] = $users;

Read RFC

5. Class constant visibility

Class constants can help us to provide a meaningful name for a value used in the class. These constants were always public and could be accessed from outside the class. But sometimes we may not want to expose them to the outside world.

In PHP7.1, we can specify the visibility modifiers to class constants, just as we do it for methods and properties.

class PostValidator
{
    protected const MAX_LENGTH = 100;

    public function validateTitle($title)
    {
        if(strlen($title) > self::MAX_LENGTH) {
            throw new \Exception("Title is too long");
        }
        return true;
    }  
}

Here MAX_LENGHT constant is something internal to the PostValidator class and we don't want the outside world access this value.

Read RFC

6. Nullable types

This feature allows us to type hint a function, but still allow null value as parameters or returns. We can make a parameter or return value Nullable by prepending a question mark(?) to the type hint.

function sayHello(?string $name) {
    echo "Hello " . $name . PHP_EOL;
}
sayHello(null); // Hello
sayHello("John"); //Hello John

Similarly, for nullable return type

class Cookie
{

    protected $jar;

    public function get(string $key) : ?string
    {
        if(isset($this->jar[$key])) {
            return $this->jar[$key];
        }
        return null;
    }

}

Read RFC

7. Void functions

From PHP 7, we can specify the type of value a function should return. Sometimes we may want to specify that the function should not return any value.

class Cookie
{

    protected $jar;

    public function set(string $key, $value) : void
    {
        $this->jar[$key] = $value;
    }
}

When the return type is void, either the function should omit the return statement or can have an empty return statement. It should not return any other value.

Read RFC

8. Catching Multiple Exception Types

When handling exceptions, sometimes we may want to handle multiple exceptions in the same way. Normally we will have multiple catch blocks to handle different types of exceptions.

try{
  // somthing
} catch(MissingParameterException $e) {
  throw new \InvalidArgumentException($e->getMessage());
} catch(IllegalOptionException $e) {
  throw new \InvalidArgumentException($e->getMessage());
}

In PHP 7.1, we can handle multiple exceptions in single catch block

try {
  // something
} catch (MissingParameterException | IllegalOptionException $e) {
  throw new \InvalidArgumentException($e->getMessage());
}

Multiple exceptions are separated using a single 'pipe' symbol. This way we can avoid duplicating same code to handle multiple exception types.

Read RFC

9. Too few arguments exception.

As of PHP 7, we can call a function with lesser arguments than what it is actually required. In which case, it will just show a warning about the argument(s) missing, but will continue the function execution. Sometimes this can cause strange or unexpected behavior of the function.

In PHP 7.1, calling a function without required parameters will trigger an Error exception, ArgumentCountError.

function sayHello($name) {
    echo "Hello " . $name;
}
sayHello();
// Fatal error: Uncaught ArgumentCountError: Too few arguments to function sayHello(), 0 passed in...

Throwing an exception will help us to handle these cases immediately and avoid any unexpected behavior. But this is a backward incompatible change and can break your existing applications. So make sure everything is fine before upgrading in production.
Read RFC

Summary

In this article, we have discussed some of the important features introduced in PHP 7.1. To know more about the new features and changes in PHP 7.1 do check out the official migration guide. Did I miss any important feature? Let me know in comments below :)

Tags : phpphp7.1
blog comments powered by Disqus