php[architect] logo

Want to check out an issue? Sign up to receive a special offer.

What’s New and Exciting in PHP 8 3

Posted by on August 22, 2023

 

 

At the time of this recording, PHP 8.2 is in the beta release cycle so we can finally start to discuss what’s new and changed in the next release of PHP. In this article, we’ll discuss the timeline for the release and discuss some new features and changes we can expect to see.

Overall

Again at the time of this recording, PHP 8.3 is scheduled to be released on November 23, 2023, after it’s gone through alpha, beta, and release candidate phases. It’s always possible the date may change as we would rather have them release working software than something with a bug in it. This is actually what happened with the 8.2 release when a critical bug was found just before the release.

At the top level, there’s nothing that’s going to blow your socks off, but like the PHP 8.2 release, this is a good thing. PHP is at a point where it’s stabilizing so we shouldn’t expect HUGE changes all the time, especially because this is a minor release and not a major one.

What’s included in this release are improvements to the core language, some deprecations, and tweaks to existing syntax and features. Because this is a point release so it’s not expected to be a painful upgrade, but there are some breaking changes.

I have two disclaimers before we go any further. This article was made using “beta release 1” on https://onlinephp.io/ so functionality may change between now and the actual release.

I also can not stress enough to not install this in production yet.

To upgrade PHP, I generally setup a branch to test against our product using automated tests and then fix any bugs that are found in those automated tests (ideally, we would run Rector on them as well to clean up anything automatically). Sometime after the first patch (at least 8.3.1 in this case), we’ll start pushing it to testing environments and production shortly after that.

If you’re interested in trying it now there are Docker images available and instructions for compiling it directly from the source.

Let’s talk about the things that have been added.

Reinitialize Readonly Properties While Cloning

PHP 8.1 and 8.2 added readonly properties and readonly classes, respectively; readonly properties and classes could only be written to once and then never again. If we tried we would get an error. PHP 8.3 adds support to be able to reinitialize read-only properties when an instance of a class is being cloned.

For example, let’s say we have a class with a readonly $created property that keeps track of when the class was initialized.

readonly class Architect
{
    public function __construct(public DateTimeImmutable $created) {
        // 
    }
}

$item = new Architect(new DateTimeImmutable());
sleep(1);
$cloned = clone $item;

// these are identical
var_dump($item->created, $cloned->created);

When we clone this class the $created property will remain the same, which might not be what we want.

With this new feature, we can now reinitialize this property in the __clone() function.

readonly class Architect
{
    public function __construct(public DateTimeImmutable $created) {
        // 
    }

    public function __clone()
    {
        $this->created = new DateTimeImmutable(); 
    }
}

$item = new Architect(new DateTimeImmutable());
sleep(1);
$cloned = clone $item;

// these are different now
var_dump($item->created, $cloned->created);

json_validate

Before PHP 8.3 if we wanted to see if a string was valid JSON we had to pass it to the json_decode() function and see if a “truthy” value is returned.

if (json_decode($stringValue)) {
    // is valid
} else {
    // is not valid
}

This is an acceptable solution but it comes with the downside that we’re initializing a stdClass or array of the values. Depending on the string we’re decoding this can use a lot of memory. The json_validate() function is being added to determine if a JSON string is valid without initialing a stdClass or array so it saves memory. I would imagine it would also be a lot faster but I didn’t spend any time benchmarking it.

if (json_validate($stringValue)) {
    // is valid
} else {
    // is not valid
}

Typed class constants

You can now add type hints to class constants. I think we should add types to ALL the things we possibly can, so I’m glad to see this, as it prevents bugs. I wrote the wrong type in my example the first time and received an error that it was wrong, which could have caught a bug. I also have to think it will make things easier for our static code analysis tools like PHPstan to find bugs.

class Architect
{
    const string NEXT_PHP_TEK_DATE = "Apr 23-25, 2024"; 
}

Marking overridden methods (#[\Override])

This one is a little hazy so stay with me for a bit.

Let’s say we have a class with a public method in it.

class Architect
{
    public function getUniqueId(): int
    {
        return 42;
    }
}

In another class, we extend the Architect class and override the method with logic specific to the child class.

class JuniorDeveloper extends Architect
{
    public function getUniqueId(): int
    {
        return 24;
    }
}

Later, the Architect class is changed and the getUniqueId() method is renamed to better express the intent of the method. Unfortunately, nobody notices that the JuniorDeveloper class overrides the getUniqueId() method, and this introduces a bug that’s going to be a real headache to find.

class Architect
{
    public function getUniqueIdNumber(): int
    {
        return 42;
    }
}

PHP 8.3 adds the ability to mark a method with the #[Override] attribute to express that we’re explicitly overriding a function so we get an error if the parent changes.

class Architect
{
    public function getUniqueId(): int
    {
        return 42;
    }
}

class JuniorDeveloper extends Architect
{
    #[Override]
    public function getUniqueId(): int
    {
        return 24;
    }
}

Now when we change the method name we’ll get a fatal error.

class Architect
{
    public function getUniqueIdNumber(): int
    {
        return 42;
    }
}

class JuniorDeveloper extends Architect
{
    #[Override]
    public function getUniqueId(): int
    {
        return 24;
    }
}

Fatal error: JuniorDeveloper::getUniqueId() has #[\Override] attribute, but no matching parent method exists in /home/user/scripts/code.php on line 15

It will also raise a fatal error if the signature changes.

class Architect
{
    protected function getUniqueId(int $value): int
    {
        return 42;
    }
}

class JuniorDeveloper extends Architect
{
    #[Override]
    public function getUniqueId(): int
    {
        return 24;
    }
}

Fatal error: Declaration of JuniorDeveloper::getUniqueId(): int must be compatible with Architect::getUniqueId(int $value): int in /home/user/scripts/code.php on line 15

I’m not 100% sure I’ll want to do this with all of my functions but it sure provides a nice backup for unintended accidents.

Anonymous readonly classes

We can now create anonymous read-only classes. They behave just like a read-only class.

$phpTekClass = new readonly class {
    public function __construct(
        public string $nextPhpTekDate = "Apr 23-25, 2024",
    ) {}
};

$item = new $phpTekClass();
echo $item->nextPhpTekDate;

Before 8.3 we’ll get a syntax error:

Parse error: syntax error, unexpected token “readonly” in /home/user/scripts/code.php on line 2

Dynamic class constant fetch

We can now dynamically access class constants using a syntax that’s more like what we would expect.

class Architect
{
    const string NEXT_PHP_TEK_DATE = "Apr 23-25, 2024"; 
}
$name = "NEXT_PHP_TEK_DATE";

// new syntax in 8.3
echo Architect::{$name}; 

// < 8.3
echo constant(Architect::class . "::" . $name);

Make unserialize() emit a warning for trailing bytes

PHP 8.3 also changed unserialize() so it emits a warning if there are extra bytes in the string.

var_dump(unserialize('i:5;i:6;'));

>Warning: unserialize(): Extra data starting at offset 4 of 8 bytes in /home/user/scripts/code.php on line 2

Others

Other things that were changed that I would like to briefly mention are adding the missing mb_str_pad() function, having the gc_status() function return additional garbage collection information, new ini directives to throw an error when our application is close to overflowing the call stack, saner array_sum() and array_product() logic, and new functions were added to the Randomizer class.

There were also some parts of the language that were deprecated and will be removed in PHP 9. Hopefully, Rector will be able to replace those for us.


 

Leave a comment

Use the form below to leave a comment: