PHP 7.1 was released 9 months ago so it’s time to look at upcoming PHP 7.2 release. At the moment of writing this article PHP 7.2 is in Beta 3 version Looking at releases of previous versions and release plan for PHP 7.2 I think we should expect the final release until the end of year, probably at the beginning of December 2017.
Backward incompatible changes
Trait properties bug was fixed
I will start with the bug I’ve reported a few months ago which I found when I was learning to Zend Certified Engineer exam . The testing code is like this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
trait PropertiesTrait { public $same = true; public function message() { if ($this->same === true) { return "A"; } return "B"; } } class PropertiesExample { use PropertiesTrait; public $same = 2; } $example = new PropertiesExample(); echo $example->message(); |
Looking at documentation http://php.net/manual/en/language.oop5.traits.php#language.oop5.traits.properties you can read:
If a trait defines a property then a class can not define a property with the same name unless it is compatible (same visibility and initial value), otherwise a fatal error is issued.
So we would expect we get a fatal error in such situation. However in both PHP 7.0 and PHP 7.1 as output we are getting:
1 |
B |
and we are not getting fatal error. This is because non-strict comparison was done here and 2 == true. If we changed 2 to 0, we would get fatal error right away.
In PHP 7.2 this behaviour was fixed and it’s now working as we would expect looking at PHP documentation . Now strict comparison is done, so when running script in PHP 7.2 you will get fatal error like this:
1 |
<b>Fatal error</b>: PropertiesExample and PropertiesTrait define the same property ($same) in the composition of PropertiesExample. However, the definition differs and is considered incompatible. |
Casting array to object change
When we want to cast array to object and we use the following code snippet
1 2 3 4 |
$array = ['foo','bar','sample_key' => 'baz']; $object = (object) $array; echo $object->sample_key.' '; echo $object->{0}; |
in PHP 7.1 we will get the following output:
1 2 |
baz <b>Notice</b>: Undefined property: stdClass::$0 |
So in fact we cannot access foo or bar values because in array they have integer keys. However output for the same snippet in PHP 7.2 would be:
1 |
baz foo |
So as you see since PHP 7.2 we can access those values without any problem. Also notice that we cannot just use:
1 |
echo $object->0; |
to display such property. We need to add curly brackets.
Casting object to array change
Very similar thing was done when casting object to array. Let’s look at the following code snippet:
1 2 3 4 5 6 |
$object = new stdClass(); $object->{0} = 'foo'; $object->bar = 'baz'; $array = (array) $object; var_dump($array); echo $array[0]; |
Running this code in PHP 7.1 you will get:
1 2 3 |
array(2) { ["0"]=> string(3) "foo" ["bar"]=> string(3) "baz" } <b>Notice</b>: Undefined offset: 0 in ... |
As you see when we cast object to array in array we are getting key with string 0, and we cannot access it. We can try both $array[0] or $array[‘0’] but in both cases we will get the notice.
However in PHP 7.2 the following piece of code will output
1 |
array(2) { [0]=> string(3) "foo" ["bar"]=> string(3) "baz" } foo |
So as you see, now in array we have integer key 0 and we can access value without any problem.
Changes for unquoted strings
Let’s look at this code snippet:
1 2 3 4 5 6 7 |
$string = test; if ($string == 'test') { echo "This is test string"; } |
Obviously you can see unquoted string test here. In PHP 7.1 output of the script would be:
1 2 |
<b>Notice</b>: Use of undefined constant test - assumed 'test' in ... This is test string |
but in PHP 7.2 the output would be:
1 2 |
<b>Warning</b>: Use of undefined constant test - assumed 'test' (this will throw an Error in a future version of PHP) in ... This is test string |
As you see it’s not a big change (Notice was changed into Warning), however in PHP 8 it will probably cause an error.
The main cause of this change is not stopping just support for unquoted string but to avoid serious problems when you typo some PHP keywords.
For example look at the following code snippet:
1 2 3 4 5 6 7 8 9 |
error_reporting(0); $numbers = [5, 0, 3]; foreach ($numbers as $index => $number) { if ($number < 2) { contiue; } $numbers[$index] = $number + 3; } var_dump($numbers); |
As you see displaying errors was turned off. What will $numbers array contain? Surprisingly the output of the script is:
1 |
array(3) { [0]=> int(8) [1]=> int(3) [2]=> int(6) } |
and we would expect rather
1 |
array(3) { [0]=> int(8) [1]=> int(0) [2]=> int(6) } |
This is because of typo in continue keyword and in real application this typo could cause serious problems. PHP 7.2 won’t help to avoid such problems but PHP 8 should.
Looking from other perspective, how often do you have such typos in your code nowadays, when most of us are using IDE like for example PhpStorm. And we all ave unit and integration tests for everything, right?
Object keyword not for class name
What is quite surprising for me, in PHP 7.1 the following code snippet would work without any problem:
1 2 3 4 5 6 |
class object { public $foo = 'bar'; } $object = new object(); echo $object->foo; |
However in PHP 7.2 it won’t work any more as object keyword cannot be used any more as class name. So in PHP 7..2 you will now get:
Fatal error: Cannot use ‘object’ as class name as it is reserved in …
Output of json_decode for object as array
Another changes was made json_decode. If you use the following code snippet:
1 2 |
$string = json_encode(['foo' => 'bar']); var_dump(json_decode($string, null, 512, JSON_OBJECT_AS_ARRAY)); |
in PHP 7.1 as output you will get:
1 |
object(stdClass)#1 (1) { ["foo"]=> string(3) "bar" } |
but in PHP 7.2 as output you will get:
1 |
array(1) { ["foo"]=> string(3) "bar" } |
This is because now, json_decode respects JSON_OBJECT_AS_ARRAY also in case when 2nd argument is set to null
New features
Parameter Type Widening
Let’s start with code snippet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class Vendor { function sum($numbers) { return array_sum($numbers); } } class Custom extends Vendor { function sum($numbers) { return array_sum((array)$numbers); } } class Data { public $number = 1; public $number2 = 2; public $number3 = 3; } echo (new Custom())->sum([1, 2, 3]) . ' '; echo (new Custom())->sum(new Data()); |
So we have some Vendor class (that represents 3rd party library) and Custom class that was created only to extend functionality of sum method. The sum method in Vendor class just sums array of numbers. However we wanted also to be able to pass some object that we can convert to array to sum, for example our Data structure.
So far so good, in both PHP 7.1 and PHP 7.2 the output will be:
1 |
6 6 |
as expected. However let’s assume Vendor class was changed a bit by 3rd party. Type hinting was added to this method and now this class looks like this:
1 2 3 4 5 6 7 |
class Vendor { function sum(array $numbers) { return array_sum($numbers); } } |
The problem is that in PHP 7.1 the signature should be exactly the same. So when you updated the Vendor class and the method sum has now type-hinted $numbers, in all child classes it should be also added like this, otherwise warning will be raised:
1 2 |
<b>Warning</b>: Declaration of Custom::sum($numbers) should be compatible with Vendor::sum(array $numbers) in ... 6 6 |
So in PHP 7.1 you need to decide – either you won’t use new version of Vendor library or you need to adjust your code. Obviously in above case it’s not just adding type hinting to $numbers parameter in Custom class. Now, because it’s type hinted, you cannot pass Data object any more. So you need to change also they way you pass data into sum method.
Fortunately in PHP 7.2 you won’t have this problem any more. No warning will be raised and code will work without any problem. So you can update 3rd party library and you can adjust your code whenever you want to have exact same signature for class methods as parent classes. It seems as quite a big improvement and will allow to add type hinting to commonly used Composer packages without worries that it will break thousands of applications when they will update to newer version.
Abstract class parameter type widening
In similar way as parameter type widening, you might wanted to change abstract class signature a bit. Let’s look at sample code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
abstract class Vendor { abstract function sum(array $numbers); } abstract class AbstractCustom extends Vendor { abstract function sum($numbers); } class Custom extends AbstractCustom { function sum($numbers) { return array_sum((array)$numbers); } } class Data { public $number = 1; public $number2 = 2; public $number3 = 3; } echo (new Custom())->sum([1, 2, 3]) . ' '; echo (new Custom())->sum(new Data()); |
As you see in AbstractCustom class you remove type hinting from sum class and then you implement this in different way in your class. In PHP 7.2 this code will work now without any problem, but in PHP 7.1 and before you cannot repeat the same abstract method in child abstract class, so you was not able to widen parameter type at all. When running above code in PHP 7.1 you will get:
1 |
<b>Fatal error</b>: Can't inherit abstract function Vendor::sum() (previously declared abstract in AbstractCustom) in ... |
Probably this is smaller improvement (it won’t be used so often) I think however it’s again one extra thing that will be possible to do in PHP 7.2 and in case you need it you won’t need to create some workarounds or duplicate code just to make it work.
Group use statements trailing comma
Probably many developers use trailing comma in multiline arrays like so:
1 2 3 4 |
$array = [ 'foo', 'bar', ]; |
It helps adding new elements to arrays and also make diff smaller in case you add any new element. Using the same logic PHP 7.2 now supports trailing comma for group use statements like this:
1 2 3 4 5 6 |
use Test\{ Foo, Bar, }; <span class="s8">$foo </span><span class="s2">= </span><span class="s1"><b>new </b></span><span class="s2">Foo(); </span><span class="s8">$bar </span><span class="s2">= </span><span class="s1"><b>new </b></span><span class="s2">Bar();</span> |
The following code will work without any problem in PHP 7.2 and will allow to easy import any new class from Test namespace. Obviously this is not possible in PHP 7.1 and before.
Object type-hinting
As of PHP 7.2 we can now use object keyword in type-hinting function arguments or return value. So for example we can use now the following code:
1 2 3 4 5 6 7 |
function fill(array $data, object $object): object { foreach ($data as $k => $v) { $object->$k = $v; } return $object; } var_dump(fill(['foo' => 'bar', 'baz' => 'test'], new stdClass())); |
and it will return
1 |
object(stdClass)#1 (2) { ["foo"]=> string(3) "bar" ["baz"]=> string(4) "test" } |
in PHP 7.2. In PHP 7.1 and earlier as object was not restricted keyword and it could be used as class name it was not possible and running above code will raise error:
1 |
<b>Fatal error</b>: Uncaught TypeError: Argument 2 passed to fill() must be an instance of object, instance of stdClass given, called in ... |
Sodium extension as part of PHP core
In PHP 7.2 new cryptographic Sodium extension was added to PHP core. What we can read about this library on web page:
Sodium is a modern, easy-to-use software library for encryption, decryption, signatures, password hashing and more.
It is a portable, cross-compilable, installable, packageable fork of NaCl, with a compatible API, and an extended API to improve usability even further.
Its goal is to provide all of the core operations needed to build higher-level cryptographic tools.
PHP 7.2 is probably the first programming language that will add this library to core . I’m not cryptography expert but Sodium seems to be library that is highly ranked by people who deal with security, so it is really good to have it on board in PHP 7.2
Other changes
There are many other small changes. For example each function has been deprecated and will be completely removed in future (to be honest I’ve never used it and it’s a lot slower than foreach), __autoload has been deprecated and spl_autoload_register should be used instead (but it’s not a huge change – probably you either use composer and you don’t care about manual autoloading or you already used spl_autoload_register). Also some session changes were made, but again if you use PHP in normal way, you should probably don’t care about those changes at all. If you develop/run PHP application in Windows, you must to notice also the minimum supported Windows versions are now Windows 7 and Windows Server 2008 R2.
Summary
To sum up, there are not many big changes in PHP 7.2. One of the most important is probably adding new cryptographic extension (Sodium) so now we have alternative to OpenSSL. Others are rather fixes and small improvements. Obviously if you are running legacy code and want to upgrade to PHP 7.2 many of small changes might be a problem but if you don’t probably you shouldn’t expect too many issues with migration.