What’s Changing in PHP 8.2?

Matthew Setter

Written by Web Developer and Technical Educator

September 5, 2022
What’s Changing in PHP 8.2?

With PHP 8.2’s release date, November 24 this year, drawing ever closer, let’s have a look at some of the key changes that you can expect to see.

Deprecation of Dynamic Properties


In PHP 8.2, setting dynamic class properties is deprecated.

There are a number of motivations behind this change, but the key ones are to:

  • Be explicit when using dynamic properties

  • Prevent mistakes associated withthem.

This change should make code easier to maintain and easier for static analyzers to introspect. Have a look at the following code, which attempts to set the dynamic property city on the User class.

php
class User
{
  public string $firstName;

  public function __construct(string $firstName)
  {
    $this->firstName = $firstName;
  }
}

$user = new User("Matthew");
$user->city = "Nuremberg";

When run, the following deprecation notice would be issued.

php
// Fatal error: Uncaught Error: Cannot create dynamic property User::$city

If dynamic properties are desired or required, such as for backwards-compatibility, a class can be marked with the new #[AllowDynamicProperties] attribute, introduced in the RFC, as in the following example.

php
#[AllowDynamicProperties]
class User
{
  public string $firstName;

  public function __construct(string $firstName)
  {
    $this->firstName = $firstName;
  }
}

It’s worth noting that PHP’s magic __get and __set methods can still be used after this change.

PHP 8.2 Readonly Classes


PHP 8.1 introduced readonly properties, which allows class properties to be initialized once, but not to be changed afterward. PHP 8.2 takes this further by allowing whole classes to be marked readonly, as in the example below.

php
readonly class User {
  public string $firstName;
  public string $lastName;
  public string $emailAddress;

  public function __construct(
      string $firstName,
      string $lastName,
      string $emailAddress
  ) {
    $this->firstName = $firstName;
    $this->lastName = $lastName;
    $this->emailAddress = $emailAddress;
  }
}

When a class is marked readonly', **all** of its properties are marked readonly`, avoiding the need to mark each one individually.

However, before you get too excited, it’s worth knowing the limitations of this functionality. These are:

  • Dynamic, untyped, and static properties are not allowed.

  • The #[AllowDynamicProperties] attribute cannot be used, otherwise a Fatal error similar to Fatal error: Cannot apply #[AllowDynamicProperties] to readonly class will be thrown.

  • A readonly class can only extend a readonly parent class.

Regardless of these limitations, this change should be a very welcome one for developers who appreciate the benefits of immutability.

PHP 8.2 New no-capture modifier


A new /n or No-capture modifier for the preg_* functions

This is another exciting change – if you’re a fan of regular expressions (or regexes). Before I dive into it, take a look at the example below.

php
$regex = '/(\(\d\)\d{3})-(\d{4})-(\d{4})/';
$number = '(0)123-4567-8901';
preg_match($regex, $number, $matches);

var_dump($matches);

The expression in $regex uses capturing groups. These allow the regular expression to be broken up into sub-expressions. In this case, the sub-expressions match the three parts of a standard German phone number, stored in $number.

If you ran the code, the variable $matches would contain an array with an entry for the entire phone number, along with one for each of the three parts of the phone number, without the separating hyphens. In this case:

php
array(5) {
  [0]=>
  string(16) "(0)123-4567-8901"
  [1]=>
  string(6) "(0)123"
  [2]=>
  string(4) "4567"
  [3]=>
  string(4) "8901"
}

However, you may not be interested in each of the three components. Perhaps only the first and last. So, to skip the second one you’d mark it as a non-capturing group, by prefixing it with ?:, as in the following example.

php
$regex = "/(\(\d\)\d{3})-(?:\d{4})-(\d{4})/"

Then, the script would output the following:

php
array(5) {
  [0]=>
  string(16) "(0)123-4567-8901"
  ["prefix"]=>
  string(6) "(0)123"
  [1]=>
  string(6) "(0)123"
  ["area_code"]=>
  string(4) "8901"
  [2]=>
  string(4) "8901"
}

Marking sub-expressions you’re not interested in this way is fine, until you want to disable all capturing groups, save a small handful, in larger, more complex regular expressions. The new /n modifier helps make that a lot easier, by disabling all capturing groups unless they’re named.

Named capturing groups work just like capturing groups, except that the array storing the matches also has a string key for every match. Take the following example.

php
$regex = "/(?P<prefix>\(\d\)\d{3})-(?P<local_code>\d{4})-(?P<area_code>\d{4})/";
preg_match($regex, '(0)123-4567-8901', $matches);
var_dump($matches);

Here, the first sub-expression is named “prefix”, the second “local_code”, and the third “area_code”. When run, the script will output the following:

array(7) {
  [0]=>
  string(16) "(0)123-4567-8901"
  ["prefix"]=>
  string(6) "(0)123"
  [1]=>
  string(6) "(0)123"
  ["local_code"]=>
  string(4) "4567"
  [2]=>
  string(4) "4567"
  ["area_code"]=>
  string(4) "8901"
  [3]=>
  string(4) "8901"
}

Now, if you only want the middle sub-expression (“local_code”), don’t name the other two sub-expressions and use the new \n modifier, as in the following example.

$regex = "/(\(\d\)\d{3})-(?P<area_code>\d{4})-(\d{4})/n";

Then, the $matches array will only have a match for the entire expression, plus the one, named sub-expression (with a numeric and string key).

Null and false are Standalone Types


Null and false are standalone types

While both of these have been used as return types for quite some time in PHP, and they are allowed as part of a union return type, in PHP 8.2 they’ll be standalone types, able to to be used almost like any other existing PHP type.

Here’s an example of what you can expect.

php
class User
{
  public string $firstName;
  public null $someProperty = null;

  public function __construct(string $firstName, false $processed)
  {
    $this->firstName = $firstName;
    $this->processed = $processed;
  }

  public function updateUser(array $userData): true
  {
    // ...update user details

    return true;
  }

  public function nullableState(): null {
    // ...function processing
  }

  // ...other functionality
}

What you can’t do, however, which hopefully makes sense, is mark a null as nullable, as in ?null.

True is now a Type


In addition to null and false, true will also become a standalone type. The core motivation behind this change is for completeness of PHP’s type system. Given that, you can use true standalone or as a union type for a function parameter or property, as well as for a function’s return type.

Here are a few examples of how it can be used.

php
class User
{
  public string $firstName;

  public function __construct(string $firstName)
  {
    $this->firstName = $firstName;
  }

  public function updateUser(array $userData): true
  {
    // ...update user details

    return true;
  }

  public function getUserDetails(true|string $detailType): array
  {
    // ...return user details
  }
}

But before you get all excited about this change, there are some restrictions to be aware of:

  • true cannot be used as a return type if it would be redundant, such as true|false. This is because, in that instance, bool should be used.

  • true will not be coerced to another type.

php
class User
{
  public string $firstName;

  public function wasUserUpdated(): true|false
  {
    // ...determine if user was updated or not
  }
}

In the above example, wasUserUpdated will result in a Fatal error as true and false are redundant because of the bool type.

Redact Parameters in Back Traces


One of the most common practices in PHP is to display all log information in development, but to hide it in production, logging the information instead. However, if the logs are hacked any sensitive data contained in them will be compromised.

For example, if an exception was thrown during user login, it’s entirely feasible that the logs would then contain username and passwords.

This change helps prevent this attack vector, by allowing sensitive information to be redacted, replaced with a new \SensitiveParameterValue object. To use it, prefix any sensitive function parameters with the new #[\SensitiveParameter] attribute.

Take the following fictitious example.

php
function login(
  #[\SensitiveParameter] string $username,
  #[\SensitiveParameter] string $password,
  string $hostname,
  int port
) {
    throw new Exception('Something went wrong');
}

If the method was called, you’d see something similar to the following.

Fatal error: Uncaught Exception: Something went wrong in User.php:25
Stack trace:
#0 User.php(30): User->login(Object(SensitiveParameterValue), Object(SensitiveParameterValue))
#1 {main}
  thrown in User.php on line 25

Thissmall addition to standard coding practices, code can still be instrumented, but in a secure way.

Deprecate ${} string interpolation


Deprecate ${} string interpolation

In PHP, you can perform string interpolation in four ways, which you can see in the example below.

php
$user = "Matthew Setter";
echo "$user\n";
echo "{$user}\n";
echo "${user}\n";
echo "${user}\n";

While this affords significant flexibility, it’s a problem for two key reasons:

  • The syntax is not consistent across all four forms.

  • The latter two are not that commonly used.

So this change seeks to bring a greater sense of both consistency and order to string interpolation in PHP, by deprecating the third and fourth forms.

As with other deprecated functionality, if you attempt to use them you will see a deprecation notice. For example:

// Deprecated: Using ${} in strings is deprecated
// Deprecated: Using ${} (variable variables) in strings is deprecated

MySQLi can no longer be compiled with libmysql


PHP has had two MySQL libraries for quite some time, mysqlnd and libmysql. However, in PHP 8.2, support for building the mysqli extension against libmysql will be removed.

There are a number of reasons for this, with the key ones being mysqlnd:

  • Uses PHP’s memory management model, whereas libmysql doesn’t.

  • Can properly allocate memory based on a column’s size.

  • Has fewer memory leaks.

  • Has more tests.

  • Is available on Windows, as well as all other platforms.

In short, mysqlnd is a better library offering improved performance over libmysql. What’s more, mysqlnd has been the default since PHP 5.4, so it shouldn’t have much of an impact.

Deprecate utf8_encode and utf8_decode


The final change in this list is the deprecation of utf8_encode() and utf8_decode(). If you’re not familiar with these two functions, they convert strings encoded in ISO-8859-1 (or “Latin 1”) to and from UTF-8. They don’t, as is commonly assumed, convert from any other encoding to UTF-8, such as ISO-8859-15 (or “Latin 9”).

To put this into better context, quoting the RFC, of 1,000 popular packages on Packagist:

  • 4 libraries clearly using them correctly

  • 20 using them without clear understanding

  • 1 using on the output of strftime, which may be correct

  • 7 using utf8_decode to count codepoints in a UTF-8 string

  • 1 using them as “armour” (explained below)

  • 3 using them in context where they will do nothing

  • 2 providing polyfill implementations of the functions (patchwork/utf8 and symfony/polyfill-php72)

  • 4 providing stubs for static analysis

So to help avoid these issues, the functions will be deprecated in PHP 8.2 and moved back to the XML extension in PHP 9.0. The RFC suggests that it will help encourage developers to use functionality, such as mb_convert_encoding, that actually do what many believe these functions do.

That’s a wrap


While these aren’t the only changes coming up in PHP 8.2, they’re some of the most notable ones. Have a look at the upgrade notes to see the entire list of changes.

Frequently Asked Questions


How secure is PHP?

PHP has an excellent security reputation and the developers are constantly making updates. Plus, you’ll benefit from additional security measures for your site by opting for a managed hosting package.

Can I contact the PHP developers?

Not directly, however, over on PHP.net you’ll find an extensive range of patch update information, forums, and articles that will answer the majority of your technical questions.

What content management sytems use PHP?

All of the most popular content management systems are compatible with PHP including, WordPress, Joomla, Drupal, and Magento

Who is responsible for PHP bugs and security issues?

Any fixes will primarily be covered by the PHP developers, and regular updates are pushed out. Under a managed hosting solution, Verpex will make sure any updates are applied to your site as soon as they’re ready.

Matthew Setter
About the Author
Matthew Setter

I’m a software developer with over 15 years of experience, designing, developing, and deploying web-based applications. I’m also an experienced online educator, technical writer, and trainer, having written over 150 articles, published 3 books and 6 online courses, and launched two podcasts in the last ten years. The author of Mezzio Essentials and Deploy With Docker Compose.

View all posts by Matthew Setter
Jivo Live Chat