PHP 7.0: new features from the not-so-distant past
September 09, 2016 —Cet article est aussi disponible en français.
This is the 5th post in a series about PHP 7.1.
I will publish other posts about PHP 7.1 next week but, to conclude this one, here’s a quick flashback listing some nice features PHP 7.0 brought us last year.
After all, these are available in PHP 7.1 too, and, as many are still using PHP 5, they constitute additional reasons for switching to PHP 7.x ;-)
A couple of new operators
PHP 7.0 introduced two new operators, once again to simplify and shorten some syntax.
Null Coalesce Operator
The first one, ??
, aims to help this kind of construct disappear:
$username = isset($_GET['user']) ? $_GET['user'] : 'nobody';
We can now write that line this way:
$username = $_GET['user'] ?? 'nobody';
This gives the same result as the previous one and will not cause a notice if the variable used as first operand is not defined. The only difference, often an interesting one, is this operand is now evaluated only once.
As isset()
returns false
when data are null
, this operator behaves the same way and we can use it on more complex expressions, like method calls. I’d add it’s lazy-evaluated:
$model = Model::get($id) ?? $default_model;
// With short-circuit
function foo() {
echo "executed!", PHP_EOL;
}
var_dump(true ?? foo());
‣ The RFC: Null Coalesce Operator
Spaceship Operator
The other operator added with PHP 7.0 is a comparison operator, often called “spaceship” because of how it looks:
function order_func($a, $b) {
return $a <=> $b;
}
It returns -1
if its left operand is smaller than the right one, 1
if it’s bigger and 0
if they are equal. Because of this, it is quite useful when we are writing a callback function for sorts, for example with the usort()
function.
It also works when we need to compare arrays:
function order_func($a, $b) {
return [$a->x, $a->y, $a->foo]
<=> [$b->x, $b->y, $b->foo];
}
‣ The RFC: Combined Comparison (Spaceship) Operator
Unicode Codepoint Escape Syntax
If you like using Unicode characters in your strings, this new feature is for you! With PHP 7.0, you can use the \u{xxxx}
syntax to specify a character using its code:
$ php -r 'echo "I \u{2665} PHP!\n";'
I ♥ PHP!
Or, for an example with some other nice characters:
$ php -r 'echo "\u{1F408} \u{1F431} \u{1F638} \u{1F639} \u{1F63A} \u{1F63B} \u{1F63C} \u{1F63D} \u{1F63E} \u{1F63F} \u{1F640} - so cute!!!\n";'
🐈 🐱 😸 😹 😺 😻 😼 😽 😾 😿 🙀 - so cute!!!
(Did you know there were so many ‘cat’ symbol in Unicode?)
‣ The RFC: Unicode Codepoint Escape Syntax
Scalar type-declarations
This is probably the new feature of PHP 7: we can now specify type-declarations (they were previously called ‘type-hints’) with scalar types, and not only with objects / arrays / callables.
Scalar type-declarations for parameters
The different scalar types of PHP can be used for parameters: int
, float
, string
and bool
.
function add(int $a, int $b) {
return $a + $b;
}
PHP is still weakly typed and conversions will automatically be applied when possible. When they are not possible, a TypeError
exception is thrown:
try {
var_dump( add(10, 'abc') );
}
catch (TypeError $e) {
var_dump( $e->getMessage() );
}
In this case, we’d get:
string(148) "Argument 2 passed to add()
must be of the type integer, string given,
called in .../test-01.php on line 12"
‣ The RFC: Scalar Type Declarations
Return type declaration
We can also, with PHP 7, specify a return-type for a function or method, be it scalar or one of the types already accepted by PHP 5 for parameters:
function add(int $a, int $b) : int {
return $a + $b;
}
‣ The RFC: Return Type Declarations
Strict typing
If the weak typing approach is not for you or you are sure of the data-types your code handles, you can enable a strict typing mode for a file:
<?php
declare(strict_types=1);
function add(int $a, int $b) {
return $a + $b;
}
add(10, '20');
Here, as '20'
is not an integer and we are using strict typing mode, a TypeError
will be thrown:
Fatal error: Uncaught TypeError:
Argument 2 passed to add()
must be of the type integer, string given
Basically, when it comes to the distinction between weak and strict typing:
- For parameters, it’s up to the caller’s choice: she is the one knowing if she’s working with weak or strict types.
- For the return value, it’s the callee’s choice: she knowns if the function she wrote uses weak or strict types.
In any case, inside the function, data are of the specified types: the weak or strict mode only determines whether conversions are allowed.
Group use declarations
Now that we are using namespaces everywhere, we often see files beginning with an incredible number of lines looking like this:
use Mon\Espace\De\Nom\ClasseAa;
use Mon\Espace\De\Nom\ClasseBb;
use Mon\Espace\De\Nom\ClasseCc;
use Mon\Espace\De\Nom\Enfant\AutreClasse as ClassDd;
PHP 7 gives us the option to group those use
directives:
use Mon\Espace\De\Nom\ {
ClasseAa, ClasseBb, ClasseCc,
Enfant\AutreClasse as ClassDd
};
We can also use this new syntax for constants and functions:
use Mon\Nom\ {
function fonc01,
const CONST01,
Class01
};
‣ The RFC: Group Use Declarations
Enhancements on error handling
PHP 7.0 comes with two evolutions around error handling.
Internal fatal errors → exceptions
With PHP 7.0, some internal fatal errors are transformed to instances of Error
, which can be caught. For exemple:
$obj = null;
try {
// Ooops !
$obj->methode();
}
catch (Error $e) {
var_dump($e>getMessage());
}
This portion of code would have caused a Fatal Error with PHP 5, ending the execution of the script. It now leads to the following output, showing we entered the catch
block:
string(43) "Call to a member function methode() on null"
This same principle is used for other errors, like when we are using invalid PHP code in another analysis context:
$php = <<<'PHP'
$a = 10 // -- Parse error ! -- //
printf("\$a = %d\n", $a);
PHP;
try {
eval($php);
}
catch (ParseError $e) {
var_dump($e->getMessage());
}
Let me insist: this change, which will help those running PHP programs for a long time (think: daemons), is for now only applied to some fatal errors — and only when they are raised from PHP’s engine.
‣ The RFC: Exceptions in the engine (for PHP 7)
Errors / exceptions hierarchy
Along with this change, the hierarchy of exceptions classes has been re-thought, introducing a new Throwable
interface, shared by all exceptions types.
Usual exceptions still inherit from Exception
, while errors extend Error
— which is a sister-class of Exception
:
interface Throwable
Exception implements Throwable
// All usual exceptions
Error implements Throwable
AssertionError extends Error
ParseError extends Error
TypeError extends Error
This way, the code catching Exception
does not magically also start catching Errors
with PHP 7.0. This ensures the behavior of your code will not change when upgrading from PHP 5 to PHP 7.
‣ The RFC: Throwable Interface
Anonymous classes
Another new feature of PHP 7.0 is anonymous classes. This extends the idea of anonymous functions, existing since PHP 5.3, adding object-related features: inheritance, implementing interfaces…
For example, let’s instantiate an anonymous object and call one of its methods:
$obj = new class ("Monde") {
protected $qui;
public function __construct($qui) {
$this->qui = $qui;
}
public function hello() {
printf("Hello, %s !\n", $this->qui);
}
};
var_dump($obj);
$obj->hello();
We’ll get the following output. We can especially notice the "Monde"
we gave as a parameter to the constructor, and the fact state has been shared between both methods, using the $qui
attribute.
object(class@anonymous)#1 (1) {
["qui":protected]=>
string(5) "Monde"
}
Hello, Monde !
A typical use case would be a callback dealing with two options, like a success()
and a failure()
method, only using one object implementing the corresponding interface — and not using two separate functions anymore.
‣ The RFC: Anonymous Classes
Evolutions regarding generators
Generators are still a young feature of PHP, as they’ve been added with PHP 5.5 and the community and developers only begin thinking about more advanced way to use them.
‣ If you’d like to know more about generators, here’s a series of posts on the matter, written by several members of the community a few months ago: Generators’ week: here we go!
PHP 7.0 brought two new features to generators.
Generators and return
It previously wasn’t possible to return a value from a generator. PHP 7.0 changed that:
function plop() {
yield 100;
yield 200;
return 42;
}
foreach (($generator = plop()) as $val) {
var_dump($val);
}
var_dump( $generator->getReturn() );
This would get us the following output, with values dumped from the loop, and then the return value:
int(100)
int(200)
int(42)
How can this help? We can for example think of a function that would generate intermediary values, finally returning some kind of total calculated from these.
‣ The RFC: Generator Return Expressions
Generator Delegation
The yield
keyword has been reworked and we can now use yield from
to generate values from a traversable (with yield
alone, we would have generated only one thing: the traversable itself):
function test() {
yield from [10, 20, 30];
}
foreach (test() as $val) {
var_dump($val);
}
Here, we use yield from
to generate the three values contained in the array, and get the following output:
int(10)
int(20)
int(30)
As a generator is traversable, we can also use yield from
to generate values from other ‘sub-generators’ — hence the idea of ‘delegation’:
function sub_generator_1() {
yield 10;
yield 20;
}
function sub_generator_2() {
yield from [ 'aa', 'bb', ];
}
function delegating_generator() {
yield from sub_generator_1();
yield from sub_generator_2();
}
foreach (delegating_generator() as $val) {
var_dump($val);
}
Here, we’d get:
int(10)
int(20)
string(2) "aa"
string(2) "bb"
This is especially interesting when we need to aggregate data coming from several sources — for instance, to load data from a database (using a first generator) and a CSV file (using another generator).
‣ The RFC: Generator Delegation
What else?
Of course, I have not listed here all new features of PHP 7.0 and only wrote about those I noticed the most. Your turn to play with them now ;-)
And see you next week for more posts about PHP 7.1!