DEV Community

Cover image for All the changes that come with PHP 7.4
Carlos Gándara
Carlos Gándara

Posted on • Edited on

All the changes that come with PHP 7.4

PHP 7.4 is available since past 28th of November. This new update for PHP 7 version comes with some nice additions, another minor features and a bunch of deprecations.

In this post, we will review all the changes in the version, one RFC at a time.

The fancy additions

Some awaited features are finally here with 7.4. Following is the list of the ones I think have a bigger impact for the language.

Typed properties

Class properties can be typed.

Here is a quick tour:

class Color {}
class Profession {}

class Guy {
    public string $name;
    private int $id;
    public static int $age;
    public Color $hairColor;
    public ?Profession $profession;
    public string $address = 'Discworld';
    public string $phone;
}

class InheritingGuy extends Guy{
    public int $name; //Error, types are invariant
    public string $id; //Unless the visibility changes from private to a less restrictive one
}

$guy = new Guy();
$guy->name = 'Sherlok'; //Good!
$guy::$age = 42; //Static properties are typed too
$guy::$age = '42'; //If strict_types is enabled this assigment throws a TypeError, if not PHP will try to convert fom string to int
$guy->hairColor = new \DateTimeImmutable(); //Throws TypeError, we can only assign a Color
$guy->profession = null; //Possible because it is nullable
echo $guy->address; //This is ok because it has a value
echo $guy->phone; //Throws an error because value was not initialized
Enter fullscreen mode Exit fullscreen mode

The next natural step in improving the language type system. The above example covers several scenarios when using typed properties, but I strongly recommend to read the RFC to fully understand all the nuts and bolts of typed properties.

Arrow functions

Enable a simpler syntax for anonymous functions.

The syntax removes the need for the use keyword to import variables in the function scope, reducing substantially the size of simple functions. Especially handy for the array_map, filter and reduce methods.

$template = ', I choose you!'.PHP_EOL;

$oldStyle = function($who) use ($template) {
    return $who.$template;
};

$withArrowFunctions = fn($who) => $who.$template;

$name = 'Pikachu';
echo $oldStyle($name);
echo $withArrowFunctions($name);

//Both calls print "Pikachu, I choose you!"
Enter fullscreen mode Exit fullscreen mode

Covariant returns and contravariant parameters

When overriding a method it will be possible to define more specific return types and to define less specific types of parameters.

Covariance and contravariance are tricky to explain, but actually not that hard. So far, in PHP we have invariant parameters and return types. This will change with PHP 7.4. Let's illustrate it with an example:

class MarioEnemy {}
class Koopa extends MarioEnemy {}

class EnemyType {}
class Turtle extends EnemyType {}
class Mushroom extends EnemyType {}

interface MarioEnemyGrabber {
    public function grabEnemyOfType(Turtle $type): MarioEnemy;
}
Enter fullscreen mode Exit fullscreen mode

Previous to 7.4 you cannot extend the interface changing the method's signature because parameters and return types are invariant. With 7.4 we can have more specific return types and less specific parameter types:

//Overriding return type with a more specific one
interface OneKoopaGrabber extends MarioEnemyGrabber {
    public function grabEnemyOfType(Turtle $type): Koopa;
}
//Overridign parameter type with a less specific one
interface AnotherKoopaGrabber extends MarioEnemyGrabber {
    public function grabEnemyOfType(EnemyType $type): MarioEnemy;
}
Enter fullscreen mode Exit fullscreen mode

When this post was published it had an example of covariant parameters, suggesting it would be possible to provide more specific types for parameters when overriding methods. That is not correct and I have removed the example. Thanks to @goceb for pointing it in the comments, you can check there a similar example of what cannot be done.

Null coalescing assignment operator

The null coalescing assignment operator ??= is available.

//Keeps $discount if it is not null, assigns 20 otherwise
$discount ??= 20;

//Before we had to do
$discount = $discount ?? 20;
Enter fullscreen mode Exit fullscreen mode

Another step forward to reduce noise when assigning values that might not be there.

Spread operator in arrays

An array (or a Traversable) can be spread inside another array.

$blueGreen = ['blue', 'green'];
$andAlsoRed = ['red', ...$blueGreen];
//result is ['red', 'blue', 'green']
Enter fullscreen mode Exit fullscreen mode

Note this is not a replacement for array_merge. It can be more handy in some situations, but not usable in others.

Preloading

Allow to preload the application code, removing some overhead of recompilation code in each request. Average performance gain is around 10 to 15%

With OpCache almost all the overhead of PHP runtime compilation is mitigated, but there are still some parts that can be further optimized. This is what preloading is about, with the trade-off of requiring a server restart for any change in the code to be detected and the need for a file defining some classes that must be excluded from preloading, because not all can be.

This is a low-level feature and quite hard to explain in an easy way, at least for my understanding of it (not a system guy or a PHP Core Member). I recommend reading the RFC and watching this talk by Nikita Popov in the past PHP Barcelona Conference, explaining in a somehow accessible way what is the benefit over OpCache and plenty of other interesting stuff, not only preloading related.

Other additions

Here is a list of other less shiny additions to 7.4.

Foreign Function Interface

It is possible to run C code from within PHP.

$ffi = FFI::cdef("here we declare C structs and methods");

//Creating a struct from the C code in the PHP code
$myStruct = new $ffi->new ('struct my_struct'); 
echo $myStruct->some_property;

//Calling functions from the C code in the PHP code
echo $ffi->some_method();
Enter fullscreen mode Exit fullscreen mode

This is complex stuff, aiming for fast prototyping. Some writing on the web claim it will allow to load extensions with Composer instead of installing them via PECL. For what is said in the RFC, it is more a first step that may or may not allow such thing in the future.

Numeric literal separator

An underscore will be supported as number separator.

So we can write 1_000.50 instead of 1000.50. Useful to improve readability of big numbers, ten millions is better to identify as 10_000_000 than as 10000000.

Weak references

A WeakReference will be implemented in the core of the language. Before there were extensions implementing it, but with a number of issues or unusable after PHP 7.3.

To be honest weak references concept is new to me and after searching and reading about it for a while I still don't get for what they are useful. All the examples I can find are basically the same as the one in PHP documentation that looks quite irrelevant to me. If you know a practical use case please share it in the comments.

Allow to throw exception inside __toString() method

Pretty self-explanatory. It was not possible due to some PHP internals weirdness.

Notice for non valid array container

Accessing as an array something that cannot be accessed that way raises a warning.

Until 7.4, when we do so we get NULL and that's it.

$notArrayAccessible = 123;
$notArrayAccessible[0]; //throws a warning, integer cannot be accessed as arrays
Enter fullscreen mode Exit fullscreen mode

Warning when providing invalid input to base_convert()

Invalid input for base_convert and similar functions will raise a warning and the method supports negative values.

The base_convert method changes numbers between bases (decimal, hexadecimal, binary, etc.), along with concrete representation methods like decbin. However, these methods silently ignore invalid input like passing 42 as a binary value.

Include hash extension in the language core

The hash extension will be always enabled.

Improve openssl_random_pseudo_bytes()

Fix an inconsistency with the function openssl_random_pseudo_bytes().

Sometimes calling this function enforces userland checks for errors. Now it will throw an exception and the confusing second parameter is marked as deprecated and will be removed in PHP 8.

str_split() supporting multibyte

The method mb_str_split allows multibyte support for splitting strings.

Reflection for references

The class RefelctionReference class allow detecting references between variables.

Tools requiring this kind of information had to use some hacky techniques. Not anymore.

New custom object serialization mechanism

Two new magic methods __serialize() and __unserialize for object serialization.

Previous serialization mechanism using Serializable interface and __sleep() and __wakeup() magic methods, had a number of issues with complex object graphs that are solved with the new magic methods.

It is a tricky topic well explained in the RFC, so go for it to know the details.

Password Hash Registry

Function password_algos() allows retrieving the available password hashing algorithms.

Support for argon2i and argon2id hashing when no ext/sodium installed

Hashing with argon2i and argon2id is possible when ext/sodium is not enabled in the core thanks to the password registry hash introduced in 7.4.

Escape PDO "?" parameter placeholder

It is possible to escape the ? character in PDO using ??.

Some PostgreSQL features were not possible to use with the PDO extension because they used ? as part of their syntax. Now PDO parses ?? as a single ?, allowing these PostgreSQL operations and any others using question marks.

Deprecations

Several parts of the language have been marked as deprecated and some extensions moved out.

Small deprecations

A number of language constructs and methods are marked as deprecated, raising a E_DEPRECATED warning when used.

Nothing especially remarkable since most of the deprecations are based on inconsistencies and clean ups. The list of deprecations:

  • The real type
  • Magic quotes legacy
  • array_key_exists() with objects
  • FILTER_SANITIZE_MAGIC_QUOTES filter
  • Reflection export() methods
  • mb_strrpos() with encoding as 3rd argument
  • implode() parameter order mix
  • Unbinding $this from non-static closures
  • hebrevc() function
  • convert_cyr_string() function
  • money_format() function
  • ezmlm_hash() function
  • restore_include_path() function
  • allow_url_include ini directive

Deprecate curly braces array access

Accessing arrays with curly braces will emit a deprecation warning in order to be removed in PHP 8.

Funny since the ability to use curly braces to access array elements
is pretty much unknown and has very little usage in the wild.

$myArray = [1,2,3];
echo $myArray{1}; //Will trigger a deprecation warning 
Enter fullscreen mode Exit fullscreen mode

Enforce parentheses for chained ternary operators

Nested ternaries will require parentheses since they will be right-associative instead of left-associative. Not doing so will raise a deprecation warning.

1 ? 2 : 3 ? 4 : 5;   // deprecated
(1 ? 2 : 3) ? 4 : 5; // ok
1 ? 2 : (3 ? 4 : 5); // ok
Enter fullscreen mode Exit fullscreen mode

Nested ternary operators are quite hard to read anyway, so you can just don't use them straight :P

Change the precedence of the concatenation operator

Raise a deprecation error when using an expression with operator . and + or - without parentheses.

In PHP 8 the precedence of . will be less than + or -.

echo "result: " . $a + $b;
Enter fullscreen mode Exit fullscreen mode

In PHP 7.3 this concatenates doing the sum afterwards as in ("result: " . $a) + $b;.
In PHP 7.4 it will raise a deprecation warning if no parentheses.
In PHP 8 this will do the sum, then concatenate as in ("result: " . $a) + $b;.

Weird fact: I could not reproduce this behavior in a test script ¯\(ツ)

Removed extensions

A number of extensions have been unbundled from the language:

  • ext/interbase: this extension to support InterBase database has serious implementation issues and has been moved to PECL RFC
  • ext/wddx: this data format intended for exchanging data had its own extension, now deprecated and moved to PECL RFC
  • ext/recode: an extension to convert between charsets. It is old, discontinued, and iconv and mbstring extensions are doing the same in PHP RFC

Top comments (17)

Collapse
 
goceb profile image
Goce • Edited

Unfortunately, only contravariant arguments are supported so the command/handler example will not work:

interface Command{}

interface CommandHandler{
    public function handle(Command $command) : void ;
}

class KillAllHumans implements Command{}

class KillAllHumansHandler implements CommandHandler{
    public function handle(KillAllHumans $command) : void {}
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
xoubaman profile image
Carlos Gándara • Edited

You are right. Amazingly enough, I realized it in a conversation in some slack community at the same time you wrote the comment.

I will correct the article as soon as I can. Update: fixed!

Thanks for pointing it out!

Collapse
 
latengoyo profile image
alex #1Oct • Edited

I can't see the usefulness of contravariant VS covariant arguments. I guess there were some technical issues implementing covariant arguments.

I was hyped up with PHP 7.4 only for that!

Collapse
 
david_j_eddy profile image
David J Eddy

An excellent break down of the new feature set Carlos.

Collapse
 
xoubaman profile image
Carlos Gándara

Thanks! Glad you liked it :)

Collapse
 
zexias profile image
Matheus Faustino

Weak references is related to garbage collector basically, there are good examples of how to use it in nodejs and java. It helps the garbage collector identify what it can get, not when, but which objects/vars and etc...

It's a kind of feature that will help projects like doctrine, that can consume a lot of resources because of the references through objects and so on.

Did you get it or it was confused?

Collapse
 
xoubaman profile image
Carlos Gándara

Yup, I got the concept but couldn't find a real-life example. I guess I never had to deal with such type of functionality in any project, like holding big graphs of object relations.

Funny enough, Ocramius, a Doctrine core maintainer, voted against weak references feature 🤷‍♂️

Thanks for the comment!

Collapse
 
marinabsa profile image
Marina A.

Thanks for the article Carlos.
I‘m currently learning PHP and something intrigued me, why did you write
if(!($command instanceOf RegisterUserCommand)){} as if it would be the same as passing RegisterUserCommand as a parameter? Doesn‘t the exclamation inside the if statement mean „only if $command is not an instance of RegisterUserCommand“?

Collapse
 
nicolasdanelon profile image
Nicolás Danelón

thanks, awesome article!

Collapse
 
petecapecod profile image
Peter Cruckshank

Wow first off that elephant pic is 💯🤩
Great article 👐👐

Collapse
 
iarmankhan profile image
Arman Khan

Great Article Man!❤✨ Loved it!

Collapse
 
xoubaman profile image
Carlos Gándara

Thanks!

Collapse
 
marinabsa profile image
Marina A.

Oh nevermind, I see what you mean now. In my head you meant to check if it is an instance in the first place , but you are looking for the case that it isn’t in order to throw an Exception.

Collapse
 
xoubaman profile image
Carlos Gándara • Edited

Exactly. But not needed anymore :D

UPDATE: Actually no, the example was not correct :(

Collapse
 
xoubaman profile image
Carlos Gándara

Actually, my assumption was not correct, I have removed the example. Sorry for the confusion.

Collapse
 
xoubaman profile image
Carlos Gándara

I fixed an error in the underscore for numeric literals, the correct way is to use a dot for separating the decimal part. So 1000.50 is 1_000.50, not 1_000_50.

Collapse
 
pthreat profile image
pthreat

Well explained, well done