Recently I set up GitHub sponsors, if my content helps you, you can consider a one-time or monthly sponsorship.

What's new in PHP 8.3

PHP 8.3 will be released on November 23, 2023; it has improvements to readonly classes, the new json_validate() function, additions to the recently added Randomizer class, stack overflow detection, and more.

In this post, we'll go through all features, performance improvements, changes and deprecations one by one. If you want to stay up to date, you can subscribe to my newsletter, follow me on Twitter, or subscribe to my RSS feed.

# Readonly amendments RFC

This RFC proposed two changes, only one was accepted: being able to reinitialize readonly properties while cloning. That might sound like a very big deal, but this RFC only addresses a very specific (but important) edge case: overwriting property values within __clone(), in order to allow deep cloning readonly properties.

readonly class Post
    public function __construct(
        public DateTime $createdAt,
    ) {}
    public function __clone()
        $this->createdAt = new DateTime(); 
        // This is allowed,
        // even though `createdAt` is a readonly property.

You can read an in-depth post about this RFC and some sidenotes here.

# Anonymous readonly classes UPGRADING

Previously, you weren't able to mark anonymous classes as readonly. That's fixed in PHP 8.3:

$class = new readonly class {
    public function __construct(
        public string $foo = 'bar',
    ) {}

# The new json_validate() function RFC

Previously, the only way to validate whether a string was valid JSON, was to decode it and detect whether any errors were thrown. This new json_validate() function is beneficial if you only need to know whether the input is valid JSON, since it uses less memory compared to decoding the string.

json_validate(string $json, int $depth = 512, int $flags = 0): bool

# Randomizer additions RFC

PHP 8.2 added the new Randomizer class. This update brings some small additions:

Randomizer::getBytesFromString(string $string, int $length): string

This method allows you to generate a string with a given length that consists of randomly selected bytes from a given string.

    float $min,
    float $max,
    IntervalBoundary $boundary = IntervalBoundary::ClosedOpen
): float

getFloat() returns a float between $min and $max. You can define whether $min and $max should be included thanks to the IntervalBoundary enum. Closed means the value is included, while Open means excluded.

Randomizer::nextFloat(): float {}

Finally, nextFloat() is a shorthand for getFloat(0, 1, IntervalBoundary::ClosedOpen), in other words: it'll give you a random float between 0 and 1, where 1 is excluded.

# Dynamic class constant fetch RFC

PHP 8.3 allows you to fetch constants with a more dynamic syntax:

class Foo 
    const BAR = 'bar';

$name = 'BAR';
// Instead of this:
constant(Foo::class . '::' . $name);

// You can now do this:

# More Appropriate Date/Time Exceptions RFC breaking

In many cases, PHP would simply throw an Exception or Error object; or emit a warning or error when something went wrong in dealing with dates and times. This RFC goes through all those edge cases and adds proper, dedicated exceptions for them.

We now have exceptions like DateMalformedIntervalStringException, DateInvalidOperationException, and DateRangeError.

In general, these additions won't break any code, since these newly added exceptions and errors subclass the generic Exception and Error classes. However, there are three small breaking changes that come with this RFC:

# Improved unserialize() error handling RFC

unserialize() will now always emit a E_WARNING when running into problems instead of sometimes an E_NOTICE.

This RFC also proposed adding more exceptions when running unserialize(), but that part didn't get accepted.

# Stack overflow detection PR

PHP 8.3 adds two new ini directives called zend.max_allowed_stack_size and zend.reserved_stack_size. Programs that are close to overflowing the call stack may now throw an Error when using more than the difference between zend.max_allowed_stack_size and zend.reserved_stack_size.

The benefit of this feature is that stack-overflow-induced segmentation faults won't result in segfaults anymore, making debugging a lot easier.

The default for zend.max_allowed_stack_size is 0, meaning PHP will automatically determine a value. You can also provide -1 to indicate there isn't a limit, or a specific number of bytes. The zend.reserved_stack_size directive is used to determine the "buffer zone", so that PHP is able to still throw an error instead of actually running out of memory. The value here should be a number of bytes, but PHP will determine a reasonable default for you, so you don't necessarily need to set it, unless you're running into edge cases for specific programs.

On a final note, for fibers, the existing fiber.stack_size directive is used as the max allowed stack size.


# Small, but notable changes

Not every change in PHP passes the RFC process. In fact, the majority of changes include maintenance and bugfixing, and don't require an RFC. All of these changes are listed in the UPGRADING document. I'll list some of the most prominent ones, but you should definitely read throughout the whole list if you want to know about the tiniest details.

That's it for now, but this list will grow over time. If you want to stay up to date, you can subscribe to my newsletter, follow me on Twitter, or subscribe to my RSS feed.