PHP 8.1: New Features and Deprecations from the Major Release

PHP 8.1: New Features and Deprecations from the Major Release

PHP 8.1 is the new PHP version released November 2021. This version comes with new features, performance improvements, and changes that will open more opportunities for PHP developers to work efficiently and more creatively.

There are over 20 new features, tons of changes, and several deprecations in PHP 8.1. This article will go through them and help you smoothly transition from PHP 8.0 to PHP 8.1.

New Features and Upgrades in PHP 8.1

This new major PHP version introduces over 20 new features. Keep in mind that there may be changes after the release date that can make this list longer or shorter.

Here are some of the new features in PHP 8.1.


PHP 8.1 supports enumerations, or enum for short. It’s an enumerated data type that consists of a fixed set of possible values.

The syntax uses enum to declare enumerations, followed by its user-defined name. Then, it’s followed by case to declare the possible values. Enumerations can have any number of cases, from zero to as many as you need.

The enumeration RFC uses standard playing cards to explain how it works. There are four fixed suits – Spades, Hearts, Diamonds, and Clubs. You can enumerate these suits with enum in PHP:

enum Suit {
	case Spades;
	case Hearts;
	case Diamonds;
	case Clubs;

Now, the Suit enum has four possible values of card suits. You can use a function and enforce types when accepting or returning a suit value with a syntax, for example, Suit::Clubs.

function pick_card(Suit $suit) {}

Enumerations can also contain an optional string or int value. This is called backed Enum, and it has the following criteria:

  • Declare the scalar type, whether string or int, in the Enum declaration.
  • All cases have values.
  • All cases contain the same scalar type, whether string or int.
  • Each case has a unique value.

Suppose we want to assign a score to the suits in the previous example. We can use this syntax:

enum Suit: string {
	case Spades = ‘10’;
	case Hearts = ‘8’;
	case Diamonds = ‘5’;
	case Clubs = ‘3’;


This new feature introduces concurrent execution in PHP programming. However, concurrent execution in PHP doesn’t mean that the actions are executed at the same time.

For example, there’s a point where the main code will start a fiber. The code within the fiber will be executed separately in a different thread.

Also, the main program can’t suspend or terminate fibers. Only the code within the fiber can suspend itself and return any data to the main program. The main program, then, can continue the Fiber execution from the point of suspension.

A complete stack of a PHP program with fibers can be complicated, but here’s a simple example of fiber usage in an Echo program:

$fiber = new Fiber(function(): void {
    echo "Hello from the Fiber\n";
    echo "Welcome back to the Fiber\n";

echo "Starting the program\n";
echo "Taken control back\n";
echo "Resuming Fiber\n";
echo "Program ends\n";

Fiber::suspend() is where the fiber will suspend its execution. Then, when the main code calls for the fiber again with $fiber->resume(), the fiber will start from the point of suspension. Hence, it will echo Welcome back to the Fiber.

Here’s what the output should look like:

Starting the program
Hello from the Fiber
Taken control back
Resuming Fiber
Welcome back to the Fiber
Program ends

Pure Intersection Type

PHP 8.1 supports intersection types that allow declaring multiple class types for a value. At a glance, this sounds similar to union types that were introduced in PHP 8.0. However, there’s a significant difference.

Union types allow the value to fulfill any of the declared types. For example, if strings and integers are declared, the value can be either strings or declared.

Pure intersection types, however, only allow values that fulfill all of the declared types. This is why the pure intersection types can only use class or interface types, as most standard types like strings and integers can’t be fulfilled simultaneously.

To declare a pure intersection type in PHP 8.1, use & (AND) operator. Here’s an example:

function count_and_iterate(Iterator&\Countable $value) {
    foreach($value as $val) {}

The $value must be an object that implements the Iterator and Countable interfaces. Otherwise, it will cause a type error.

never Return Type

There’s a new never return type in PHP 8.1. When a never return type is declared in a function, it won’t return a value. The function must also end with an exit statement explicitly or implicitly.

Here’s an example of a simple function with the never return type:

function redirect(string $url): never {
    header('Location: ' . $url);

When you use a never return type, the rest of the code in the program will not be executed or return any value. In practical use, the never return type is useful to indicate that the function won’t execute the rest of the code and will terminate or throw.

In a way, the never return type is similar to the void return type. However, the void return type continues the execution after the function.

array_is_list Type

The new array_is_list function in PHP 8.1 helps you see whether an array contains entries that are a list. It will check the array keys – if they are in sequential order, start from zero, have no gaps, and all integers, it will return true. It will also return true by default for empty arrays.

For example, the following array_is_list function will return true:

array_is_list([1, 2, 3]);
array_is_list([‘rock’, 2, 3]);
array_is_list([‘rock’, scissor]);
array_is_list([0 => 'rock', 'scissor']);
array_is_list([0 => 'rock', 1 => 'scissor']);

Conversely, here are examples of false array_is_list functions:

array_is_list([1 => 'rock', 'scissor']); // false, doesn’t start with 0
array_is_list([1 => 'rock', 0 => 'scissor']); // false, not in order
array_is_list([0 => 'rock', 'suit' => 'paper']); false, non-integer keys
array_is_list([0 => 'rock', 2 => 'paper']); false, non-sequential

#[ReturnTypeWillChange] Attribute

This new attribute indicates a mismatching tentative return type shouldn’t result in a deprecation notice. Before PHP 8.0, attribute syntax was parsed as a code comment so that it wouldn’t cause a syntax error.

The new attribute addition #[ReturnTypeWillChange] to a class method in the PHP 8.1 version won’t cause any problem – it’ll only prevent the deprecation notice.

This is an example of #[ReturnTypeWillChange] in a PHP code:

class Sample implements ArrayAccess {
    public function offsetGet(mixed $offset) {}
    // ...

Note that this attribute won’t have any effect anymore when the tentative return types are upgraded to standard return types.

readonly Properties

readonly is a new class property in PHP 8.1 and can only be initialized once from the class where it’s declared. Any class with this property will be unmodifiable and will result in an error if you force it to change the value.

Here’s an example of how you declare a readonly property in a class:

class Author {
    public readonly int $authid;
    public function __construct(int $authid) {
        $this->authid = $authid;
$author = new User(25);

Now, since the readonly has already been initialized, you can’t modify the $authid in the code.

If you try to input a line like this:

$author->authid = 15

It will result in this error:

Error: Cannot modify readonly property User::$uid in ...:..

fsync() and fdatasync() Functions

fsync() and fdatasync() are new additions to the file system functions in PHP 8.1. They have similarities to fflush(), which is used for flushing buffers to the operating system. However, fsync() and fdatasync() flush the buffer to the physical storage.

The key difference between them is that the fsync()function includes metadata when synchronizing files’ changes, while the fdatasync() doesn’t.

Here’s an example of the fsync() function in a code:

$file = 'sample.txt';
$test = fopen($file, 'h');
fwrite($test, 'sample data');
fwrite($test, "\r\n");
fwrite($test, 'another data');

You can use either fsync()or fdatasync()in line 6. When the function is called, the operating system is requested to flush the data to the storage.

These functions are useful when the program needs consistent data storage. Plus, it makes it possible to retrieve the data in case the application crashes or the system fails.

Sodium XChaCha20 Functions

Sodium is a PHP extension that contains a cryptographic library. It supports the XChaCha20 encryption algorithm, a stream cipher that’s a variant of ChaCha20.

In PHP 8.1, three new functions let you encrypt and decrypt data using XChaCha20 without authentication. This method is called detached mode, and the functions are as follows:

  • sodium_crypto_stream_xchacha20_keygen – generate a random key to use with the sodium_crypto_stream_xchacha20 function.
  • sodium_crypto_stream_xchacha20 – generate a keystream of pseudorandom bytes by expanding the cryptographic nonce and the key generated earlier.
  • sodium_crypto_stream_xchacha20_xor – encrypt a message using a nonce and secure key without authentication.

Alongside the new functions, two new PHP constants are defined in the global namespace:


new in Initializers

PHP 8.1 now allows the use of new expressions to initialize a value inside a function declaration.

When using PHP 8.0, we have to initialize the value for an optional parameter outside the constructor. While this works just fine, it makes the code a little bit longer. Here’s an example from the new RFC:

class Test {
    private Logger $logger;
    public function __construct(
        ?Logger $logger = null,
    ) {
        $this->logger = $logger ?? new NullLogger;

In PHP 8.1, we can simply write it like this:

class Test {
    public function __construct(
        private Logger $logger = new NullLogger,
    ) {}

String-Keyed Array Unpacking Support

The previous PHP version added support for the array spread operator (…). It works for array unpacking and as an alternative to the array_merge() function.

However, the spread operator used to support numeric keys only. Using the operator or function for string-keyed arrays would result in an error.

PHP 8.1 now supports array unpacking for string-keyed arrays. Suppose you have these arrays:

$array_1 = ['a', 'b'];
$array_2 = ['c', 'd'];

You can unpack the array’s keys using the spread operator.

$array = [...$array1, ...$array2];
//['a', 'b', 'c', 'd'];

Or, unpack the array’s keys using the array_merge() function:

$array = array_merge($array1, $array2);
//['a', 'b', 'c', 'd'];

First-class Callable Syntax

PHP 8.1 introduces a new first-class callable syntax to create a callable from inside the current scope.

Prior to PHP 8.1, syntax for a callable uses Closure::fromCallable, such as this:

$callable = Closure:: fromCallable(‘hostinger’);

With the new syntax, you can use (...) after the object instead of using Closure::from Callable. This makes it easier to create a first-class callable. Here’s an example of the above function using the new syntax:

$callable = hostinger(...);

New IntlDatePatternGenerator Class

In the previous version of PHP, you can create a localized date and time only with the pre-defined IntlDateFormatter. There are eight pre-defined formats – four basic formats and four relative variants that use yesterday, today, and tomorrow.

While these are reasonable options, it’s not as customizable as it is in PHP 8.1.

The new IntlDatePatternGenerator class allows you to specify the format for a date, month, and time. The exact order itself can be left to the formatter.

Here’s an example of its use from the IntlDatePatternGenerator RFC:

$skeleton = "YYYYMMdd";
$today = \DateTimeImmutable::createFromFormat('Y-m-d', '2021-04-24');
$dtpg = new \IntlDatePatternGenerator("de_DE");
$pattern = $dtpg->getBestPattern($skeleton);
echo "de: ", \IntlDateFormatter::formatObject($today, $pattern, "de_DE"), "\n";
$dtpg = new \IntlDatePatternGenerator("en_US");
$pattern = $dtpg->getBestPattern($skeleton), "\n";
echo "en: ", \IntlDateFormatter::formatObject($today, $pattern, "en_US"), "\n";
de: 24.04.2021
en: 04/24/2021

The skeleton variable specifies the YYYYMMDD date and time format, while the formatter specifies the final ordering. From the code above, we get two different results from the formatter. However, the date still uses the YYYYMMDD format.

final Class Constants

Declaring the new final flag on class constants prevents them from being overridden or extended by sub-classes.

If you try to expand or override a final class, it will result in a fatal error. Here’s an example:

class First {
    final public const TEST = '1';
class Second extends First {
    public const TEST = '2';

It will result in:

Fatal error: Second::TEST cannot override final constant First::TEST in %s on line %d

This flag can’t be used for a private constant as it can’t be accessed outside the class. Declaring both final and private constants together will result in a fatal error.

class First {
    final private const TEST = '1';
Fatal error: Private constant First::TEST cannot be final as it is never overridden in ... on line ...

Explicit Octal Numeral Notation

PHP supports several numeral systems, such as default decimal (base-10), binary (base-2), octal (base-8), and hex (base-16). However, numeral systems other than decimal have to use the following prefixes:

  • Hex – 0x
  • Binary – 0b
  • Octal – 0

PHP 8.1 introduces o (lowercase) and O (uppercase) prefixes for the octal numerals. This means that octal numerals are now more apparent and readable.

Have a look at these examples:

echo 77; // 77, numeric string
echo 077; // 63, old-format octal numeral
echo 0o77; // 63, octal numeral in PHP 8.1

Note that this new prefix is just an addition. The existing 0 prefix will work the same way in PHP 8.1.

$_Files : full_path Value for Directory Uploads

$_FILES is a super global variable containing names, sizes, and MIME types of files uploaded.

In the earlier version of PHP 8.1, $_FILES didn’t store the relative paths or the exact directory to the server. Therefore, you couldn’t upload an entire directory with an HTML file upload form.

The new full_path key solves the problem. It stores the relative paths and reconstructs the exact directory structure on the server, making directory uploads possible.

array(1) {
  ["myupload"]=> array(6) {
    ["name"]=> array(2) {
      [0]=> string(8) "file.txt"
      [1]=> string(8) "file.txt"
    ["full_path"]=> array(2) {
      [0]=> string(19) "foo/test1/file.txt"
      [1]=> string(19) "foo/test2/file.txt"
    ["tmp_name"]=> array(2) {
      [0]=> string(14) "/tmp/phpV1J3EM"
      [1]=> string(14) "/tmp/phpzBmAkT"
    // ... + error, type, size

There are two different file.txt files in the example above, but they are stored in different directories. If you use the $_FILES[“myupload”][“name”] array, there will be duplicate entries, because both files are specified with the same name.

However, the $_FILES[“myupload”][“full_path”] array contains the path for each file. It prevents the duplication that happens on the previous array.

WebP Lossless Encoding and Support for GdImage

In PHP 8.1, there’s a new lossless WebP encoding support for the GdImage object. It can be enabled using GD extension and libwebp version 0.2.0.

The new PHP constant to declare the lossless WebP encoding is IMG_WEBP_LOSSLESS, along with imagewebp function. In a PHP code, it looks like this:

$image = imagecreatefromjpeg('image.jpg');
imagewebp($image, 'image.webp', IMG_WEBP_LOSSLESS);

AVIF Image Support

In addition to WebP support, PHP 8.1 – using the GD extension, also supports the AVIF image format. It’s a relatively new image format, based on the AV1 video format. It provides higher compression and is royalty-free.

To add AVIF image support to the PHP image processing, you must compile the GD extension with AVIF support. You’ll also need libavid version 0.8.2 or higher for the GD extension.

Depending on your operating system, run the following commands:

  • Ubuntu/Debian apt install libavif-dev
  • RHEL/Fedora dnf install libavif-devel

The next step is to run the --with-avif flag with the ./configure script to compile PHP with the AVIF support.

./buildconf --force
./configure --enable-gd --with-avif

Then, run the following command to test whether the AVIF support is now enabled:

php -i | grep AVIF
If it’s enabled, you should see this output:
AVIF Support => enabled

DNS-over-HTTPS Support

DNS-over-HTTPS increases privacy and security by running the DNS resolution through the HTTPS protocol. It encrypts data sent by the client computer to the DNS resolver.

The Curl extension in PHP 8.1 now allows specifying a server for the DNS-over-HTTPS protocol. To use this new feature, you need to compile PHP with libcurl 7.62 or higher. That said, most operating systems already use Curl 7.68, so this shouldn’t be a problem.

To specify DNS-over-HTTPS, use the CURLOPT_DOH_URL option.

$ch = curl_init('');
curl_setopt($ch, CURLOPT_DOH_URL, '');

Note that the server URL has to be an HTTPS URL, and it will be validated when the request is executed. The request will fail if the URL for the DNS server is not valid or doesn’t provide a valid response.

We used Google’s public DNS server in the example above, but you can also pick any other public DNS-over-HTTPS server.

CurlStringFile Class for File Uploads from Strings

The Curl extension has a new CURLStringFile class that works similarly to the CURLFile class for file uploads. However, the CURLFile class only accepts a URI or a file path, whereas CURLStringFile accepts the file’s content instead of a path or a URI.

The new PHP 8.1 class makes it easier to create a file upload request using data stored in the memory. For example, use this to upload an image processed in PHP or stored in a PHP variable.

Here’s an example of using the CURLStringFile in a PHP code:

$txt = 'test content';
$txt_curlfile = new \CURLStringFile($txt, 'text/plain', 'test.txt');

$ch = curl_init('');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, ['file' => $txt_curlfile]);

Algorithm Support for MurmurHash3 and xxHash Hashes

PHP 8.1 introduces support to two non-cryptographic hashing algorithms – MurmurHash3 and xxHash. Non-cryptographic hashes are faster than most of the current PHP hashing algorithms, yet still provide great output dispersion and randomness.


For MurmurHash3, here are the variants supported:

  • murmur3a – 32-bit hash
  • murmur3c – 128-bit hash on x86 architecture
  • murmur3f – 128-bit hash on x64 architecture

To hash a string using MurmurHash3, use syntax like this:

hash(‘murmur3a’, ‘hash_example’);
hash(‘murmur3c’, ‘hash_example’);
hash(‘murmur3f’, ‘hash_example’);

You can update the values in sequence without hashing the input string as a whole. This is known as a streaming hash.

$context = hash_init(‘murmur3a’);
hash_update($context, 'hash');
hash_update($context, '_');
hash_update($context, ‘example');
$hash = hash_final($context);


PHP 8.1 supports the following xxHash algorithm:

  • xxh32 – 32-bit hash output
  • xxh64 – 64-bit hash output
  • xxh3 – 64-bit hash output
  • xxh128 – 128-bit hash output

Here’s the syntax to hash a string using xxHash:

hash(‘xxh32’, ‘hash_example’);
hash(‘xxh64’, ‘hash_example’);
hash(‘xxh3’, ‘hash_example’);
hash(‘xxh128’, ‘hash_example’);

Similar to MurmurHash3, xxHash is also a streaming hash that allows you to update the values in sequence.

$context = hash_init(‘xxh32’);
hash_update($context, 'hash');
hash_update($context, '_');
hash_update($context, ‘example');
$hash = hash_final($context);

Performance Improvements

PHP 8.1 adds a new feature to remove the overhead of class inheritance, the Inheritance Cache.

Previously, the opcache compiled and cached PHP classes separately. Then, it linked these classes at run-time during each request.

The linking process requires compatibility checks and borrowing methods, properties, or constants from parent classes and traits. This slows down the execution, although the result will be the same for each request.

The new Inheritance Cache solves this by linking all unique dependent classes and saving them in the opcache shared memory. This patch also removes limitations for immutable classes – all classes stored in the opcache are now immutable.

It results in fewer instructions for each request and an 8% improvement for the Symfony “Hello, World!” application.

MySQL_I_REFRESH_Replica Constant

MySQLi introduces a new constant for PHP 8.1 called MYSQLI_REFRESH_REPLICA. This constant has the same functionality as the existing MYSQLI_REFRESH_SLAVE constant.

The new name addresses racial insensitivity regarding the slave terminology. However, the existing MYSQLI_REFRESH_SLAVE constant won’t be removed or deprecated on PHP 8.1.

Changes in PHP 8.1

PHP 8.1 introduces some changes in syntax and functionalities. Most of them don’t significantly alter the existing features, but you should be aware of these changes if you continue using an existing PHP application on PHP 8.1.

HTML Encoding/Decoding Functions Convert Single Quotes and Substitute by Default

PHP 8.1 brings changes to functions that decode and encode HTML entities. HTML entity is a textual representation of a character in the HTML code. Common examples of HTML entities are the < and > characters that make up HTML tags such as <h1> or <b>.

The browser will interpret these characters as HTML code if you use them in an HTML document. Therefore, if you want the browser to show them in their original form, you must use their HTML entities.

For example, you want the browser to render <h1> as a text. Thus, you write it in the HTML document as &lt;h1&gt. &lt; and &gt; represent < and > respectively.

PHP has some functions to convert HTML characters into HTML entities:

  • htmlspecialchars – converts special characters to HTML entities.
  • htmlspecialchars_decode – converts HTML entities to special characters.
  • htmlentities – converts applicable characters to HTML entities.
  • html_entitity_decode – converts HTML entities to their related characters.
  • get_html_translation_table – returns the translation table for the htmlspecialchars and htmlentities functions.

In the previous PHP versions, these functions didn’t convert single quotes () and would return an empty string. This changed in PHP 8.1 as they now convert single quote characters to its HTML entity, &apos;.

The change is done by switching the ENT_COMPAT signature in the functions to ENT_QUOTES | ENT_SUBSTITUTE. Here are the new syntax:

  • htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE);
  • htmlspecialchars_decode($string, ENT_QUOTES | ENT_SUBSTITUTE);
  • htmlentities($string, ENT_QUOTES | ENT_SUBSTITUTE);
  • html_entity_decode($string, ENT_QUOTES | ENT_SUBSTITUTE);
  • get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES | ENT_SUBSTITUTE);

Interactive Shell Requires Readline Extension

In the previous PHP versions, it’s possible to open the interactive shell with the php -a option. However, the interactive features were not enabled since they require the readline extension.

Therefore, the php -a option was meaningless and the same as opening a non-interactive shell using the php command.

This is now fixed in PHP 8.1. Using the php -a option in the command line without the readline extension enabled will return an error message.

php -a
Interactive shell (-a) requires the readline extension.

However, if you have the extension enabled, it will open the interactive shell.

php -a

Interactive shell

php >
php > echo "Hello";
php > function test() {
php { echo "Hello";
php { }
php > test();

MySQLi Default Error Mode Set to Exceptions

Before PHP 8.1, the default error handling behavior of MySQLi was to silence the errors. Developers had to set their error handling functions, or the code wouldn’t follow the strict error handling.

In PHP 8.1, the default error handling is set to throw an exception.


This is a breaking change and, as the value is different, it will cause a compatibility issue. However, solve this by explicitly setting the error handling using the MYSQLI_REPORT function before making the first MySQLi connection.


Customizable Line Ending for CSV Writing Functions

In the previous PHP versions, the fputcsv() and SplFileObject::fputcsv functions are hard-coded to “\n” end-of-line character. PHP 8.1 introduces a new eol function to allow customizable end-of-line characters.

However, this parameter is optional, and the function will still use “\n” by default. If you have any existing code or application that doesn’t explicitly set an end-of-line character, it will still use “\n”.

If you want to use the eol parameter for a custom end-of-line character, here is a syntax example:

function fputcsv($stream, array $fields, string $separator = ",", string $enclosure = "\"", string $escape = "\\", string  $eol = "\n"): int|false {}
public function fputcsv(array $fields, string $separator = ",", string $enclosure = "\"", string $escape = "\\", string  $eol = "\n") {}

Note that you have to use double quotes with end-of-line characters such as \r, \n, or \r\n so they will be interpreted correctly.

Compact Function Calls Warning for Parameters With Non-string and Non-array String

The compact function is useful to create an array that contains variables and their values. Here’s an example of a compact function:

$low_cost = ‘Shared hosting’;
$mid_cost = ‘VPS hosting’;
$high_cost = ‘Dedicated hosting’;
compact (‘low_cost’, ‘mid_cost’, ‘high_cost’);
//[low_cost’ => “Shared hosting”, ‘mid_cost’ => “VPS hosting”, ‘high_cost’ => “Dedicated hosting”]

In its documentation, the compact function only accepts string parameters or arrays with string values. However, it might look like it accepts non-string and non-array parameters, but it actually ignores them.

If you declare strict-types with declare(strict_types=1), the function won’t perform a strict-type checking either.

This is changed in PHP 8.1, as the compact function now shows a warning if the argument contains a non-string or an array that doesn’t consist of string keys.

// []

PHP Warning:  compact(): Argument #1 must be string or array of strings, null given in ... on line …

That said, declaring strict types won’t have an impact, and the function still won’t throw a type error.

Phar Signature Algorithm Changed From SHA1 to SHA256

The Phar extension is used to pack a PHP application into an archive Phar file. This file can then be executed in any PHP environment.

Each Phar file contains a signature to verify the file’s integrity before the execution. The default signature algorithm for the Phar file used to be SHA1. However, in PHP 8.1, that changes to SHA256.

You can override the signature algorithm by using Phar::setSignatureAlgorithm and select MD5, SHA1, SHA256, or SHA512. Here’s a code snippet of that:

$filename = 'test.phar';
$phar = new Phar($filename);
$phar->setDefaultStub('index.php', '/index.php');

Note that all PHP versions since 5.3 support the SHA256 algorithm. This means all Phar files generated with PHP 8.1 will still be compatible with older versions. However, two new signature algorithms – OpenSSL-256 and OpenSSL-512 – are incompatible with older PHP versions.

JsonSerializable Implementation in SplFixedArray

SplFixedArray creates a fixed number of elements in the array and the keys will be integers in the range.

In previous PHP versions, the json_encode function encodes SplFixedArray instance as objects instead of arrays. This will change as SplFixedArray implements JsonSerializable interface and encodes it as arrays.

$array = new SplFixedArray(3);
$array[] = 'Web hosting';
$array[] = 'Domain name';
$array[] = 'SSL certificate';

echo json_encode($array);

Prior PHP 8.1, the output would be as follows:

{"0":"Web hosting","1":"Domain name","2":"SSL certificate"}

In PHP 8.1, it will be like this:

["Web hosting","Domain name","SSL certificate"]

Resources to Object Migrations

PHP development is planning to move resources to standard class objects.

One of the problems is that you can’t coerce or cast to resource type. Plus, the lack of a resource type prevents some resource-related functionalities from adding stricter types.

Another challenge is maintaining some resource types with features like internal state, memory handling, and reference counting. Therefore, in this case, PHP classes can be used instead.

Those are some of the reasons why the PHP team is migrating resource types to objects. However, resource types have been used extensively in PHP applications, so the team needs to ensure non-disruptive migration.

FTP Connection Resources Are Now FTP\Connection Class Objects

In the previous PHP version, we used ftp_connect() or ftp_ssl_connect() functions to create an FTP connection. These functions return a resource of type ftp.

In PHP 8.1, these functions return a FTP\Connection class. This new class is also declared final, meaning that you can’t extend it. It also minimizes the potential backward-compatibility problems if the PHP core changes the methods of the FTP\Connection class.

GD Font Identifiers Are Now GdFont Class Objects

In previous PHP versions, the imageloadfont()function from the GD extension was used to return a font-identifier resource ID in an integer.

In the new PHP version, the resource ID has migrated to a GdFont class.

To make the change less disruptive, all functions that accept a resource ID from imageloadfont() also accept the new GdFont class object.

file_info Resources Are Now finfo Objects

Some of the functions from the finfo extension return and accept resource objects with the file_info type. This is now changed to finfo class objects to align with PHP’s migration plan.

Note that the finfo class has been available since PHP 5.3. However, the finfo_*()functions accept or return resources. This is now fixed, and it is even possible to change the return values to objects.

These are the functions affected by the changes:

  • finfo_buffer()
  • finfo_close()
  • finfo_file()
  • finfo_open()
  • finfo_set_flags()

imap Resources Are Now IMAP\connection Class Objects

The IMAP connection resources were migrated to the IMAP\Connection class. Before this, the imap_open()function returned a resource object of typeimap.

To minimize the migration disruption, all functions that accept resources will work with the new IMAP\Connection class.

Similar to the FTP\Connection class, the IMAP\Connection class is also declared final to prevent it from being extended and causing backward-compatibility issues.

PostgreSQL Resources Are Now \PgSql\Connection, \PgSql\Result, and \PgSql\Lob Objects

PostgreSQL is a PHP database extension that uses resource objects for database connections and results. Three resource objects for PostgreSQL were migrated into class objects.

  • pgsql link resource to the PgSql\Connection class.
  • pgsql result resource to the PgSql\Result class.
  • pgsql large object resource to the PgSql\Lob class.

All PostgreSQL functions that return and accept resource objects now support the new class objects.

LDAP Resources Are Now LDAP\Connection, LDAP\Result, and LDAP\ResultEntry Objects

LDAP (Lightweight Directory Access Protocol) is a PHP extension for accessing Directory Servers, a special database that stores information in a tree structure.

This extension used three resource objects before PHP 8.1. These three objects were now migrated to new class instances.

  • ldap link resource to the \LDAP\Connection class.
  • ldap result resource to the \LDAP\Result class.
  • ldap result entry resource to the \LDAP\ResultEntry class.

All LDAP functions that return or accept resource objects now accept the new class objects.

Pspell Config Resources Are Now PSpell\Dictionary, PSpell\Config Class Objects

The Pspell PHP extension lets you check spellings and provides word suggestions. It uses pspell and pspell config resource object types with an integer identifier.

These resource objects were replaced by PSpell\Dictionary and PSpell\Config class objects in PHP 8.1. Here are the functions for creating PSpell\Dictionary and PSpell\Config objects:

  • pspell_new()
  • pspell_new_config()
  • pspell_new_personal()
  • pspell_config_create()

Like other migrated resources, all functions that accept pspell and pspell config resources will also accept PSpell\Dictionary and PSpell\Config.

Deprecations in PHP 8.1

PHP 8.1 deprecated several features, including more than ten already existing PHP functions. Here’s the rundown of what features and functions were deprecated in PHP 8.1.

$Globals Usage Restrictions

The $GLOBALS variable gives references to all global scope variables. It also contains “superglobal” values, including $_GET, $_SERVER, and $_ENV.

PHP 8.1 doesn’t fully deprecate the feature but restricts any modification to the $GLOBALS array, such as destroying the array, overwriting its value, and referencing the $GLOBALS variable.

This is because supporting some $GLOBALS variable behaviors requires complex technical work.

If you try to do these modifications on PHP 8.1, you’ll get a fatal error. Here’s an example of creating a mass change to the $GLOBALS variable:

$GLOBALS = [];
$GLOBALS = ['foo' => 1, 'bar' => 2];
$GLOBALS = get_new_vars();
$GLOBALS =& $new_vars;
list($GLOBALS) = [1];
foreach ($new_var_c as $GLOBALS) {}

It will result in the following error:

Fatal error: $GLOBALS can only be modified using the $GLOBALS[$name] = $value syntax in ... on line …

Note that read-only usage of the $GLOBALS variable and modification to individual array elements are still allowed.

Passing null to non-nullable Internal Function Not Allowed

In PHP, you shouldn’t be able to pass null values to non-nullable functions. The restriction worked for user-defined functions in previous PHP versions. Internal functions silently accepted null values.

To maintain consistency, the internal functions don’t accept null values anymore. Passing null to non-nullable arguments will return a deprecation notice.

var_dump(str_contains("foobar", null));
Deprecated: Passing null to argument of type string is deprecated

This deprecation is part of a plan to remove the functionality. According to its RFC, passing null values to non-nullable internal functions will result in TypeError. That will make the internal functions consistent with the current behavior of user-defined functions.

Deprecated Functions

PHP 8.1 also deprecates 11 PHP functions that may affect existing applications.

Deprecated Serializable Interface

PHP classes have their unique serialization logic. For instance, they can prevent specified sensitive data from serialization or reestablish remote server connections using serialized text. They use three different methods:

  • __sleep and __wakeup magic methods.
  • Serializable interface, Serializable::serialize, and Serializable:unserialize methods.
  • __serialize and __unserialize magic methods.

__serialize and __unserialize were introduced in PHP 7.4 to resolve the issue with the Serializable interface. However, the final plan is to remove the Serializable interface in the future PHP versions.

As for PHP 8.1, implementing Serialize interface on a class without the __serialize and __unserialize methods will result in deprecation error.

class Test implements Serializable{
    public function serialize() {}
    public function unserialize($data) {}

It will result in the following error message:

Deprecated: Test implements the Serializable interface, which is deprecated. Implement __serialize() and __unserialize() instead (or in addition, if support for old PHP versions is necessary) in ... on line …

However, it’s still possible to implement a Serializable interface as long as the class also implements the __serialize and __unserialize magic methods.

class Test implements Serializable{
    public function __serialize(): array {}
    public function __unserialize(array $data): void {}

    public function serialize(): array {}
    public function unserialize(string $data): void {}

The Serializable interface decodes the existing serialization payload, and the deprecation notice won’t appear. If there’s no need for PHP 7.3 support in the application, you can replace the Serializable interface with the __serialize and __unserialize magic methods.

Deprecated Autovivification on False

Autovivification is the auto-creation of arrays from false and null values. While this feature is widely used in PHP applications, creating an array automatically from false values is not ideal as it may be a sign of a buggy code.

Therefore, PHP 8.1 deprecates autovivification only for false values. Implementing autovivification on a false variable on PHP 8.1 will result in a deprecation notice.

$arr = false;
$arr[] = 2;

Deprecated: Automatic conversion of false to array is deprecated in

In future releases, the plan is to emit a fatal error on such implementation. As for undefined and null values, autovivification is still allowed.

Deprecated mysqli::get_client_info and mysqli_get_client_info ($param)

Passing parameters to the mysqli_get_client_info() function and the mysqli::get_client_info method are used to return the MySQLi client version through the client_info string constant.

Prior to PHP 8.1, there are four possible options to expose the client_info constant:

  • mysqli_driver properties
  • mysqli properties
  • mysqli_get_client_info() function
  • mysqli::get_client_info method

On the other hand, MySQL Native Driver (MySQLnd) exposes the constant with only two options – a constant and a function call.

Passing parameters to the mysqli_get_client_info() function and mysqli::get_client_info is now deprecated in an effort to unify the access method.

Using the get_client_info method in the mysqli class now emits a deprecation notice.

$info = $connection->get_client_info();
Deprecated: Method mysqli::get_client_info() is deprecated in …

Passing parameters to the mysqli_get_client_info() function will also result in a deprecation notice:

$info = mysqli_get_client_info($connection);
Deprecated: mysqli_get_client_info(): Passing connection object as an argument is deprecated in …

Instead of using the deprecated options, use the mysqli_get_client_info() function without parameters.

$info = mysqli_get_client_info();

Deprecated Implicit Incompatible Float to Int Conversion

Type coercion is common in PHP, and in most cases, they are helpful. However, the problem arises when a float number is converted to an integer as it may cause data loss. For example, 1.618 will be converted to 1, losing its fractional value.

PHP 8.1 addresses this issue by issuing a deprecation notice if a non-compatible float is implicitly coerced to an integer. The notice should prevent unintended fractional value loss.

Below is an example of declaring a function with an int return type. When the function is called, PHP will convert the value of 1.618 to an integer, generating a deprecation notice:

function get_integer(): int {
Return ‘1.618’:

PHP Deprecated:  Implicit conversion from float-string "6.8" to int loses precision in ... on line ...

The deprecation notice won’t be emitted if the value is an integer-compatible float. According to the RFC, here are the characteristics of an integer-compatible float:

  • It is a number.
  • It is in the range of a PHP integer.
  • It doesn’t have a fractional part.

Deprecated filter.default and filter.default_options INI Settings

The filter.default INI setting applies a filter to all PHP super globals according to its value. The default value for this setting is filter.default=unsafe_raw – it avoids any filter application on super globals.

The issue with this setting is that it can bring back the magic quotes functionality with filter.default=magic_quotes value. This feature was removed in PHP 5.4 due to an unreliable approach for user input sanitation.

To overcome this, assigning any value other than unsafe_raw to the filter.default INI setting will emit a PHP deprecation notice at start-up time. There’s no separate warning for filter.default_options, but these two INI settings will be removed in the next major PHP version.

Deprecated date_sunrise and date_sunset

The date_sunrise() and date_sunset() functions return the sunrise and sunset times by taking the default date.default_latitude and date.default_longitude values from the PHP INI settings. Unfortunately, no other functions use these INI settings, and the concept itself makes little sense.

As a result, date_sunrise() and date_sunset()functions are deprecated in PHP 8.1 and replaced by the date_sun_info() function. In accordance with the deprecation, the following INI settings are also deprecated:

  • date.default_latitude
  • date.default_longitude
  • date.sunrise_zenith
  • date.sunset_senith

Using date_sunrise and date_sunset functions in PHP 8.1 will emit a deprecation warning. However, setting the related INI values or using the ini_set function won’t generate the notice.

Deprecated strptime

The strptime() function parses date or time strings into arrays. Using this function on some Linux distributions like Alpine may result in unexpected behavior as it uses the musl C library. Moreover, this function is locale-based and can be affected if you run other code in a different thread.

The function’s manual recommends the use of date_parse_from_format() as it’s more consistent and doesn’t suffer from the same issues. Other alternatives are also available, like the more object-oriented DateTime::createFromFormat() and IntlDateFormatter::parse() for locale-dependent parsing.

As a result, the strptime() function is deprecated in favor of the more consistent options.

Deprecated strftime and gmstrftime

The strftime() and gmstrftime() functions are similar to strptime(). They format a time and date timestamp according to locale settings, except that gmstrftime returns the time in GMT.

Unfortunately, these functions also suffer similar issues as strptime() in terms of the formats supported and behaviors.

These functions are deprecated and replaced by the date()function or DateTime::format(). For locale-dependent use, IntlDateFormatter::format is the ideal replacement.

Deprecated mhash*() or hash extensions

mhash*() functions were brought to PHP by the mhash extension. However, this extension was replaced with hash extension in PHP 5.3. It’s only possible to use the functions with a hash extension and the PHP configuration option --enable-mhash.

Since PHP 7.4, the hash extension became a bundle with PHP. The mhash*()functions are still usable because the --enable-mhash option is still supported for compatibility purposes.

However, the PHP team deprecated mhash*() functions in PHP 8.1 and will remove them in PHP 9.0. The use of the following mhash*() functions will return a deprecation warning:

  • mhash
  • mhash_count
  • mhash_get_block_size
  • mhash_get_hash_name
  • mhash_keygen_s2k

Since hash is now the default extension, we recommend using hash*() functions instead.


PDO::FETCH_SERIALIZE is used as a flag for PDO statement fetch methods. Then, PDO is supposed to call unserialize on the data fetched from the database.

However, this functionality is unusable. Plus, with the deprecation of the Serializable interface, the PHP team also decided to deprecate PDO::FETCH_SERIALIZE.

Using PDO::FETCH_SERIALIZE in PHP 8.1 will return a deprecation notice.

Deprecated MySQLi: mysqli_driver->driver_version property

The mysqli_driver->driver_version property in MySQLi extension is supposed to return the driver version value. However, this property hasn’t been updated for 13 years despite many changes to the driver.

Therefore, this property was rendered meaningless and deprecated in PHP 8.1.

Hostinger web hosting banner


As a new major version, PHP 8.1 brings plenty of new features, changes, and deprecations that are good news for PHP developers.

For example, features like fibers should open new opportunities to be creative with coding execution. Even small changes like single quote conversion to HTML entities are very welcomed.

You can check PHP’s official documentation to see the complete list of RFCs or the deprecation list for more information on PHP 8.1.

Hostinger will support PHP 8.1, but we will keep the options for older PHP versions in case you need them. However, we highly recommend moving to PHP 8.1 to access the new improvements and maintain compatibility with frameworks and libraries.

The author

Leonardus Nugraha

Leo is a Content Specialist and WordPress contributor. Armed with his experience as a WordPress Release Co-Lead and Documentation Team Representative, he loves sharing his knowledge to help people build successful websites. Follow him on LinkedIn.