Articles/PHP/What's New in PHP 8.3

What's New in PHP 8.3

PHP 8.3 is a focused release full of quality-of-life wins. Here are its headline features with practical examples: typed class constants, the Override attribute, json_validate, dynamic constant fetch, random string generation, and readonly deep cloning.

November 25, 2023·5 min read

PHP 8.3 was released on 23 November 2023. There is no single blockbuster feature here, but it is a deeply practical release: it closes gaps in the type system, adds tooling that catches real mistakes, and ships a few standard-library functions you will wonder how you lived without. Here are the highlights.

Typed class constants

Class constants can finally carry a type declaration. Before 8.3, a child class could redefine a constant with a completely different type and PHP would shrug. Now the type is part of the contract and is enforced.

PHP
interface HasVersion
{
    const string VERSION = '1.0';
}

class App implements HasVersion
{
    const string VERSION = '8.3';
    const int MAX_UPLOAD = 10;
}

If a subclass tried to override VERSION with an int, PHP would reject it at compile time. This finishes the work that promotions, readonly, and the standalone types from earlier releases started: putting real types on every member of a class.

The Override attribute

#[Override] tells PHP "this method is meant to replace a parent method," and the engine verifies that a matching parent method actually exists. It catches the classic bug where a typo silently creates a brand-new method instead of overriding the one you intended.

PHP
class TestCase
{
    protected function tearDown(): void {}
}

class UserTest extends TestCase
{
    #[Override]
    protected function taerDown(): void // Fatal error: nothing to override
    {
        // cleanup that would never have run without the attribute
    }
}

Without the attribute, that misspelled taerDown() would just sit there, never called, and your cleanup would quietly never run. With it, PHP refuses to compile until the name matches.

json_validate()

Checking whether a string is valid JSON used to mean calling json_decode() and inspecting the error, which wastes memory building a structure you are about to throw away. json_validate() answers the yes-or-no question without allocating the result.

PHP
$payload = file_get_contents('php://input');

if (!json_validate($payload)) {
    http_response_code(400);
    exit('Invalid JSON');
}

$data = json_decode($payload, true);

For large payloads, or a hot path that rejects bad input early, this is meaningfully cheaper than decoding twice. It is a small function with a very clear job.

Dynamic class constant fetch

You can now fetch a constant whose name is held in a variable using Class::{$name} syntax, the same way dynamic property and method access already worked.

PHP
enum Suit: string
{
    case Hearts = 'H';
}

class Settings
{
    const string THEME_DARK = 'midnight';
    const string THEME_LIGHT = 'daylight';
}

$choice = 'THEME_DARK';
echo Settings::{$choice}; // 'midnight'

Before 8.3 this meant reaching for the constant() function with a clumsy string of the fully qualified name. The new syntax is both shorter and consistent with the rest of the language.

Random string generation

The Random extension from PHP 8.2 gained getBytesFromString(), which builds a random string from exactly the alphabet you give it. That makes generating tokens, codes, and slugs a one-liner.

PHP
$randomizer = new \Random\Randomizer();

$alphabet = '0123456789abcdefghijklmnopqrstuvwxyz';
$token = $randomizer->getBytesFromString($alphabet, 12);
// e.g. "q3v9zk1mb7xa"

You control the character set, so there are no surprise symbols to URL-encode or confusing look-alike characters if you do not want them. PHP 8.3 also added getFloat() for unbiased random floats within an interval, rounding out the extension nicely.

Readonly deep cloning

Readonly properties are write-once, which created a real problem for __clone(): you could not give a cloned object fresh copies of its nested objects, because reassigning the property was forbidden. PHP 8.3 allows readonly properties to be written exactly once inside __clone().

PHP
final class Order
{
    public function __construct(
        public readonly Address $shipTo,
    ) {}

    public function __clone(): void
    {
        $this->shipTo = clone $this->shipTo; // now allowed in __clone
    }
}

Cloning an Order now produces an independent Address rather than two orders quietly sharing one. This made readonly value objects practical to copy, which sets the stage for the much nicer cloning syntax that arrives in 8.5.

PHP 8.3 is the kind of release you upgrade to without drama and then quietly enjoy. The typed constants and #[Override] attribute prevent bugs, and the new functions remove little chores. Next, 8.4 brings one of the biggest syntax additions of the whole 8.x line: property hooks.