All the changes that come with PHP 7.4
Tags: php
Published: 2019-12-01 15:13:20

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](https://wiki.php.net/rfc/typed_properties_v2)

Class properties can be typed.

Here is a quick tour:

```php
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
```

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](https://wiki.php.net/rfc/arrow_functions_v2)

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.

```php
$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!"
```

### [Covariant returns and contravariant parameters](https://wiki.php.net/rfc/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:

```php
class MarioEnemy {}
class Koopa extends MarioEnemy {}

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

interface MarioEnemyGrabber {
    public function grabEnemyOfType(Turtle $type): MarioEnemy;
}
```

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:

```php
//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;
}
```

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](https://wiki.php.net/rfc/null_coalesce_equal_operator)

The null coalescing assignment operator `??=` is available.

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

//Before we had to do
$discount = $discount ?? 20;
```

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

### [Spread operator in arrays](https://wiki.php.net/rfc/spread_operator_for_array)

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

```php
$blueGreen = ['blue', 'green'];
$andAlsoRed = ['red', ...$blueGreen];
//result is ['red', 'blue', 'green']
```

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

### [Preloading](https://wiki.php.net/rfc/preload)

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](https://www.youtube.com/watch?v=JBWgvUrb-q8) 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](https://wiki.php.net/rfc/ffi)

It is possible to run C code from within PHP.

```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();
```

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](https://wiki.php.net/rfc/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](https://wiki.php.net/rfc/weakrefs)

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](https://wiki.php.net/rfc/tostring_exceptions)

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

### [Notice for non valid array container](https://wiki.php.net/rfc/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.

```php
$notArrayAccessible = 123;
$notArrayAccessible[0]; //throws a warning, integer cannot be accessed as arrays
```

### [Warning when providing invalid input to base_convert()](https://wiki.php.net/rfc/base_convert_improvements)

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](https://wiki.php.net/rfc/permanent_hash_ext)

The hash extension will be always enabled.

### [Improve openssl_random_pseudo_bytes()](https://wiki.php.net/rfc/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](https://wiki.php.net/rfc/mb_str_split)

The method `mb_str_split` allows multibyte support for splitting strings.

### [Reflection for references](https://wiki.php.net/rfc/reference_reflection)

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](https://wiki.php.net/rfc/custom_object_serialization)

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](https://wiki.php.net/rfc/password_registry)

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

### [Support for argon2i and argon2id hashing when no ext/sodium installed](https://wiki.php.net/rfc/sodium.argon.hash)

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](https://wiki.php.net/rfc/pdo_escape_placeholders)

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](https://wiki.php.net/rfc/deprecations_php_7_4)

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](https://wiki.php.net/rfc/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.

```php
$myArray = [1,2,3];
echo $myArray{1}; //Will trigger a deprecation warning 
```

### [Enforce parentheses for chained ternary operators](https://wiki.php.net/rfc/ternary_associativity)

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

```php
1 ? 2 : 3 ? 4 : 5;   // deprecated
(1 ? 2 : 3) ? 4 : 5; // ok
1 ? 2 : (3 ? 4 : 5); // ok
```

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](https://wiki.php.net/rfc/concatenation_precedence)

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 `-`.
```php
echo "result: " . $a + $b;
```

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](https://wiki.php.net/rfc/deprecate-and-remove-ext-interbase)
* `ext/wddx`: this data format intended for exchanging data had its own extension, now deprecated and moved to PECL [RFC](https://wiki.php.net/rfc/deprecate-and-remove-ext-wddx)
* `ext/recode`: an extension to convert between charsets. It is old, discontinued, and iconv and mbstring extensions are doing the same in PHP [RFC](https://wiki.php.net/rfc/unbundle_recode)