Quick Links
PHP 8.2was released inDecember 2022 as the latest minor version in the PHP 8.x release cycle. It adds new features that build upon the capabilities ofprevious versions, further simplifying the development experience. In this article, we’ll tour each of the major changes and show how they’ll make your code easier to maintain.
Type System Improvements
PHP has been gradually evolving its type system towards a more strongly typed model over the past several releases. 8.2 includes two enhancements that allow types to be even more expressive.
Disjunctive Normal Form: Combined Unions and Intersections
Union and intersection types can now be combined anywhere a type is accepted, such as function parameters and return values. The complete type definition must be written using booleandisjunctive normal form notation (DNF). This means intersection types have to be wrapped in parentheses to be valid.
The following function allows you to pass either an array, or an object that implements both the
and
// …
}
This facilitates more powerful type definitions, similar to the example function shown above. Prior to PHP 8.2, you’d have to write two separate functions to achieve the same effect, one for each of the types in the union.
The values
,
, and
are now accepted as standalone types. They can be used in any type definition to indicate that the specific value will be returned:
function alwaysReturnsFalse() : false {}
function alwaysReturnsNull() : null {}
In realistic use cases, you’ll normally write these values as part of a union type. Handling errors by returning
instead of a regular value is a common pattern in both the PHP core and userland code, for example. Now you can properly describe this behavior in your return type definition:
- Tries to connect to the database; returns
falseon failure
*/
function connectToDatabase() : DatabaseConnection|false;
/**
- Tries to close the database connection; returns an error code on failure
function closeDatabaseConnection() : true|DatabaseErrorCode;
Readonly Classes
Readonly propertieswere one of the headline features of PHP 8.1. They let you enforce immutability for class properties after their initial assignation:
public function __construct(
public readonly string $Username,
public readonly bool $Admin) {}
$user = new User(
Username: “howtogeek”,
Admin: true
);
// Error - The property is readonly
$user -> Username = “Demo”;
In practice, many classes want all their properties to be readonly. Now you can mark the entire class as readonly, which lets you drop the
public string $Username,
public bool $Admin) {}
Besides saving some repetitive typing, making a class readonly adds a few other constraints:
Readonly classes make it more convenient to write data transfer objects and other structures that are intended to be immutable. Their use is not obligatory though: you can still create partially mutable classes by continuing to use the
keyword on individual properties.
Enum Properties Can Be Consumed In Constant Expressions
Enum properties can now be used in constant expressions. The following code, which was not supported in the version ofenums shipped with PHP 8.1, is now legal:
case Draft = 1;
case Published = 2;
const VALUES = [self::Published -> value => self::Published];
The
property of the enum’s
case is accessed without throwing an error, because PHP’s engine is aware its value can never change.
Traits Can Define Constants
It’s now possible for traits to define constants, something that was omitted from previous PHP releases. Any constants that are written within a trait will be visible in the scope of classes that use it.
This example demonstrates the possibilities for accessing a trait’s constants, within the trait’s code, the code of the class that uses it, and from the global scope:
public const LOG_TYPE_JSON = 1;
public function log(string $message) : void {
if ($this -> getLogType() === self::LOG_TYPE_JSON) {
abstract public function getLogType() : int;
final class NewUserEvent {
use Loggable;
public function getLogType() : int {
return self::LOG_TYPE_JSON;
// “1”
echo NewUserEvent::LOG_TYPE_JSON;
Note that you cannot access the constant’s value directly on the trait, from the global scope. The following code will throw a “cannot access trait constant directly” error:
A Modern Object-Oriented Approach to Random Numbers
PHP 8.2 adds a new object-orientedrandom number generation extension. It allows you to produce random numbers using several modern generation engines. This example demonstrates how to produce a random integer between
use Random\Randomizer;
$randomizer = new Randomizer(
new Xoshiro256StarStar(
hash(
algo: “sha256”,
data: “256-bit seed value”,
binary: true
)
/** Generated with xoshiro256** */
echo $randomizer -> getInt(1, 100);
If you subsequently want to switch to a different engine, you need only replace the parameter that’s passed to your
$randomizer = new Randomizer(new Mt19937(1234));
/** Generated with Mt19937 */
Prevent Passwords Leaking Into Stack Traces and Error Logs
Code like the following is a standard feature in many PHP codebases:
string $host,
int $port,
string $username,
string $password) : void {
This poses a challenge when the function throws an uncaught exception. PHP’s stack traces include the values of function parameters, so the password ends up being emitted to your error logs. This is a security risk.
PHP 8.2 addresses the problem by providing a new attribute that marks parameters as “sensitive.” Applying the
attribute to any parameter will redact its value from stack traces:
#[\SensitiveParameter]
#0 index.php(1): connectToDatabase(“localhost”, “3306”, “demo”, Object(SensitiveParameterValue))
#1 {main}
It’ll be worthwhile auditing your codebase to find parameters with sensitive values after you upgrade. Add the attribute to any instances you find. This will help prevent leaked logs from compromising your environment’s security.
Dynamic Class Properties Have Been Deprecated
PHP has historically allowed you to set properties on object instances without first declaring them:
$user = new User();
$user -> Username = “howtogeek”;
This behavior is often problematic. Neither you nor automatedstatic analysis toolscan assert which properties the instances will have.
Dynamic properties also facilitate typos which can be difficult to spot:
public string $Username;
$user -> Usernamee = “howtogeek”;
You’ve mistakenly set theUsernameeproperty, but PHP won’t throw an error or provide any help. This causes you to spend time debugging why the correctUsernameproperty doesn’t have the value you expect.
PHP 8.2 addresses these problems by deprecating dynamic properties. Going forwards, you should define all properties a class can accept, either individually or aspromoted constructor properties.
As this is a deprecation and not a removal, existing code will continue to function for now. A deprecation notice will be logged each time a dynamic property is read or set. The removal, which could occur in PHP 9.0, will be a breaking change. Any code that relies on dynamic properties will stop working.
There is a way to continue using dynamic properties, however. You can explicitly opt-in a class to dynamic properties by marking it with the new#[\AllowDynamicProperties]attribute. This will continue to work in PHP 9.0 too. It solves some of the legitimate use cases for dynamic properties, such as config stores, and temporary maps and caches.
final class ConfigStore {}
$store = new ConfigStore();
$settings = json_decode(file_get_contents(DIR . “/settings.json”), true);
foreach ($settings as $key => $value) {
$store -> $key = $value;
Use of this attribute should be discouraged except in cases similar to this example, where the class is dynamic by nature. The deprecation of dynamic properties is intended to help you write safer code that’s less vulnerable to mistakes.
This change does not affect classes that use the__get()and__set()magic methods. They’ll continue to work as normal, allowing you to implement your own routines when an undefined property is read or set. Instances of\stdClass()also continue to support dynamic properties.
Summary
PHP 8.2 is an exciting new release of usability improvements. It includes time-saving readonly classes, more flexible type definitions, and small adjustments that enhance the developer experience, such as constants in traits, enum values in constant expressions, and sensitive parameter value redaction for stack traces.
The upgrade is available now through all supported PHP distribution channels. Moving to 8.2 should be straightforward for most modern codebases that are already written using PHP 8.x features and standards. There are some narrow backward compatibility breaks to be aware of, however, so refer to theofficial migration guideto learn about all the changes and what you must do to prepare.