PHP world! 7.4 is here - what's new?

A long time ago, in a galaxy that is our own, something great was born - the PHP language. At first, it was far from perfect. Straight out of the box, it was quite strange. However, since that time (June 8, 1995) its gone through a lot of changes and we’ve grown to love it! It’s a great language for creating modern, bullet-proof, secure web applications. It is an ever-evolving language and recently, it has been updated once more! This update brings numerous great new features and the deprecation of old, outdated and messy ones. Let’s take a look at some of the changes the new release brings to the table!

Mikhail Kuklin
Web Developer

Foreign function interface

We can now write low-level, fast PHP extensions and use them as composer packages since this feature brings support of C code execution right at your PHP code. Here is an example taken directly from the documentation:

<?php
// create FFI object, loading libc and exporting function printf()
$ffi = FFI::cdef(
    "int printf(const char *format, ...);", // this is a regular C declaration
    "libc.so.6");
// call C's printf()
$ffi->printf("Hello %s!\n", "world");
?>

gives you
Hello world!

Although C knowledge isn’t common among PHP developers, you now have the option to dive into it and perhaps it may result in great performance enhancements!

Preloading

This is something that can result in significant improvements in speed for your web applications. This new feature gives developers a chance to preload rare changing functionality, such as composer packages, to the memory at server start and use it directly from memory for following requests. This way, we can avoid monotonous processing of preloaded code, and PHP only needs to handle our code.

The drawback to this is that you have to restart the server if you change preloaded code (e.g. you ran composer update and packages were updated to the fresh version) and you probably can’t use preloading at shared hosting due to the fact that you don’t have access to php.ini there.

How preloading works:

  • You create preload script and mention it at php.ini at opcache.preload.
  • At preload script, you use opcache_compile_file() or require once every file that should be preloaded.
  • Be careful - for every preloaded file, you should preload its dependencies such as parent classes, interfaces and traits.
  • You can check what files were preloaded and how much memory is taken by them using opcache_get_status().
  • Also, the composer team works on creating preload script out of the box. You can follow the issue here - https://github.com/composer/composer/issues/7777 (that’s an interesting issue number, isn't it?)
  • The benchmarks published by Ben Morel in the above composer issue show that there isn’t a big performance boost if you preload around 100 hot used classes vs all classes (+13% vs +17% speed difference). Therefore, I think that the rule of 100 preload classes may be introduced as good practice.

Typed properties

You can now add and cast properties of your classes to get strict rules about what type of variable they may contain and how another code may handle it. Here’s an example:

class Amigo
{
    public string $name;
	private integer $salary;
}

$greg = new Amigo;

Important things to note:

  • You can use any type, except void and callable.
  • You may use it only on classes.
  • You have to use an access modifier for every typed property - public, protected, private, or var.
  • Typed properties doesn't mean that you can't assign int value to a string property. You certainly can and PHP will cast it for you. If you don’t need it, use declare (strict_types=1); at the very beginning of your class.
  • You may create a class with typed properties but if you didn’t set the default value for typed properties or didn’t assign their value at the constructor, you’ll receive the following error:
$greg = new Amigo;
var_dump($greg->name);

Fatal error: Uncaught Error: Typed property Amigo::$name
must not be accessed before initialization
  • There are numerous other things and nuances of typed properties usage, such as specific inheritance rules. Please take a look at the RFC to get a deeper understanding on how to use these with your code.

Improved type variance

It's all about respecting the Barbara Liskov Substitution Principle:

If S is a subtype of T, then objects of type T may be replaced with objects of type S. Please see - Wikipedia

This principle requires its own entire article because for one, it’s very interesting and two, it involves a lot of explanation in regards to how OOP code should follow it. But, reverting our focus back to PHP 7.4, we may say that it brings:

  • Usage of covariant return types
class Developer {}
class Amigo extends Developer {}

class Project
{
    public function covariantReturnTypes(): Developer
    { /* … */ }
}

class Crm extends Project
{
    public function covariantReturnTypes(): Amigo
    { /* … */ }
}
  • Usage contravariant arguments
class Project
{
    public function contraVariantArguments(Amigo $type)
    { /* … */ }
}

class Crm extends Project
{
    public function contraVariantArguments(Developer $type)
    { /* … */ }
}

Numeric literal separator

Let’s say for example that you need to write a long number inside your code, such as a test credit card or bank account number. It’s easy to make a mistake and miss a digit or two. PHP now gives us syntax sugar to handle this issue.

It used be like this:
$creditCardNumber=4261629123629224

Now you can do this:
$creditCardNumber=4261_6291_2362_9224

Short closure, AKA arrow functions

This is a new way of writing shorter functions in PHP. It seems like the core team was inspired by changes to Javascript and as a result, we are now able to do the following:

// A collection of Player objects
$players = [/* … */];
$ids = array_map(fn($player) => $player->id, $players);

instead of

$ids = array_map(function ($player) {
    return $player->id;
}, $players);

or more strict version of arrow function usage:

$ids = array_map(fn(Player $player): int => $player->id, $players);

Seems neat, doesn’t it? Here is something you should consider if you're going to use this:

  • They start with the fn keyword.
  • They can only have one expression, which is the return statement.
  • No return keyword allowed.
  • Arguments and return types can be type hinted.
  • The spread operator is also allowed.
  • References are allowed.
  • It doesn't require use to get access to variables out of scope, you may use them as you wish.
  • You can change variable out of arrow functions scope but the changes won't happen at the original variable because it is bound by value and not by reference. One exception to this is when it goes to the scoped function, which acts exactly the same as normal closures.

Null coalescing assignment operator

Basically this is shorthand for null coalescing operation that was introduced in PHP 7.

Instead of
$project['start_date'] = $project['start_date'] ?? new DateTime();

you may use
$project['start_date'] ??= new DateTime();

I enjoy typing less code for the same functionality. What about you?

Array spread operator

This only works with arrays with numeric keys and it makes it possible to replace some functions for merging arrays. For example:

$goodSchoolMarks = [4, 5];
$badSchoolMark = [1, 2, 3];
$schoolMarks = [...$badSchoolMarks, ...$goodSchoolMarks];

you get
$schoolMarks = [1, 2, 3, 4, 5]

Short open tag was deprecated

Eventually, and most likely when PHP 8.0 comes around, we won’t be able to use the <??> notation. The reasons for this are described here but this is the rundown:

  • The PHP documentation discourages their usage.
  • PHP's short open tags clash with XML <?xml ?> and can mean two different things depending on the INI configuration.
  • PHP's short open tags depend on an INI directive and as such, are non-portable.
  • As such, source code may leak if PHP relying on the short open tags is executed on a configuration where this isn't enabled.
  • PHP's parser simplification.

And much more...

This is only a short list of what I personally found to be the most interesting aspects of the new update. However, I’d like to briefly mention a few other things that I left out:

  • Custom object serialization, which gives us some new, magical methods! The __serialize and _unserialize. Please check out the RFC to understand why good old __sleep and _wakeup may not function properly in some cases.
  • Reflection for references gives the PHP language certain API to get reflections of references for operations such as dumping, cloning or comparison. You will no longer need hacks to understand if one variable references to another.
  • Weak references to avoid a memory leak if you use objects for caching.
  • And a whole list of other changes that you can find here: https://github.com/php/php-src/blob/PHP-7.4/UPGRADING

Thank you for reading! I hope your projects transitioned well with the new update and that you’re getting faster and cleaner code! (Do you remember that we should dive into the Liskov principle?)