PHP 7.1: a few bc-breaks and conclusion

September 16, 2016php, php-7.1, engligh

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


To conclude this series of posts about PHP 7.1 I started two weeks ago, I will list a few points that could, especially if your code is a bit legacy, slow down upgrading.


A few BC-breaks?

In the mind of many of us, a minor version should not bring any change that breaks compatibility: code running on PHP 7.0 should work the same way on PHP 7.1 and this new version should only bring new features and bug fixes.

This principle is also written down in the Release Process RFC:

Backward compatibility must be respected with the same major releases, for example from 5.2 to 5.6.


Still, we often consider as OK for a minor version to add new E_DEPRECATED or E_NOTICE warnings, to highlight features that will be removed in the next major version or points that could be linked to bugs in our code — I even wrote about this yesterday.


That being said, behavior of some things — quite specific and rarely used — will change with PHP 7.1. I’ll list them here, as they could be of interest if your code was depending on those.


Warn about invalid strings in arithmetic

PHP is a weakly typed language, and we can do numerical calculations on strings: they are converted to integers, following the rules specified in the manual. For example, "10 pommes" can be interpreted as the number 10.

This means the following piece of code actually adds two numerical values:

var_dump('10 apples' + '5 oranges');
// int(15)


This approach was fitting well in PHP’s mindset some years ago, but is now often thought as surprising.

PHP 7.1 still accepts this syntax, but will generate a E_NOTICE warning each time such a conversion is done automatically, to get the developer’s attention:

// Notice: A non well formed numeric value encountered in .../01-warn-invalid-string-arithmetic.php on line 7
// Notice: A non well formed numeric value encountered in .../01-warn-invalid-string-arithmetic.php on line 7

If you really want this kind of conversion, you can still use explicit type-casting like (int)"10 apples".


Another case has been changed for PHP 7.1, when a string is converted to a numeric while it didn’t contain one:

var_dump(10 + "plop");
// int(10)

Instead of a E_NOTICE, this case, more surprising and probably caused by an error, now raises a E_WARNING warning:

// Warning: A non-numeric value encountered in .../01-warn-invalid-string-arithmetic.php on line 12

Note this is a warning and not a TypeError exception, which would have a much higher impact on compatibility.

‣ The RFC: Warn about invalid strings in arithmetic


Fix inconsistent behavior of $this variable

Up to PHP 7.0, we could in some situations redefine the $this variable, using a variable variable:

class MaClasse
{
    public function method()
    {
        $var = 'this';
        $$var = "plop !";
        var_dump($this);
        $this->otherMethod();
    }

    public function otherMethod()
    {
        var_dump(__METHOD__);
    }
}

$obj = new MaClasse();
$obj->method();

This portion of code, run with PHP 7.0, leads to the following output:

string(6) "plop !"
string(21) "MaClasse::otherMethod"

The first var_dump() show $this has been redefined and now contains "plop !", but the second var_dump() proves calling $this->otherMethod() still works!


As this behavior is quite surprising, this kind of redefinition of $this is not accepted by PHP 7.1 anymore and will cause an Error exception:

Fatal error: Uncaught Error: Cannot re-assign $this in .../02-fix-inconsistent-this.php:8

‣ The RFC: Fix inconsistent behavior of $this variable


Replace "Missing argument" warning with "Too few arguments" exception

With current versions of PHP, we can call a function, passing it less arguments than it expects:

function my_function($a, $b)
{
    var_dump($a, $b);
}

my_function(10);

Until PHP 7.0, this was causing a E_WARNING warning, and unspecified parameters were set to NULL:

Warning: Missing argument 2 for my_function(),
    called in .../03-missing-argument-exception.php on line 9
    and defined in .../03-missing-argument-exception.php on line 3
int(10)
NULL

As the second parameter is declared in the function’s prototype, we rarely take the time to check if it’s been received — which can lead to strange behaviors.


With PHP 7.1, this warning is transformed to an Error exception; which is much harder to just ignore!

Fatal error: Uncaught Error: Too few arguments to function my_function(),
    1 passed in .../03-missing-argument-exception.php on line 8
    and exactly 2 expected in .../03-missing-argument-exception.php:3

‣ The RFC: Replace “Missing argument” warning with “Too few arguments” exception


Forbid dynamic calls to scope introspection functions

PHP gives us a few functions that manipulate their caller’s scope: extract(), compact(), parse_str(),… They all have access, in read or write mode, to variables defined in the function from which they are called.

For example:

function my_function()
{
    $vars = [
        'a' => 123,
        'plop' => 'Hello',
    ];

    $func = 'extract';
    call_user_func($func, $vars);

    var_dump($a, $plop);
}

my_function();

With PHP 7.0, this portion of code gets us the following output:

int(123)
string(5) "Hello"

Still, with dynamic calls like call_user_func() or the $func() syntax (or other approaches), the way these functions behave is not always clearly defined — I was almost lucky it did what I wanted; here…


With PHP 7.1, dynamic calls to functions that manipulate scope are not allowed anymore and will result in a E_WARNING warning:

Warning: Cannot call extract() dynamically in .../04-dynamic-call-scope-introspection-functions.php on line 11
NULL
NULL

‣ The RFC: Forbid dynamic calls to scope introspection functions


PHP 7.1, conclusion

To conclude this series of almost a dozen posts about PHP 7.1, what can I say?


This second minor version of the PHP 7 branch, which is going to be released about one year after PHP 7.0, brings its share of new stuff — including features I find interesting and will use, like:

  • nullable types
  • The iterable pseudo-type
  • The Closure::fromCallable() method
  • A few small enhancements to syntax and consistency

It is also, clearly, an evolution and not a revolution: this should make migrating from PHP 7.0 quite easy.


Still, I’m a bit sad seeing several bc-breaks, which don’t all seem justified to me for this minor version. Even if they should not have much impact on the applications I’m working on, they might scare less adventurous developers and slow down adoption of this new version.


And, finally, one reminder: take a loot at the Supported Versions page every now and then. You’ll for example notice active support for PHP 7.0 ends in about one year, leaving place to PHP 7.1 ;-)


You liked this post? Spread the word!

This blog has recently been migrated to a static site generator and I haven't had time to setup a commenting solution yet.

With a bit of luck, I'll manage to take care of this in a few weeks ;-)