What's new in PHP 8.4
PHP 8.3 has already been released a few months back and it’s time to look at what’s coming in PHP 8.4.
It’s not a lot of features so far but there are some interesting ones. I’ll be updating this article as more features are added to the PHP 8.4 release. So, make sure you bookmark this article.
- New modes for the
round()
function - A new way to disable JIT
- A new JIT implementation
- A new option to parse huge XMLs
- Multibyte equivalents for the
trim()
function - A new class for parsing and serializing HTML5
- Multibyte for
ucfirst
andlcfirst
functions - Grapheme cluster for str_split function
- Dedicated functions for HTTP response headers
- Calling methods on newly instantiated class without parentheses
- New array functions
- Property hooks
- A new
#[\Deprecated]
attribute - Implicitly nullable parameter types will be deprecated
- Separate visibilities for read and write operations on properties
- exit() is now a standard function
- A new Enum for rounding modes
- Raising zero to the power of negative number will give deprecation warning
- A new function to allow parsing of
multipart/form-data
content type for non-POST requests - A dedicated class for stream processing
- Session propagation will no longer be done using GET/POST requests
- A new function to efficiently calculate both the quotient and remainder in a single operation
- Improvements to the
XMLReader
andXMLWriter
classes - Some extensions are going away from PHP core
- Introduction of Lazy Objects
New modes for the round()
function
The round()
function is used to round a number to its nearest integer. It’s a very common function used in PHP.
Up until now, the round()
function had only four rounding modes: PHP_ROUND_HALF_UP
, PHP_ROUND_HALF_DOWN
, PHP_ROUND_HALF_EVEN
, and PHP_ROUND_HALF_ODD
.
In PHP 8.4, four more rounding modes will be added to the round()
function.
-
PHP_ROUND_CEILING
- This will round the number to the nearest integer bigger than the given number. So,round(1.3, 0, PHP_ROUND_CEILING)
will return2
since2
is the nearest integer bigger than1.3
. -
PHP_ROUND_FLOOR
- This will round the number to the nearest integer smaller than the given number. So,round(1.3, 0, PHP_ROUND_FLOOR)
will return1
since1
is the nearest integer smaller than1.3
. -
PHP_ROUND_AWAY_FROM_ZERO
- This will round the number away from zero. So,round(1.3, 0, PHP_ROUND_AWAY_FROM_ZERO)
will return2
since2
is away from zero and is the nearest integer. -
PHP_ROUND_TOWARD_ZERO
- This will round the number towards zero. So,round(1.3, 0, PHP_ROUND_TOWARD_ZERO)
will return1
since1
is towards zero and is the nearest integer.
To simplify this further, PHP 8.4 also introduces a dedicated Enum to explicitly name the rounding modes that accurately describe the rounding behavior without the need for documentation.
A new way to disable JIT
PHP 8.0 introduced JIT (Just In Time) compilation which is a technique that converts PHP code into machine code at runtime. This improves the performance of PHP code.
So, you can change a lot of different configurations related to JIT in the php.ini
file but the current way of disabling JIT is a little bit confusing.
You can do it by setting the opcache.jit_buffer_size
to 0
in the php.ini
file. But this is not very intuitive.
opcache.jit=tracing
opcache.jit_buffer_size=0
To make it more intuitive, PHP 8.4 will introduce a new configuration directive called disable
which will disable JIT.
opcache.jit=disable
opcache.jit_buffer_size=64m
Here, the default value for opcache.jit_buffer_size
is also updated to 64m
from 0
.
A new JIT implementation
This change is not a user-facing change but it’s worth mentioning here.
PHP 8.4 will attempt to implement a new JIT implementation based on the IR (Intermediate Representation) framework. This new implementation is said to be “smarter” than the current one.
This implementation only uses a single back-end that constructs IR to generate x86 and AArch64 code as opposed to the current implementation which uses two back-ends.
A new option to parse huge XMLs
PHP 8.4 will introduce a new option called XML_OPTION_PARSE_HUGE
that can be passed along to xml_parser_set_option
while parsing XMLs.
Here’s how this looks like.
function startElement($parser, $name, $attrs)
{
// Do something interesting
}
function endElement($parser, $name)
{
// Do something interesting
}
$parser = xml_parser_create();
xml_parser_set_option($parser, XML_OPTION_PARSE_HUGE, true);
// Changing this to false, or not executing this line,
// will cause the parsing to error out on large inputs
xml_set_element_handler($parser, "startElement", "endElement");
// Add more handlers
$success = xml_parse($parser, $my_long_xml_input_already_in_memory);
This option will allow you to parse huge XMLs without running into memory issues or running into parsing errors.
Multibyte equivalents for the trim()
function
PHP has always been lacking multibyte equivalents for the trim()
function. But in PHP 8.4, this will be fixed.
The following three functions will be added to PHP 8.4.
mb_trim()
- Same astrim()
but for multibyte strings. It will remove all whitespace characters from the beginning and end of a string.
Here’s the entire signature of the function.
function mb_trim(string $string, string $characters = " \f\n\r\t\v\x00\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}"): string
mb_ltrim()
- Same asltrim()
but for multibyte strings. It will remove all whitespace characters from the beginning of a string.
Here’s the entire signature of the function.
function mb_ltrim(string $string, string $characters = " \f\n\r\t\v\x00\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}", ?string $encoding = null): string
mb_rtrim()
- Same asrtrim()
but for multibyte strings. It will remove all whitespace characters from the end of a string.
Here’s the entire signature of the function.
function mb_rtrim(string $string, string $characters = " \f\n\r\t\v\x00\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}", ?string $encoding = null): string
A new class for parsing and serializing HTML5
Up until now, PHP’s DOM extension is kind of stuck with the HTML4 specification. That is the DOMDocument
class can only parse and serialize HTML4 documents.
But since HTML5 is the new standard, PHP 8.4 will introduce a new class called DOM\HTMLDocument
that can parse and serialize HTML5 documents.
Here’s how this looks like.
namespace DOM {
// The base abstract document class
abstract class Document extends DOM\Node implements DOM\ParentNode {
/* all properties and methods that are common and sensible for both XML & HTML documents */
}
final class XMLDocument extends Document {
/* insert specific XML methods and properties (e.g. xmlVersion, validate(), ...) here */
private function __construct() {}
public static function createEmpty(string $version = "1.0", string $encoding = "UTF-8"): XMLDocument;
public static function createFromFile(string $path, int $options = 0, ?string $override_encoding = null): XMLDocument;
public static function createFromString(string $source, int $options = 0, ?string $override_encoding = null): XMLDocument;
}
final class HTMLDocument extends Document {
/* insert specific Html methods and properties here */
private function __construct() {}
public static function createEmpty(string $encoding = "UTF-8"): HTMLDocument;
public static function createFromFile(string $path, int $options = 0, ?string $override_encoding = null): HTMLDocument;
public static function createFromString(string $source, int $options = 0, ?string $override_encoding = null): HTMLDocument;
}
}
class DOMDocument extends DOM\Document {
/* Keep methods, properties, and constructor the same as they are now */
}
Multibyte for ucfirst
and lcfirst
functions
PHP has always been lacking multibyte equivalents for the ucfirst()
and lcfirst()
functions. But in PHP 8.4, this will be fixed.
The following two functions will be added to PHP 8.4.
mb_ucfirst()
- Same asucfirst()
but for multibyte strings.
function mb_ucfirst(string $string, ?string $encoding = null): string
mb_lcfirst()
- Same aslcfirst()
but for multibyte strings.
function mb_lcfirst(string $string, ?string $encoding = null): string
Here are some examples of how these functions will work.
mb_ucfirst(“აბგ”); // prints “აბგ” (U+10D0 U+10D1 U+10D2)
mb_ucfirst(“lj”) // prints “Lj” (U+01C9 -> U+01C8)
Grapheme cluster for str_split function
A grapheme cluster is a collection of symbols that together represent an individual character that the user will see within a string on the screen.
PHP was lacking a str_split function that would return an array of grapheme clusters. PHP 8.4 will introduce a new function called grapheme_str_split()
that will return an array of grapheme clusters.
Here’s the signature of the function.
function grapheme_str_split(string $string, int $length = 1): array|false {}
$string
only supports UTF-8. $length
is the length of the grapheme cluster per element of the array.
Here is an example of how this function will work.
var_dump(grapheme_str_split("🙇♂️"));
/*
[0]=>
string(13) "🙇♂️"
}
*/
One more example.
var_dump(grapheme_str_split("ä-pqr-b̈-xyz-c̈"));
/*
array(13) {
[0]=>
string(2) "ä"
[1]=>
string(1) "-"
[2]=>
string(1) "p"
[3]=>
string(1) "q"
[4]=>
string(1) "r"
[5]=>
string(1) "-"
[6]=>
string(3) "b̈"
[7]=>
string(1) "-"
[8]=>
string(1) "x"
[9]=>
string(1) "y"
[10]=>
string(1) "z"
[11]=>
string(1) "-"
[12]=>
string(3) "c̈"
}
Dedicated functions for HTTP response headers
Instead of automatically creating a $http_response_header variable in the local scope while an HTTP request is performed through PHP’s stream layer, i.e. when using the HTTP wrapper (one such usage is using file_get_contents()
to retrieve the content of a URL), two new functions have been added to PHP 8.4.
http_get_last_response_header()
- Returns the last response headers as an array.http_clear_last_response_headers()
- Clears the last response headers.
Here’s what the response would look like.
$response = http_get_last_response_header();
var_dump($response);
/*
array(9) {
[0]=>
string(15) "HTTP/1.1 200 OK"
[1]=>
string(35) "Date: Sat, 12 Apr 2008 17:30:38 GMT"
[2]=>
string(29) "Server: Apache/2.2.3 (CentOS)"
[3]=>
string(44) "Last-Modified: Tue, 15 Nov 2005 13:24:10 GMT"
[4]=>
string(27) "ETag: "280100-1b6-80bfd280""
[5]=>
string(20) "Accept-Ranges: bytes"
[6]=>
string(19) "Content-Length: 438"
[7]=>
string(17) "Connection: close"
[8]=>
string(38) "Content-Type: text/html; charset=UTF-8"
}
*/
Calling methods on newly instantiated class without parentheses
Up unitl now, when you want to call a method on a newly instantiated class without parentheses, you had to do it like this.
$obj = (new MyClass())->myMethod();
While this will still work, PHP 8.4 will let you call a method on a newly instantiated class without parentheses.
So, previous example will look like this.
$obj = new MyClass()->myMethod();
It just looks a bit cleaner in my opinion.
Here are all the places where this will work.
new MyClass()::CONSTANT;
new MyClass()::$staticProperty;
new MyClass()::staticMethod();
new MyClass()->property;
new MyClass()->method();
new MyClass()();
new MyClass(['value'])[0];
New array functions
PHP 8.4 will introduce a few new array functions which are helper functions for common patterns of checking an array for the existence of elements matching a specific condition.
The new methods are:
array_find
- Returns the value of the first element for which the $callback returns true. If no matching element is found the function returnsNULL
.array_find_key
- Returns the key of the first element for which the$callback
returns true.array_any
- Returnstrue
if any of the elements in the array pass the$callback
test. Otherwise, it returnsfalse
.array_all
- Returnstrue
if all of the elements in the array pass the$callback
test. Otherwise, it returnsfalse
.
I have covered these functions in a dedicated article: A few new array functions are making their way into PHP 8.4.
Property hooks
PHP 8.4 will introduce property hooks that allows you to define custom logic for property access and mutation. This can be useful for a variety of use cases, such as mutation, logging, validation, or caching.
Essentially, property hooks allow you to define additional behavior on class properties mainly using two hooks:
get
andset
. And this will be individual for certain properties.
Here’s how a set hook looks like.
class User
{
public string $email {
set (string $value) {
// validate the email address
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException(
'Invalid email address'
);
}
// Set the value
$this->email = $value;
}
}
}
And here’s how it works when setting the value of the $email
property.
$user = new User();
$user->email = 'example.com'; // This will throw an exception
$user = new User();
$user->email = '[email protected]';
echo $user->email; // [email protected]
You can learn more about property hooks in the dedicated article.
A new #[\Deprecated]
attribute
PHP 8.4 will introduce a new attribute called #[\Deprecated]
that can be used to mark functions, classes, and constants as deprecated.
Meaning, once the function, class, or constant is marked as deprecated, it will give a deprecation warning when the function, class, or constant is used.
Here’s how this looks like.
class User
{
#[\Deprecated]
public function getSession(): string
{
return 'session';
}
#[\Deprecated('Use getUser() instead')]
public function getUsername(): string
{
return $this->username;
}
}
$user = new User();
$user->getUsername();
// Deprecated: Use getUser() instead
$user->getSession();
// Deprecated: Function getSession() is deprecated
As you can tell, you can use the #[\Deprecated]
with your own message to better explain why the function, class, or constant is deprecated.
You can also check if a function, class, or constant is deprecated programmatically by using reflection.
#[\Deprecated]
function test() {
}
$r = new ReflectionFunction('test');
var_dump($r->isDeprecated()); // bool(true)
Implicitly nullable parameter types will be deprecated
PHP 8.4 will deprecate implicitly nullable parameter types. This means that when you define a parameter in a function or method and assign null
as a default value, you will get a deprecation warning.
Here’s how this looks like.
function test(T $value = null) {
// Do something
}
// Deprecated: Implicitly marking parameter $value
// as nullable is deprecated, the explicit nullable
// type must be used instead
To get rid of this warning, you will have to use the explicit nullable type like so.
function test(?T $value) {
// Do something
}
Separate visibilities for read and write operations on properties
PHP 8.4 will bring in an ability to define separate visibilities for read and write operations on properties. This means that you can define a property as public
and private
for read and write operations respectively. It’s called “asymmetric visibility”.
This is how it works.
class Foo
{
public private(set) string $bar = 'baz';
}
$foo = new Foo();
var_dump($foo->bar); // prints "baz"
$foo->bar = 'beep'; // Visibility error
Essentially, you can now declare the “set” operation visibility of an object property to limit the visibility.
In this example, the set
operation is limited to private
and the get
operation is limited to public
. So, you can access the property from the public scope without any issues but you can’t set the value of the property from the public scope.
This works same for protected
as well.
Apart from this, if a set visibility is specified on a property then a public get visibility may be omitted. So, both the following statements are equivalent.
public private(set) string $bar = 'baz';
private(set) string $bar = 'baz';
Here’s a more practical example of how this can be used.
class User
{
public function __construct(
// The user's email can only be set internally by the class,
// but it can be read publicly
public private(set) string $email
){}
// Method to change the email, ensuring only valid updates are done
public function updateEmail(string $newEmail)
{
if (filter_var($newEmail, FILTER_VALIDATE_EMAIL)) {
$this->email = $newEmail;
} else {
throw new Exception('Invalid email format.');
}
}
}
$user = new User('[email protected]');
var_dump($user->email);
// prints "[email protected]"
// This would result in an error,
// since the property is read-only from outside
$user->email = '[email protected]'; // Visibility error
// Proper way to update email via the class's method
$user->updateEmail('[email protected]');
var_dump($user->email);
// prints "[email protected]"
exit() is now a standard function
The current exit()
construct does not behave like a standard function, leading to confusion and inconsistency, especially in type handling and error messaging.
So, PHP 8.4 will introduce exit()
as a proper function with a defined signature, allowing for named arguments, compatibility with strict_types
, and adherence to standard type juggling semantics. The behavior of exit()
will be like a typical function, including the ability to be passed as a callable and respecting type juggling for arguments.
function exit(string|int $status = 0): never {}
The new function will maintain the same parsing and will not be removable via the disable_functions
INI directive.
As per the RFC, the impact of this change to the backward compatibility will be fairly low with the future scope being deprecating the use of exit
without parentheses, executing finally blocks when exit is called, and the ability to disable exit()
/die()
funuctions via disable_functions
INI directive.
A new Enum for rounding modes
PHP 8.4 will rename rounding modes of the round()
function for clarity and consistency, and to implement them as an Enum in PHP 8.4, enhancing type safety and usability.
PHP currently have four rounding modes (PHP_ROUND_HALF_UP
, PHP_ROUND_HALF_DOWN
, PHP_ROUND_HALF_EVEN
, and PHP_ROUND_HALF_ODD
), which suffer from two main issues:
- The use of integers for rounding modes, leading to potential misuse
- The counterintuitive behavior of “up” and “down” modes for negative values.
This RFC suggests the introduction of a RoundingMode
enum in the core to replace the existing integer constants, providing explicit names that accurately describe the rounding behavior without the need for documentation.
enum RoundingMode
{
/**
* Round to the nearest integer. If the decimal part is 5, round to
* the integer with the larger absolute value.
*
* 1.0 -> 1
* -1.0 -> -1
* 1.2 -> 1
* -1.2 -> -1
* 1.6 -> 2
* -1.6 -> -2
* 1.5 -> 2
* -1.5 -> -2
* 2.5 -> 3
* -2.5 -> -3
*/
case HalfAwayFromZero; // PHP_ROUND_HALF_UP
/**
* Round to the nearest integer. If the decimal part is 5, round to
* the integer with the smaller absolute value.
*
* 1.0 -> 1
* -1.0 -> -1
* 1.2 -> 1
* -1.2 -> -1
* 1.6 -> 2
* -1.6 -> -2
* 1.5 -> 1
* -1.5 -> -1
* 2.5 -> 2
* -2.5 -> -2
*/
case HalfTowardsZero; // PHP_ROUND_HALF_DOWN
/**
* Round to the nearest integer. If the decimal part is 5, round to
* the even integer.
*
* 1.0 -> 1
* -1.0 -> -1
* 1.2 -> 1
* -1.2 -> -1
* 1.6 -> 2
* -1.6 -> -2
* 1.5 -> 2
* -1.5 -> -2
* 2.5 -> 2
* -2.5 -> -2
*/
case HalfEven; // PHP_ROUND_HALF_EVEN
/**
* Round to the nearest integer. If the decimal part is 5, round to
* the odd integer.
*
* 1.0 -> 1
* -1.0 -> -1
* 1.2 -> 1
* -1.2 -> -1
* 1.6 -> 2
* -1.6 -> -2
* 1.5 -> 1
* -1.5 -> -1
* 2.5 -> 3
* -2.5 -> -3
*/
case HalfOdd; // PHP_ROUND_HALF_ODD
/**
* Round to the nearest integer with a smaller or equal absolute value.
*
* 1.0 -> 1
* -1.0 -> -1
* 1.2 -> 1
* -1.2 -> -1
* 1.6 -> 1
* -1.6 -> -1
* 1.5 -> 1
* -1.5 -> -1
* 2.5 -> 2
* -2.5 -> -2
*/
case TowardsZero; // PHP_ROUND_TOWARD_ZERO
/**
* Round to the nearest integer with a greater or equal absolute value.
*
* 1.0 -> 1
* -1.0 -> -1
* 1.2 -> 2
* -1.2 -> -2
* 1.6 -> 2
* -1.6 -> -2
* 1.5 -> 2
* -1.5 -> -2
* 2.5 -> 3
* -2.5 -> -3
*/
case AwayFromZero; // PHP_ROUND_AWAY_FROM_ZERO
/**
* Round to the largest integer that is smaller or equal.
*
* 1.0 -> 1
* -1.0 -> -1
* 1.2 -> 1
* -1.2 -> -2
* 1.6 -> 1
* -1.6 -> -2
* 1.5 -> 1
* -1.5 -> -2
* 2.5 -> 2
* -2.5 -> -3
*/
case NegativeInfinity; // PHP_ROUND_FLOOR
/**
* Round to the smallest integer that is greater or equal.
*
* 1.0 -> 1
* -1.0 -> -1
* 1.2 -> 2
* -1.2 -> -1
* 1.6 -> 2
* -1.6 -> -1
* 1.5 -> 2
* -1.5 -> -1
* 2.5 -> 3
* -2.5 -> -2
*/
case PositiveInfinity; // PHP_ROUND_CEILING
}
The round()
function will be updated to accept this new enum, while still supporting integer values to minimize breaking changes.
So, you can now use the round()
function like so.
round(1.5, \RoundingMode::HalfAwayFromZero); // 2
round(1.5, \RoundingMode::HalfTowardsZero); // 2
The aim behind this change is to enhance the developer experience by making the code more intuitive and maintainable, with a smoother transition to the new enum-based system.
Raising zero to the power of negative number will give deprecation warning
Currently, when you raise zero to the power of a negative number, it will result into INF
instead of throwing an exception.
var_dump(0 ** -1); //float(INF)
var_dump(0 ** -1.1); //float(INF)
But we already have the ability where if we divide a number with 0, it will throw a DivisionByZeroError
exception.
And so, PHP 8.4 will address this inconsistency and will trigger a deprecation warning when you try to raise zero to the power of a negative number, with the intention of fully replacing this behavior with a DivisionByZeroError
in PHP 9.0.
A new function to allow parsing of multipart/form-data
content type for non-POST requests
PHP 8.4 introduces a new function called request_parse_body()
to allow parsing of multipart/form-data
content type for HTTP verbs other than POST, addressing the needs of RESTful APIs and improving performance for large data transfers.
This will extend the native parsing capabilities of PHP to support multipart/form-data
requests for methods like PUT
and PATCH
, which are commonly used in RESTful APIs. This is currently only supported for POST requests.
#[Route('/api/videos', methods: ['PUT'])]
public function index(): Response {
[$post, $files] = request_parse_body(options: [
'post_max_size' => '128M',
]);
// ...
}
The function enables userland code to parse such requests, providing flexibility and performance benefits, especially for handling large amounts of data. The function also allows for overriding global INI settings through an options parameter, offering endpoint-specific customization of parsing limits. This approach avoids potential security risks associated with global changes to PHP settings.
A dedicated class for stream processing
PHP 8.4 will introduce a dedicated StreamBucket
class to replace the current stdClass object usage for stream processing, enhancing code clarity and IDE support, and deprecating the $bucket
resource property and $datalen
abbreviation in favor of $dataLength
.
This change aims to address the current limitations where stream buckets are represented by generic stdClass objects, leading to less precise type handling and reduced IDE support.
Here’s how the new class will look like.
final class StreamBucket
{
/**
* @var resource
* @deprecated after resource to object conversion happens
*/
public $bucket;
public string $data;
/** @deprecated in PHP 8.5 or PHP 9.0 whichever comes next */
public int $datalen;
public int $dataLength;
}
The StreamBucket
class will provide a clear structure with typed properties, which will facilitate better static analysis and autocompletion in development environments.
Session propagation will no longer be done using GET/POST requests
PHP 8.4 will be deprecating the use of GET/POST for session propagation and automatic URL transformation in PHP 8.4, with complete removal planned for PHP 9.0.
The PHP community is considering a proposal to deprecate two session handling features: the initialization of sessions using identifiers from GET/POST requests and the automatic transformation of URLs to include session IDs (use_trans_sid
). These practices are deemed insecure and outdated, with cookies being the preferred method for session token propagation.
Essentially, the GET/POST methods for session propagation in PHP involve passing the session ID as part of the URL (in the case of GET) or within the body of a POST request. This will throw a deprecation warning if the following setting are used.
session.use_only_cookies=Off ; Will generate a deprecation warning
session.use_trans_sid=On ; Will generate a deprecation warning
This will encourage better security practices by phasing out support for less secure methods of session propagation.
A new function to efficiently calculate both the quotient and remainder in a single operation
PHP 8.4 will add a bcdivmod()
function and a corresponding divmod()
method to the BcMath\Number
class to efficiently calculate both the quotient and remainder in a single operation.
The BCMath extension currently requires separate operations for division and modulus, which is inefficient. So, these functions will overcome this limitation.
/** @return array{string, string} */
function bcdivmod(string $num1, string $num2, ?int $scale = null): array {}
/** @return array{Number, Number} */
public function divmod(Number|string|int $num, ?int $scale = null, int $roundingMode = PHP_ROUND_HALF_UP): array {}
Here’s an example usage of the bcdivmod()
function.
[$quot, $rem] = bcdivmod('123', '2');
// $quot is '61'
// $rem is '1'
And here’s an example usage of the divmod()
method.
$slicesOfPizza = new BcMath\Number(8);
$mouthsToFeed = new BcMath\Number(3);
[$perMouth, $slicesLeft] = $slicesOfPizza->divmod($mouthsToFeed);
// $perMouth->value is '2'
// $slicesLeft->value is '2'
This change is inspired by similar functions in Python and Ruby and is expected to improve performance.
The new function and method are designed to return an array with the quotient and remainder, similar to the GMP extension’s gmp_div_qr()
function.
Improvements to the XMLReader
and XMLWriter
classes
PHP 8.4 will be adding stream handling capabilities to XMLReader
and XMLWriter
classes to allow direct operation on already-open streams, enhancing efficiency and ease of use.
class XMLReader {
/** @param resource $stream */
public static function fromStream($stream, ?string $encoding = null, int $flags = 0, ?string $documentUri = null): static {}
}
class XMLWriter {
/** @param resource $stream */
public static function toStream($stream): static {}
}
This will address the limitation in XMLReader
and XMLWriter
classes that prevents them from operating on pre-existing open streams. This limitation often forces developers to use inefficient workarounds, such as loading entire streams into memory or writing to temporary files before processing.
Like I said previously, this will make it easy in environments where XML documents are too large to fit into memory comfortably, these functions allow for processing in chunks directly from a stream. This is particularly useful in server-side applications, such as parsing large configuration files or processing XML data feeds from external services enabling more efficient use of server resources.
Some extensions are going away from PHP core
PHP 8.4 will unbundle the ext/imap
, ext/pspell
, ext/oci8
, and ext/PDO_OCI
extensions from the PHP source distribution and move them to PECL for future maintenance and compatibility with PHP 8.4 and beyond.
This change would require PHP users to install these extensions from PECL if needed, as they will no longer be maintained by the PHP Core team.
Introduction of Lazy Objects
The implementation of lazy objects in PHP 8.4 to enable transparent lazy initialization at the engine level, improving performance and simplifying userland code for lazy loading patterns.
This will introduce two types of lazy objects—ghost objects and proxies—each with distinct initialization strategies:
- Ghost objects are initialized in-place.
- Proxies forward interactions to a separate instance.
The feature essentially will use the Reflection API to create and manage lazy objects, and specifies the behavior of these objects throughout their lifecycle, including initialization triggers, cloning, and handling of readonly properties and destructors by introducing new methods to the ReflectionClass
and ReflectionProperty
classes.
Creating Lazy Objects
Here is how you can create a lazy object for a class.
class MyClass
{
public function __construct(private int $foo)
{
// Heavy initialization logic here.
}
// ...
}
// Initializer for a ghost object
$initializer = static function (MyClass $ghost): void {
$ghost->__construct(123);
};
// Create a ReflectionClass instance for MyClass
$reflector = new ReflectionClass(MyClass::class);
// Create a lazy ghost object
$lazyGhost = $reflector->newLazyGhost($initializer);
// At this point, $lazyGhost is a lazy ghost object
// that will be initialized when first used.
Handling the State of Lazy Objects
If you know certain properties ahead of time, you can set them without triggering initialization:
$reflector->getProperty('id')->setRawValueWithoutLazyInitialization($lazyGhost, 123);
Lifecycle of Lazy Objects
You can force the initialization of a lazy object using:
$initializedObject = $reflector->initializeLazyObject($lazyGhost);
A real-world usage of Lazy Objects
In a dependency injection container, you can defer the creation of expensive services until they are actually requested using lazy objects.
class ServiceContainer
{
public function getService(string $serviceName)
{
if ($serviceName === 'expensiveService') {
$reflector = new ReflectionClass(ExpensiveService::class);
return $reflector->newLazyProxy(function () {
// Expensive initialization logic
return new ExpensiveService();
});
}
// ...
}
}
Like this article?
Buy me a coffee👋 Hi there! I'm Amit. I write articles about all things web development. You can become a sponsor on my blog to help me continue my writing journey and get your brand in front of thousands of eyes.