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.
Randomizer::getFloat(
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:
Foo::{$name};
# 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:
- The
Epoch doesn't fit in a PHP integer
now returns a newDateRangeError
instead of a genericValueError
, which it does not subclass. This is only an issue for 32-bit platforms. - The
Only non-special relative time specifications are supported for subtraction
warning withDateTime::sub()
anddate_sub()
becomes a newDateInvalidOperationException
. - The
Unknown or bad format (%s) at position %d (%c): %s
andString '%s' contains non-relative elements
warnings that are created while parsing wrong/brokenDateInterval
strings will now throw a newDateMalformedIntervalStringException
when used with the OO interface, instead of showing a warning and returning false.
# 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 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.
zend.max_allowed_stack_size=128K
# 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.
- When using FFI, C functions that have a return type of
void
now returnnull
instead of returningFFI\CData:void
-
posix_getrlimit()
now takes an optional$res
parameter to allow fetching a single resource limit. -
gc_status()
has four new fields:running
,protected
,full
, andbuffer_size
. -
class_alias()
now supports creating an alias of an internal class. -
mysqli_poll()
now raises aValueError
when the read nor error arguments are passed. -
array_pad()
is now only limited by the maximum number of elements an array can have. Before, it was only possible to add at most 1048576 elements at a time. - New posix functions:
posix_sysconf()
,posix_pathconf()
,posix_fpathconf()
, andposix_eaccess()
- Executing
proc_get_status()
multiple times will now always return the right value on posix systems.
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.