PHP version 8.4 will be released to the end user on November 21, 2024, and I think it's time to at least get a little familiar with what's new for us as programmers. The changes are one idea more than in the previous version and, in my personal opinion, will be a bit more interesting.
Version Support
Before we get to the fun part, let's take a moment to look at two graphs related to the lifespan of PHP versions so far.
If we compare the data from the current graphs with last year's, we can conclude that the security support of every version of PHP after 8.0 (inclusive) has been extended from one to two years. This means that the period in which we can safely use a version has been extended from three to four years.
New Features
Sodium: AEGIS-128L and AEGIS256 support
AEGIS is an AES-based family of encryption algorithms that are faster than AES-GCM. The Sodium extension in PHP 8.4 supports AEGIS-128L and AEGIS256 encryption algorithms if compiled with version 1.0.19 of libsodium or later.
In the Sodium extension, we get three new functions and four new constants for each algorithm.
AEGIS-128L
Constants:
- SODIUM_CRYPTO_AEAD_AEGIS128L_KEYBYTES: Number of bytes required for the key used by the AEGIS-128L algorithm. Value - 16
- SODIUM_CRYPTO_AEAD_AEGIS128L_NPUBBYTES: Number of bytes required for the nonce value used by the AEGIS-128L algorithm. Value - 16
- SODIUM_CRYPTO_AEAD_AEGIS128L_NSECBYTES: Value - 0
- SODIUM_CRYPTO_AEAD_AEGIS128L_ABYTES: Value - 32
Functions:
- sodium_crypto_aead_aegis128l_keygen(): Generates a cryptographically secure random number of the required length (SODIUM_CRYPTO_AEAD_AEGIS128L_KEYBYTES) for the AEGIS-128L
- sodium_crypto_aead_aegis128l_encrypt(): Encrypts and authenticates data with AEGIS-128L
- sodium_crypto_aead_aegis128l_decrypt(): Verifies and then decrypts a message with the AEGIS-128L
AEGIS-256
Constants:
- SODIUM_CRYPTO_AEAD_AEGIS256_KEYBYTES: Number of bytes required for the key used by the AEGIS-256 algorithm. Value - 32
- SODIUM_CRYPTO_AEAD_AEGIS256_NPUBBYTES: Number of bytes required for the nonce value used by the AEGIS-256 algorithm. Value - 32
- SODIUM_CRYPTO_AEAD_AEGIS256_NSECBYTES: Value - 0
- SODIUM_CRYPTO_AEAD_AEGIS256_ABYTES: Value - 32
Functions:
- sodium_crypto_aead_aegis256_keygen(): Generates a cryptographically secure random number of the required length (SODIUM_CRYPTO_AEAD_AEGIS256_KEYBYTES) for AEGIS-256
- sodium_crypto_aead_aegis256_encrypt(): Encrypts and authenticates data with AEGIS-256
- sodium_crypto_aead_aegis256_decrypt(): Validates and then decrypts a message with AEGIS-256
New array_find(), array_find_key(), array_any(), and array_all() functions
We have 4 new functions for working with arrays. They aim to make some common basic operations easier.
array_find(array $array, callable $callback): mixed
It accepts an array as the first parameter, and a callback function as the second. Returns the first element for which the callback function returns true.
Example:
array_find([1, 2, 3, 4, 5], fn($value) => $value % 2 === 0);
// 2
array_find_key(array $array, callable $callback): mixed
Similar to array_find(), but returns the key pointing to the element that first returns true.
Example:
array_find_key(
['foo' => 1, 'bar' => 2, 'baz' => 3],
fn($value) => $value % 2 === 0
);
// "bar"
array_any(array $array, callable $callback): bool
Returns true if at least one element from the array causes the callback function to return true.
Example:
array_any(
[1 => '', 'bar' => '', 'baz' => ''],
fn($value, $key) => is_numeric($key),
);
// true
array_all(array $array, callable $callback): bool
Returns true if all elements of the array cause the callback function to return a positive response when called with them.
Note: the function also returns true for an empty array ([])!
Example:
array_all(
[1 => '', 2 => '', 3 => ''],
fn($value, $key) => is_numeric($key),
);
// true
Intl: New grapheme_str_split() function
This is a new function for splitting strings of characters. At first glance, it may sound useless, but it has its uses. The difference between mb_str_split() and grapheme_str_split() is that the former splits a string into Unicode multi-byte characters, while the new function splits it into functional units for the writing system (graphemes).
Why do we need it?
There are languages that require a combination of symbols to write a single letter. There are also complex emojis that are composed of several such symbols.
Examples:
Curl: New CURL_HTTP_VERSION_3 and CURL_HTTP_VERSION_3ONLY constants for HTTP/3 support
- CURL_HTTP_VERSION_3: Value - int 30
- CURL_HTTP_VERSION_3ONLY: Value - int 31
These two new constants can be used to configure Curl when attempting to make HTTP/3 requests.
It is important to mention that to make such a request, the Curl extension requires a minimum version of libcurl 7.66.0.
Intl: New intltz_get_iana_id() function and IntlTimeZone::getIanaID() method
These two functions return canonical time zone identifiers from a given time zone. This may sound strange, but there are cases where they are useful.
Example:
intltz_get_iana_id('Europe/Berlin'); // Valid, returns the same
// "Europe/Berlin"
intltz_get_iana_id('Mars'); // Invalid, returns false
// false
intltz_get_iana_id('Europe/Nicosia'); // Returns Asia/Nicosia
// "Asia/Nicosia"
IntlTimeZone::getIanaID('Asia/Chongqing'); // Duplicating existing location
// "Asia/Shanghai"
In most cases, the returned value is the same as the provided identifier. However, there are situations where a given time zone identifier is outdated or has been replaced with a new one. In these cases, these new functions help to obtain the correct time zone.
New request_parse_body() function
PHP automatically processes POST requests and populates the super global variables $_POST and $_FILES. However, for other methods such as PUT and PATCH, this does not happen automatically and the responsibility for processing falls on the application itself.
The new function request_parse_body() aims to expose PHP's internal request processing logic, thus allowing consistency in request handling.
function request_parse_body(?array $options = null): array {}
The function reads directly from php://input and returns an array. It will always contain 2 keys, even if the request is empty. The first will contain data that we would expect to see in the $_POST variable, and the second - what we would expect to see in $_FILES. The function also accepts an $options array, which allows overwriting of .ini settings for the current function call.
BCMath: New bcdivmod() function
The new function divides a number by a given divisor and returns an array - the first element is the quotient, and the second is the remainder.
Example:
bcdivmod('42', '10');
// ["4", "2"]
MBString: New mb_ucfirst() and mb_lcfirst() functions
The PHP extension mbstring, as I assume you know, provides multibyte-safe equivalents for most standard string processing functions in PHP. Well yes, but the functions ucfirst() and lcfirst() were not among these until now. That's why in version 8.4 we get mb_ucfirst() and mb_lcfirst(), which take one parameter of string type and return the same string, but with the first letter capitalized and lowercase respectively.
Example:
mb_ucfirst('we love PHP'); // We love PHP
mb_lcfirst('Version 8.4'); // version 8.4
New http_(get|clear)_last_response_headers() functions
PHP provides an HTTP wrapper that can access remote HTTP content through standard file system functions.
Example:
file_get_contents("<https://www.php.net/>")
In this case, file_get_contents() will make a GET request to php.net and populate the $http_response_header variable.
This is a historical artifact that was once used to make HTTP headers accessible, but the "magical" nature of this variable can be confusing and requires special handling in IDEs and static analyzers.
To improve the clarity of code actions and IDE support, two new functions are recommended instead:
- http_get_last_response_headers(): ?array - Returns the HTTP headers received on the last successful request in an array identical to what we would see in the $http_response_header variable
- http_clear_last_response_headers(): void - Clears HTTP headers from memory, then all calls to http_get_last_response_headers() will return null until the next successful request
Curl: curl_version() feature_list support
In the new version of the curl_version() function, we get a new key in the array returned by the function. It is called feature_list and contains information about which Curl features are supported and which are not:
curl_version();
[
"version_number" => 524801,
"age" => 10,
"features" => 1438631837,
"feature_list" => [
"AsynchDNS" => true,
"CharConv" => false,
"Debug" => false,
// ...
"HTTP2" => true,
"HTTPS_PROXY" => true,
"BROTLI" => true,
"ALTSVC" => true,
"HTTP3" => false,
"ZSTD" => true,
"HSTS" => true,
// ...
],
"ssl_version_number" => 0,
"version" => "8.2.1",
"host" => "x86_64-pc-linux-gnu",
// ...
]
PCRE2 Upgrade and Regular Expression Changes
The RegEx functions in PHP (preg_*) use the PCRE (Perl-Compatible Regular Expressions) library. PHP 8.4 will now use PCRE2 version 10.44, which will bring several changes to RegEx usage.
Quantifiers Without Minimum Quantity
Until now, it has not been possible to make quantifiers that do not have a minimum quantity. If we don't care, we could put 0.
Example:
preg_match('/a{0,3}/', 'aaa'); // Before PHP 8.4
preg_match('/a{,3}/', 'aaa'); // After PHP 8.4
If we don't care about the minimum, we can now just drop it.
Spaces Allowed in Curly Braces
As of PHP 8.4, we can now put spaces and tabs in quantifier pairs.
Example:
// Before PHP 8.4
preg_match('/a{5,10}/', 'aaaaaaa');
// After PHP 8.4
preg_match('/a{ 5,10 }/', 'aaaaaaa');
preg_match('/a{5 ,10}/', 'aaaaaaa');
preg_match('/a{ 5, 10 }/', 'aaaaaaa');
preg_match('/a{ 5, 10 }/', 'aaaaaaa');
Unicode 15 Update
The PCRE2 version used in PHP 8.4 now supports Unicode 15. One of the novelties there are the new character classes.
- Kawi - from U11F00 to 11F5F
- Nag Mundari - from U1E4D0 to 1E4FF
Example:
preg_match('/\\p{Kawi}/u', 'abc');
preg_match('/\\p{Nag_Mundari}/u', 'abc');
Regex \W in Unicode Mode
Before version 8.4, the character class \w was equivalent to [\\p{L}\\p{N}_].
From version 8.4 it will now be equivalent to [\\p{L}\\p{N}_\\p{Mn}\\p{Pc}].
This means it will catch 1849 additional characters, which can cause problems in some cases.
Caseless Restrict Modifier Support
This is a new modifier and a new RegEx flag that when applied prevents matching between ASCII and non-ASCII characters. For example, the sign for Kelvin (K, "\u{212A}") and the English letter K can match “k” (normal letter “k” in English) in the Unicode Regex:
preg_match('/k/iu', "K"); // Matches
preg_match('/k/iu', "k"); // Matches
preg_match('/k/iu', "\\u{212A}"); // Matches
PHP 8.4 introduces a "caseless restrict" mode that prevents case-insensitive (/i) matches between ASCII and non-ASCII characters. This mode is activated by placing (?r) at the position where the insensitive match should begin. Likewise (?-r) disables it:
preg_match('/(?r)k/iu', "K"); // Matches
preg_match('/(?r)k/iu', "k"); // Matches
preg_match('/(?r)k/iu', "\\u{212A}"); // Does NOT Match
To execute all RegEx in this mode a new "r" flag is supported:
preg_match('/\\x{212A}/iu', "K"); // Matches
preg_match('/\\x{212A}/iur', "K"); // Does NOT match
Length-Limited Variable-Length Lookbehind Support
PCRE2 10.43 supports variable-length look behind as long as there is a maximum length described. This means that PHP 8.4 will support RegEx of the type:
preg_match('/(?<=Hello{1,5}) world/', 'Hello world'); // Matches
preg_match('/(?<=Hello{1,5}) world/', 'Hellooooo world'); // Matches
preg_match('/(?<=Hello{1,5}) world/', 'Helloooooo world'); // No Matches
Named Capture Group Label Length Increased to 128
PCRE2 10.44 increased the maximum length of named capture group labels. Before PHP 8.4, the maximum tag length of a named capture group was 32 characters.
preg_match('/(?<mylabel1234567890123456789012345>a+)/');
In PHP 8.4, a label (eg. from the example above, starting with mylabel123...) can be up to 128 characters long.
Phpinfo: Show PHP Integer Size Information
In the updated phpinfo() function, we will now see information about the size of the integer type in bits relative to the current PHP settings.
This also applies to output data in the PHP CLI:
❯ php -a
Interactive shell
php > phpinfo();
phpinfo()
PHP Version => 8.4.0RC1
// ...
PHP Integer Size => 64 bits
// ...
Date: New DateTime(Immutable)::get/setMicrosecond methods
The DateTime and DateTimeImmutable classes will now support getMicrosecond() and setMicrosecond() methods to get and set the microseconds of time objects.
Example:
class DateTime {
// ...
public function getMicrosecond(): int {}
public function setMicrosecond(int $microsecond): static {}
// ...
}
New Rounding Modes in Round() Function
Prior to PHP 8.4, the round() function supported four rounding methods:
- PHP_ROUND_HALF_UP: Rounds the number away from zero when it is in the middle.
- PHP_ROUND_HALF_DOWN: Rounds the number towards zero when it is in the middle.
- PHP_ROUND_HALF_EVEN: Rounds the number towards the nearest even value when halfway.
- PHP_ROUND_HALF_ODD: Rounds the number towards the nearest odd value when in the middle.
In the new version, the round() function supports four new rounding methods:
- PHP_ROUND_CEILING: Rounds the number to the nearest integer greater than the number. (as the ceil() function does)
- PHP_ROUND_FLOOR: Rounds the number to the nearest integer lower than the number. (as the floor() function does)
- PHP_ROUND_TOWARD_ZERO: Rounds the number towards zero.
- PHP_ROUND_AWAY_FROM_ZERO: Rounds the number away from zero.
Example:
Date: New DateTime(Immutable)::createFromTimestamp() methods
DateTime(Immutable)::createFromTimestamp() allows the creation of time objects directly using a UNIX timestamp, which until now was done in a slightly more complicated way.
Example:
$dt = DateTimeImmutable::createFromTimeStamp(1703155440);
$dt->format('Y-m-d'); // "2023-12-21"
$dt = DateTimeImmutable::createFromTimeStamp(1703155440.628);
$dt->format('Y-m-d h:i:s.u'); // "2023-12-21 10:44:00.628000"
Mbstring: New mb_trim(), mb_ltrim(), and mb_rtrim() functions
In PHP 8.4 we also get three new multibyte string functions. They are the multibyte equivalent of trim(), ltrim(), and rtrim():
- mb_trim(): removes characters from the left and right side of a multibyte character string;
- mb_ltrim(): removes characters from the left side of a multibyte character string;
- mb_rtrim(): removes characters from the right side of a multibyte character string.
All three new functions by default only remove whitespace characters such as space, tab, newline, etc.
Syntax/Functionality Changes
Curl: Minimum required libcurl version increased to 7.61.0
Before PHP 8.4, the Curl extension required a minimum version of libcurl 7.29.0, released in 2013.
In PHP 8.4, the Curl extension now requires libcurl version 7.61.0, released in 2018.
MBString: Unicode Character Database updated to version 16
The MBString extension contains partial data from the Unicode specification that it uses for some of its operations. In PHP 8.3, it included the data from the Unicode 14.0 standard, released in September 2022. In PHP 8.4, the Unicode Character Database (UCD) data source was updated from version 14.0 to 16.0, released in September 2024. Unicode 16.0 is the latest UCD released to date.
This means that the MBString extension can handle all the latest emoticons and has the most up-to-date information about the case folding and character width.
Openssl: Minimum Required Openssl Version Increased to 1.1.1
As of PHP 8.4, the minimum OpenSSL extension requirement for the OpenSSL library has been increased from 1.0.1 to 1.1.1. The extension continues to be compatible with OpenSSL as well.
round() - Invalid rounding modes throw \ValueError exceptions
Until now, if we passed the round() function as an invalid rounding method, it used PHP_ROUND_HALF_UP by default. As of the new version of PHP, this behavior is replaced by throwing an exception of type ValueError.
Example:
round(num: 3.14, mode: 42); // Invalid $mode parameter
ValueError: round(): Argument #3 ($mode) must be a valid rounding mode (PHP_ROUND_*).
Opcache: Ini Changes on How Jit Is Enabled
In PHP version 8.0, we got JIT (Just-In-Time compilation). It has its settings in the .ini file, which will undergo slight changes in version 8.4. JIT remains off by default.
// Before PHP 8.4
opcache.jit=tracing
opcache.jit_buffer_size=0
// After PHP 8.4
opcache.jit=disable
opcache.jit_buffer_size=64M
PHP_ZTS and PHP_DEBUG constant value type changed from int to bool
The PHP_ZTS and PHP_DEBUG constants carry the following information:
- PHP_ZTS: Indicates whether the current PHP build is thread-safe;
- PHP_DEBUG: Indicates whether the current PHP build is a debug build.
Until now, these two constants were of type int (1 for true, 0 for false). They will now be of type bool.
Password Hashing: Default Bcrypt Cost Changed From 10 to 12
PHP 8.4 changed the default hash value parameter (“cost”) of the built-in password hashing API from 10 to 12. This change is intended to make passwords more resistant to brute force attacks as modern hardware becomes more powerful. The change will result in a slightly longer time to calculate the hash of a given password, but this is a necessary trade-off to increase the security of password hashes.
Applications that explicitly set the hash value parameter will not be affected by this change. However, applications using the default hash value parameters will need to call the password_needs_rehash() function to check if the password needs to be rehashed with the new “cost” value. This is a one-time process and the updated hash can be stored in the database for future use.
Example:
// User tries to log into the system.
if (password_verify($password, $hash)) {
// The password is correct.
// Should the password be rehashed?
if (password_needs_rehash($hash, PASSWORD_BCRYPT)) {
// Creating the new hash.
$newHash = password_hash($password, PASSWORD_BCRYPT);
// Replace the old with the new hash.
// ...
}
// Continue with the login process.
}
Deprecations
Implicitly Nullable Parameter Declarations Deprecated
From PHP 8.4 we can expect the following warnings:
function test(array $value = null) {}
// Implicitly marking parameter $value as nullable is deprecated, the explicit nullable type must be used instead
In this case, we can see that the test() function receives a $value parameter of type array. It has a default value of null. But null is not form type array. This behavior still goes unpunished, but we will now receive a deprecation warning. What we can do in this case is to explicitly declare that this parameter can be left empty.
Recommended fixes:
function test(?array $value = null) {}
// or
function test(array|null $value = null) {}
E_STRICT Constant Deprecated
In PHP, errors, warnings, and notes have an error level assigned to them. Using the error_reporting() and set_error_handler() functions, PHP applications can control which errors are reported and change the default error behavior.
One of the error levels in PHP was E_STRICT. As of PHP 7.0, most of the existing E_STRICT warnings were moved to E_NOTICE, and in PHP 8.0 all of them are now E_NOTICE.
Since there are no more errors of type E_STRICT, it will be deprecated.
Calling session_set_save_handler() with more than 2 arguments deprecated
The session_set_save_handler() function has two different implementations. This is called method overloading. This behavior is only possible for built-in PHP functions, but not for custom functions (those that are in our PHP program).
function session_set_save_handler(
callable $open,
callable $close,
callable $read,
callable $write,
callable $destroy,
callable $gc,
?callable $create_sid = null,
?callable $validate_sid = null,
?callable $update_timestamp = null
): bool
function session_set_save_handler(
SessionHandlerInterface $sessionhandler,
bool $register_shutdown = true
): bool
Because of this difference between built-in and user space PHP code, the first implementation of the function will be deprecated from PHP 8.4 and will be completely removed in PHP 9.0.
If you are using the first implementation you can modify your code as follows:
// Before PHP 8.4
session_set_save_handler('my_session_open', 'my_session_close', 'my_session_read', 'my_session_write', 'my_session_destroy', 'my_session_gc');
// After PHP 8.4
$sessionHandler = new class() implements SessionHandlerInterface {
public function open(string $path, string $name): bool {
return my_session_open($path, $name);
}
public function close(): bool {
return my_session_close();
}
public function read(string $id): string|false {
return my_session_read($id);
}
public function write(string $id, string $data): bool {
return my_session_write($id, $data);
}
public function destroy(string $id): bool {
return my_session_destroy($id);
}
public function gc(int $max_lifetime): int|false {
return my_session_gc($max_lifetime);
}
};
session_set_save_handler($sessionHandler);
Curl: CURLOPT_BINARYTRANSFER deprecated
This deprecation is long overdue. The CURLOPT_BINARYTRANSFER constant was used for a Curl setting via the curl_setopt() function, but as of PHP version 5.1.2 it does not perform any action.
All programs using PHP version above 5.1.2 can safely remove the use of the constant without any changes.
Removed Features and Functionality
OCI8 and PDO-OCI extensions from PHP Core to PECL
Both extensions have known issues and a strong need for support. For this reason, they have been removed from the PHP core.
All applications needing the OCI8 and PDO-OCI extensions can install them from PECL:
pecl install oci8
pecl install pdo_oci
Imap Extension Moved From PHP Core to Pecl
This extension uses a C library that hasn't been updated since 2018, isn't thread-safe, and has other issues. It has been moved from the core to Pecl and is recommended to be avoided. There are better-performing and safer libraries that can perform the same functionality, you can find them here.
Pspell Extension Moved From PHP Core to Pecl
The libraries that Pspell uses haven't been updated in the last few years. The extension is removed from the core and moved to Pecl. If your app uses it, there are several alternatives:
- Using a PHP library;
- Refactoring the code so it uses the Enchant extension.
If needed, the easiest option would be to install Pspell via Pecl.
Conclusion
In conclusion, PHP 8.4 presents a significant advancement with many new features and improvements that make the language even more powerful and easy to use. New array functions like array_find(), array_any(), and array_all() make it easier to work with data, while improvements to mbstring and new functions like mb_ucfirst() and mb_trim() provide better support for multibyte strings. Updates to Curl and HTTP/3 support open up new possibilities for web development, and improvements to PCRE2 expand RegEx capabilities.
The team behind PHP continues to demonstrate a commitment to the development of the language by implementing functionalities that make the daily work of programmers easier. With each new version, PHP becomes an increasingly powerful tool for web development, and we look forward to seeing how these innovations will improve and optimize our work in the future. We believe that PHP will continue to evolve and provide innovations for all programmers around the world.
For more information you can visit:
https://php.watch/versions/8.4