PHP 8.3 is the newest installment of the popular programming language with its release on 23rd November 2023. Packed full of new features and improvements including the long-awaited, all-new JSON validate method. We will break down each feature and change (including deprecations) so you can be in the know when it comes to November!
PHP 8.3
8th June 2023 began the first stages of the release timetable for PHP's next upcoming version, with an alpha 1 released, followed by two more alphas 2, and 3, with a feature freeze on 18th July 2023. This then follows numerous beta tests and release candidates before hitting general availability on November 23rd, 2023. Pierrick Charron, Jakub Zelenka, and Eric Mann will head up as the release managers. Let's explore what's new in PHP 8.3 and the request for comments (RFC). The current version of PHP 8.3 is available for download, currently at full release.
PHP 8.3 RFC
Every PHP version goes through a round of RFC, which is the process by which proposals for changes and new features to PHP are discussed, reviewed, and will either be accepted or rejected by PHP's core developers. As we explore the new RFC we'll compare the changes between PHP 8.3 vs 8.2.
Brand new json_validate function
Everyone at Dev Lateral cannot wait for this one, the long-awaited validator for JSON. Previously if you wanted to check if a string was valid JSON markup, you had to decode it and then check for throw errors, not ideal, and not great for performance. Now, in PHP 8.3, you can let PHP handle the validation process of the JSON string.
# PHP 8.3 json_validate function
json_validate(string $json, int $depth = 512, int $flags = 0): bool
The multibyte version of str_pad, mb_str_pad
PHP does a really good job at offering two types of string functions, one for byte strings and another for multibyte strings. Byte strings in case you're unsure are suitable for handling ASCII characters and single-byte encodings whereas multibyte strings are designed to handle much more complex characters, such as UTF-8 and emojis! In PHP 8.3, the developers have added mb_str_pad which is the equivalent of str_pad. You can learn how to use str_pad in our comprehensive guide, where mb_str_pad works the same way, including error conditions as str_pad. As this is a new function there are no backward incompatible changes, apart from where PHP developers have created the mb_str_pad() function before PHP 8.3 and upgrade without checking. Luckily by reading this guide, you'll know to check your projects before upgrading! The function's argument and return type are as below.
# PHP 8.3 mb_str_pad function
function mb_str_pad(string $string, int $length, string $pad_string = " ", int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string {}
Dynamic class constant fetch
In PHP, there are multiple ways that you can look up members' names in classes, which include methods, and static properties, however, that didn't apply to class constants before PHP 8.3. Now in PHP 8.3, constants will be modified to allow a similar fetch method to the current list of class types.
- Variables
- Properties
- Static properties
- Methods
- Static Methods
- Classes for static properties
- Classes for static methods
This means that the need to use the constant function wrapper can now be replaced with a one-liner as shown below.
# Prior to PHP 8.3
echo constant(Foo::class . '::' . $bar);
# 8.3 and higher
echo Foo::{$bar};
The unserialize function gets improved error handling
Before PHP 8.3, PHP would accept trailing bytes after a valid serialization when unserializing, which could be confusing to developers when debugging serialized data. That's because once PHP's parser has found the trailing delimiter the value is returned with the rest of the payload silently ignored. Now in PHP 8.3, this will omit a new E_WARNING when the string contains additional bytes after the parser terminates. The warning triggered will look similar to the one below.
Warning: unserialize(): Extra data starting at offset 4 of 8 bytes in %s on line %d
The Randomizer class gets three new methods and an enum
Since PHP version 8.2, developers can now use the high-level API to provide randomness by the Random engine, called Randomizer (Random\Randomizer class). The class provides a more performant and secure way of creating random numbers, among others. PHP 8.3 adds, Randomizer::getBytesFromString, Randomizer::getFloat, Randomizer::nextFloat, and a new enum IntervalBoundary.
Randomizer::getBytesFromString(string $string, int $length): string
Randomizer::nextFloat(): float {}
Randomizer::getFloat(
float $min,
float $max,
IntervalBoundary $boundary = IntervalBoundary::ClosedOpen
): float
enum IntervalBoundary {
case ClosedOpen;
case ClosedClosed;
case OpenClosed;
case OpenOpen;
}
Breaking changes to the range function in PHP 8.3
The range function in PHP has had a major overhaul in how it throws exceptions and warnings when code passes unusable arguments. This means that these new changes in PHP may cause your PHP codebase to experience breaking changes. Before upgrading the PHP 8.3, it's recommended that you check if your codebase uses the range function and if so, check if any exceptions that may be thrown are caught and handled.
Readonly changes in PHP 8.3
We covered in our extensive guide that PHP version 8.1 added support for read-only properties, but in PHP 8.3 these have been amended to address some shortcomings. The changes involve allowing read-only properties to be reinitialized during cloning when using the __clone magic method. Before PHP 8.3, cloning read-only properties wasn't allowed, but since PHP 8.3, this is now possible. We've covered PHP's readonly classes and properties in great detail, with most read-only use available in PHP 8 and higher.
public readonly string $foo;
public function __construct(
public string $foo
){}
public function __clone()
{
$this->foo = 'bar';
}
Improved Date/Time error handling
Another round of error handling this time to the date/time extension, where the error handling types that been amended so they're more logical, but don't include changes to the messages themselves. The benefit of this is to move away from generic warnings and errors to catchable exceptions that can make for a better application experience. Let's take one example from the PHP 8.3 RFC. Passing a malformed ISO 8601 date to the DatePeriod function before PHP 8.3 triggers a fatal error without an exception. From PHP 8.3, it will throw a DateMalformedPeriodStringException.
try {
new DatePeriod('2050-05-01T15:00:00Zabcdefg');
} catch(Exception $e) {
echo $e->message();
}
# Orignal
Fatal error: DateMalformedPeriodStringException: Unknown or bad format (2050-05-01T15:00:00Zabcdefg)
# Becomes
Fatal error: DateMalformedPeriodStringException: Unknown or bad format (2050-05-01T15:00:00Zabcdefg)
Typed class constants
There is a clear effort in the PHP community that the PHP ecosystem type system is getting better and better but before PHP 8.3 it wasn't possible to declare constant types. In PHP 8.3 the RFC proposes that support is added into PHP for declaring class, interface, and trait constant types. This also includes enum, as the example shows below. In PHP 8.3 you cannot specify that the const BAR is a string, but in PHP 8.3 and above, you now can.
# PHP 8.2
enum Foo {
const BAR = "Test";
}
# PHP 8.3 and higher
enum Foo {
const string BAR = "Test";
}
New #[\Override] Attribute
In PHP 8.3 there is an additional attribute available for use, called #[\Override]. When added to a method PHP's engine will validate that method against its parent's method where the same name already exists or of the implemented interfaces. An example of this is shown below where the class "Foo" extends the class "Test" of which they both contain the method "test".
# Valid example of the #[\Override] attribute
class Test {
protected function test(): void {}
}
class Foo extends Test {
#[\Override]
public function test(): void {}
}
When the Override attribute fails. PHP will trigger a Fatal error "Fatal error: Foo::test() has #[\Override] attribute, but no matching parent method exists". In the example below changing the Test's function "test" to private (from the protected method), causes the fatal error, detected by PHP.
# Invalid example of the #[\Override] attribute
class Test {
private function test(): void {}
}
class Foo extends Test {
#[\Override]
public function test(): void {}
}
Static Variable Initialisers
Before PHP 8.3, it was possible to declare static variables in all types of functions, however, the variable's assignment expression must be constant and therefore cannot change. By having an expression that isn't constant, PHP will trigger a fatal error; "Constant expression contains invalid operations". However, in PHP 8.3, arbitrary static variables are allowed, such as the example below where the static variable "$i" is called another method, which now is allowed in PHP 8.3
# This is allowed in PHP 8.3
function bar() {
return 1;
}
function foo() {
static $i = bar();
echo $i++;
}
foo();
Matched arithmetic operations behaviour from array_reduce on array_sum and array_product
The changes in PHP 8.3 to array_sum and array_product fix the discrepancies between these and those found in the function array_reduce bringing them closer together in terms of their use, or this case, the expected use. The change means that under the PHP hood, all three variants will use the same engine functions that perform the arithmetic operations, where array_sum and array_product will no longer produce an E_WARNING for incompatible types.
Arrays that contain objects will now cause backward incompatibility changes as outlined below, where the results are in two different values, depending on what version of PHP version you use.
# Prior to PHP 8.3
$foo = [5, 5.9, gmp_init(100)];
var_dump(array_sum($foo));
# Outputs
10.9
# From PHP 8.3
$bar = [5, 5.9, gmp_init(100)];
var_dump(array_sum($bar));
# Outputs
110.9
Conclusion
The PHP roadmap for 8.3 is packed full of new features to help developers when creating PHP web applications, especially around some of the popular PHP functions, all assisted with improved error handling. As PHP evolves it moves closer to other programming languages in particular becoming a stricter typed language.