This article provides an overview of new features and deprecations in PHP 8.2. PHP 8.0 already set a strong base for these and upcoming improvements. Original plan to release PHP 8.2 was on November 24th 2022, but it was posponed for December 8th 2022.

In this article, we cover what is new in PHP 8.2. Our focus is on new features, deprecation and breaking changes. At the end of the article you will find list of the official references documenting all changes in the details.

PHP 8.2: New features, breaking changes and deprecated features

What is new in PHP 8.2?

Readonly classes

PHP 8.1 introduced readonly properties. With this, developers are able to define class properties that are initialized only once in the scope of their declaration. PHP 8.2 built upon this by adding feature to declare the entire class as readonly.

How is this helping developers?

Well, previously in PHP 8.1 we might have class with many readonly parameters.

class ClassWithReadOnlyParameters
{
    public readonly int $parameter1;
    public readonly string $parameter2;
    public readonly int $parameter3;
    public readonly array $parameter4;
    public readonly int $parameter5;
}

In PHP 8.2 we can declare whole class as readonly. This way, all properties within the class will automatically inherit readonly state.

readonly class ClassWhichIsReadOnly
{
    public int $parameter1;
    public string $parameter2;
    public int $parameter3;
    public array $parameter4;
    public int $parameter5;
}

It is not possible to declare readonly on specific PHP features:

  • Interfaces
  • Traits
  • Enums

However, it is possible to declare abstract and final classes as readonly.

abstract readonly class AbstractClass {}
final readonly class FinalClass {}

Finally, it is possible to declare readonly on a class that has no properties. In this case, child classes would be able to declare their readonly properties.

null, true, and false as standalone types

PHP 8.2 continues improving on type safety by introducing null and false as standalone types.

What does a standalone type means?

Previously, we were able to use these types as a part of the union, i.e. int|false or string|false.

function calculateNumberOrReturnFalse(): int|false
{
    // ...business logic...
    if ($calculationPossible) {
        return $calculation; // return integer
    }

    return false;
}

Now, from PHP 8.2 it is possible to use null and false as standalone types.
For example:

function alwaysReturnTrue(): false
{
    // ...business logic...
    return false;
}

Of course, new standalone types are not intended as a replacement for proper error handling, throwing and catching errors. However, if cases of properly written functions with valid reason to always return true, false or null, it is possible to strictly type return parameter.

Finally, this change comes with a caveat. It is not possible to use true and false types in the union such as true|false. This makes sense, because union of the true and false would be redundant feature because it is essentially a boolean type.

Disjunctive Normal Form Types

Disjunctive Normal Form (DNF) is standardized way to organize boolean expressions. Meaning, with disjunctive normal form types it is possible to structure boolean expression with ANDs and ORs.

To understand this specific feature, let’s quickly refresh our knowledge on two PHP features:

  • union types
  • intersection types

PHP 8.0 introduced Union Types and this feature allows declaring property, parameter or return type with multiple types.

private int $commonSingleTypeVariable;
private int|float $twoTypesVariable;

PHP 8.1. introduced Intersection Types, as a feature that is somewhat opposite of union types. Intersection Types allows declaring that a specific value belongs to all of the declared classes or interface types.

public function exampleFunction(Interface1&Interface2 $bothStringAndIntValue)
{
    // business logic
}

Now, PHP 8.2 introduced Disjunctive Normal Form Types (DNF) as a way to combine union types and intersection types. However, there is a strict rule to this. When combining union and intersection types, intersection types must be grouped within the brackets.

function disjunctiveNormalFormTypesExample((Interface1&Interface2)|null $wellTypedVariable) {

    if (null === $wellTypedVariable) {
        return $something; // or null for example
    }

    // ... otherwise continue with the business logic
}

The (Interface1&Interface2)|null $wellTypedVariable represents DNF and it means: variable $wellTypedVariable must implement methods from both Interface1 and Interface2 or it can be a nullable value.

Traits now support constants

As of PHP 8.2 traits support constants.

trait ReusableCode
{
    public const IMPORTANT_CONSTANT = 3600;
}

However, it is not possible to use constant within the trait itself. It is only possible to use this constant within the class using that trait.

For example:

trait ReusableCode
{
    public const IMPORTANT_CONSTANT = 3600;

    public function reusableFunction(int $importantVariable): string
    {
        // It is NOT allowed using constant within the trait
        if ($importantVariable > ReusableCode::IMPORTANT_CONSTANT) {
            return false;
        }
    }
}

// However once we use the trait within the class
class ExampleClass
{
    use ReusableCode;
}

// It is allowed to use constant from the trait used within the class
ExampleClass::IMPORTANT_CONSTANT;

Redacting parameters in back traces

When our codebase ends up in the error state this generates back trace with all the details including values stored within the parameters. Commonly we forward these back traces to different services for keeping tracks of errors and warnings. These are services such as Sentry.io, Elastic, Datadog, Amazon CloudWatch etc.

While this might be ok for development and testing environments, in the production environments we are dealing with sensitive data. Very often we have to write different filtering mechanisms to make sure sensitive parameters and sensitive data are not exposed within the logs.

PHP 8.2 comes with a great feature to redact parameters in back traces. It is super simple. We can annotate sensitive attribute with #[\SensitiveParameter] and this will indicate PHP to filter out this value in back traces. Sensitive parameter will be replaced with Object(SensitiveParameterValue)).

Original RFC (see references) has a fantastic examples on how this is working.

Here is a basic use case:

function test(
    string $foo,
    #[\SensitiveParameter] string $privateKey,
    string $baz
) {
    throw new \Exception('Any Exception');
}

test('foo', 'sensitive_parameter-private_key', 'baz');

/*
Fatal error: Uncaught Exception: Error in example.php:10
Stack trace:
#0 test.php(...): test('foo', Object(SensitiveParameterValue), 'baz')
*/

As you can see, sensitive_parameter-private_key will not be part of the back trace. Instead it will be replaced with Object(SensitiveParameterValue). Neat!

Random Extension 5.x

In short, PHP 8.2 introduces a new random number generator that solves many known problems. New random number generator is a single Randomizer class that provides different randomization methods. New Randomizer performs better and does not rely on global state.

For example:

$randomizer = new Random\Randomizer(); // default engine is Random\Engine\Secure()

$randomInt = $randomizer->getInt(); // replaces mt_rand()
$randomRangedInt = $randomizer->getInt(1,100); // replaces mt_rand() and random_int()
$randomBytes = $randomizer->getBytes(10); // replaces random_bytes()
$shuffledArray = $randomizer->shuffleArray(['1','2','3']);  // replaces shuffle()
$shuffledString = $randomizer->shuffleString('abcdefg'); // replaces str_shuffle()

Fetch properties of enums in const expressions

In short, PHP 8.2 allows the use of ->/?-> operator to fetch properties of enums in constant expressions. According to RFC for this feature, the primary motivation was to allow fetching the name and value properties in the places where it was not possible before, like in arrays.

For example:

// This is not possible before PHP 8.2
const C = [self::B->value => self::B];

What features are deprecated in PHP 8.2?

Deprecate Dynamic Properties

Dynamic propertes in PHP allowed programmers to instantiate an object and to assign a property to that object that does not exist in the original class. We call this dynamically setting and retreiving undeclared class properties. From PHP 8.2 this will throw deprecation warnings and the plan is to deprecate it in PHP 9.0.

class Foo {
 private string $bar;
}

$instance = new Foo();

// From PHP 8.2 this will throw a deprecation warning 
// From PHP 9.0 this will throw an ErrorException
$instance->newProperty = 'anything';

PHP 8.2 provides a way to silence deprecation warnings for creation of dynamic properties by using #[AllowDynamicProperties] notation.

#[AllowDynamicProperties]
class Foo {
 private string $bar;
}

$instance = new Foo();

// Will not throw a deprecation warning
$instance->newProperty = 'anything';

Deprecate Partially Supported Callables

Up to PHP 8.1 there are callables that are accepted by the callable type, also by the is_callable() function and call_user_func(). However, they are not supported by $callable().

List of these callables

"self::method"
"parent::method"
"static::method"
["self", "method"]
["parent", "method"]
["static", "method"]
["Foo", "Bar::method"]
[new Foo, "Bar::method"]

However, to make things easier, most of the deprecated callables have a straightforward replacement, “self” is to be replaced with self::class.

"self::method"       -> self::class . "::method"
"parent::method"     -> parent::class . "::method"
"static::method"     -> static::class . "::method"
["self", "method"]   -> [self::class, "method"]
["parent", "method"] -> [parent::class, "method"]
["static", "method"] -> [static::class, "method"]

Deprecate functions utf8encode() and utf8decode()

These two built-in PHP functions encoded “Latin 1” (ISO-8859-1) to “UTF-8” and decode UTF-8 to Latin. Functions are useful, but they have downsides. Firstly, their names suggest that any provided input can be converted to UTF-8 which is not correct. Secondly, functions lack error messages, meaning it is not easy to recognize incorrect usage.

RFC states that, based on the survey from April 2021 conducted on 1000 packages sorted by Packagist popularity, out of 37 packages using these functions only 4 libraries were clearly using them correctly.

Finally, RFC also suggests alternatives to removed functionality. Most interesting one is the most used one, for character conversion, stating that ext/mbstring is probably one that is most commonly used.

ext/mbstring: $utf8 = mb_convert_encoding($latin1, 'UTF-8', 'ISO-8859-1'); and $latin1 = mb_convert_encoding($utf8, 'ISO-8859-1', 'UTF-8');

ext/intl: $utf8 = UConverter::transcode($latin1, 'UTF8', 'ISO-8859-1'); and $latin1 = UConverter::transcode($utf8, 'ISO-8859-1', 'UTF8');

ext/iconv: $utf8 = iconv('ISO-8859-1', 'UTF-8', $latin1); and $latin1 = iconv('UTF-8', 'ISO-8859-1', $utf8);

Deprecate ${} String Interpolation

In the PHP programmers embed variables in strings with double-quotes and in heredoc. We call this string interpolation. There are several ways to accomplish string interpolation.

  1. Directly embedding variables “$foo”
  2. Braces outside the variable “{$foo}”
  3. Braces after the dollar sign “${foo}”
  4. Variable variables “${expr}” which is equivalent to (string) ${expr}

While options 1 and 2 to have their pros and cons, in the RFC describes options 3 and 4 as easily confusing due to overlapping syntax. Furthermore, option 3 is less capable than 1 and 2, and option 4 has completely different semantic.

Therefore, RFC proposed to deprecate options 3 and 4.

var_dump("${foo}");
// Deprecated: Using ${} in strings is deprecated

var_dump("${(foo)}");
// Deprecated: Using ${} (variable variables) in strings is deprecated

RFC also proposes a migration paths, as the easiest way to implement this change.

Migrating “braces after the dollar sign” to “braces outside the variable”, option 3 to option 2.
Straightforward, simply by moving the dollar sign inside the braces.

"${foo}" => "{$foo}"
"${foo[expr]}" => "{$foo[expr]}"

Migrating “variable variables” to “braces outside the variable”, option 4 to option 2.
Also straightforward, simply adding a braces around the whole interpolation.

"${foo->bar}" => "{${foo->bar}}"

Deprecate mbstring Functions for Base64/QPrint/Uuencode/HTML Entities

PHP 8.2 deprecated the use of QPrint, Base64, Uuencode, and HTML-ENTITIES ‘text encodings’ for all mbstring functions.

This deprecation is happening for several reasons:

  1. These cases deal with a sequences of raw bytes, so it is not clear what the correct value for mbstring function is.
  2. PHP has existing and separated implementation to handle these encodings.

Using mbstring functions with deprecated values will result in some of the following warnings

Deprecated: mb_check_encoding(): Handling HTML entities via mbstring is deprecated; use htmlspecialchars, htmlentities, or mb_encode_numericentity/mb_decode_numericentity instead...

Deprecated: mb_strcut(): Handling HTML entities via mbstring is deprecated; use htmlspecialchars, htmlentities, or mb_encode_numericentity/mb_decode_numericentity instead...

Deprecated: mb_convert_encoding(): Handling QPrint via mbstring is deprecated; use quoted_printable_encode/quoted_printable_decode instead...

Deprecated: mb_convert_encoding(): Handling Base64 via mbstring is deprecated; use base64_encode/base64_decode instead in...

Deprecated: mb_convert_encoding(): Handling HTML entities via mbstring is deprecated; use htmlspecialchars, htmlentities, or mb_encode_numericentity/mb_decode_numericentity instead...

Deprecated: mb_strlen(): Handling Base64 via mbstring is deprecated; use base64_encode/base64_decode instead...

Deprecated: mb_strlen(): Handling HTML entities via mbstring is deprecated; use htmlspecialchars, htmlentities, or mb_encode_numericentity/mb_decode_numericentity instead...

All You Need to Know About the New PHP 8.2 Upgrade: Quick summary

PHP 8.2 is released on 8th December. It comes with many nice features such as readonly classes, traits supporting constants, redacting parameters from back traces etc. As in the previous versions PHP is aiming to cleanup legacy issues and to standardize coding approaches which we can see by deprecated features such as dynamic properties, partially supported callables and streamlining string interpolation. PHP 8.2 also improves security and performances, for example by introducing new random number generator.

What is new in PHP 8.2?

  1. readonly classes allows us to define whole class as read only, which is automatically applied to all parameters in this class.
  2. null, true and false as standalone types allows us to set return type to null,true or false
  3. Disjunctive normal form types allows us to combine union types with intersection types in boolean expressions
  4. Traits are now supporting constants, but they can not be used inside of traits, only inside of classes using traits
  5. Sensitive parameters can be annotated and PHP will automatically mask them in the back traces
  6. Random number generator is improved, major performance problems are solved in new Random Extension 5.x
  7. It is now possible to fetch enums properties inside of const expressions, i.e. within the arrays

What is deprecated in PHP 8.2?

  1. Properties introduced outside of the class description “Dynamic Properties” are no longer allowed.
  2. Callables that were not accepted by all PHP features “Partially Supported Callables” are no longer allowed.
  3. Utf8encode() and Utf8decode() are now deprecated becase of confusing functionallity and historical misuse.
  4. Out of 4 possible string interpolation approaches, 2 approaches are deprecated as less capable and redundant.
  5. Mbstring can no longer be used for Base64/QPrint/Uuencode/HTML Entities because there are dedicated functions for this.