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.
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.
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"];
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;
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.
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;
}
}
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.
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.
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 :)