Discover the main new features in PHP 8.4
Marcos Marcolin • November 20, 2024 • 5 min read
php php84 phprfcWhat's new in PHP 8.4 🐘
As has been tradition over the last decade, PHP ships a new version of the language every year, and now it's PHP 8.4's turn.
If you follow this blog or my LinkedIn, you've probably already seen some of these new features, since I've written posts about them. You can check them out in this topic (in Portuguese).
In this post, I bring an overall look at the main changes. I'll highlight what I consider most relevant, since a lot was done over 1 year of development to make this version even better.
Property Hooks: simplify getters and setters - RFC
Property hooks are a modern approach to defining getters and setters directly in the property declaration inside the class.
class Product
{
public float $price {
set {
if ($value <= 0) throw new ValueError('Price must be positive!');
$this->price = $value;
}
get {
return number_format($this->price, 2);
}
}
public function __construct(float $price) {
$this->price = $price;
}
}
This removes the need for repetitive getters and setters, making your code cleaner and easier to maintain.
You can check out my full explanation at this link.
Asymmetric Visibility: more control over properties - RFC
It's now possible to define different scopes for reading and writing properties, allowing public reads while keeping writes restricted, removing the need for redundant getters.
class Product
{
public private(set) float $price = 19.99;
}
$product = new Product();
var_dump($product->price); // float(19.99)
$product->price = 25.99; // Visibility error
Ideal for scenarios where you need to protect data integrity without giving up public reads.
#[\Deprecated] Attribute - RFC
The new #[\Deprecated] attribute makes PHP's existing deprecation mechanism available for user-defined functions, methods, and class constants.
class Product
{
#[\Deprecated(
message: "Use Product::getPrice() instead",
since: "8.4",
)]
public function getProductPrice(): float
{
return $this->getPrice();
}
public function getPrice(): float
{
return 19.99;
}
}
$product = new Product();
// Deprecated: Method Product::getProductPrice() is deprecated since 8.4, use Product::getPrice() instead
echo $product->getProductPrice();
Full HTML5 support: modernizing the DOM in PHP - RFC / RFC
For over 16 years, HTML5 has been around, but PHP never fully kept up with its evolution. The \DOMDocument class, designed to support HTML4, no longer met modern standards, leaving developers dealing with frustrating limitations.
With PHP 8.4, that gap has finally been filled! A fully HTML5-compatible parser has arrived to modernize working with HTML documents.
New classes Dom\HTMLDocument, Dom\XMLDocument, and methods DOMNode::compareDocumentPosition(), DOMXPath::registerPhpFunctionNS(), DOMXPath::quote(), XSLTProcessor::registerPHPFunctionNS() are now available.
$dom = Dom\HTMLDocument::createFromString(
<<<HTML
<section>
<div class="info">PHP 8.4 introduces powerful new features!</div>
<div class="destaque">Now you can easily query DOM elements with HTML5 support.</div>
</section>
HTML,
LIBXML_NOERROR,
);
$node = $dom->querySelector('section > div.destaque');
var_dump($node->classList->contains("destaque")); // bool(true)
New array_*() functions - RFC
The new version brings a new set of functions for working with arrays: array_find, array_find_key, array_any, and array_all.
These functions offer a more direct and efficient way to work with arrays, simplifying common operations.
array_find
function array_find(array $array, callable $callback): mixed {}
array_find_key
function array_find_key(array $array, callable $callback): mixed {}
array_all
function array_all(array $array, callable $callback): bool {}
array_any
function array_any(array $array, callable $callback): bool {}
You can check out my full explanation about the new functions at this link.
PDO and SQL: Driver-specific subclasses - RFC
PHP 8.4 introduced new subclasses in PDO, specific to each supported database driver: Pdo\Dblib, Pdo\Firebird, Pdo\MySql, Pdo\Odbc, and Pdo\Sql.
These subclasses bring more clarity and organization, granting access to features specific to each driver, while keeping compatibility with the standard PDO. An improvement that makes working with different databases more intuitive and specialized.
$connection = PDO::connect(
'sqlite:foo.db',
$username,
$password,
); // object(Pdo\Sqlite)
$connection->createFunction(
'prepend_php',
static fn ($string) => "PHP {$string}",
);
$connection->query('SELECT prepend_php(version) FROM php'); // PHP 8.4
new MyClass()->method() without parentheses - RFC
It's now possible to directly access methods and properties of a freshly created object without needing to wrap the new expression in extra parentheses.
This makes the code cleaner and more intuitive, especially in direct calls.
class Product
{
public string $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function getFormattedName(): string
{
return "Product: {$this->name}";
}
}
echo new Product('Notebook')->getFormattedName(); // Product: Notebook
You can check out my full explanation about this feature at this link.
Object API for BCMath - RFC
The new BcMath\Number object enables the use of standard math operations in an object-oriented way with arbitrary-precision numbers.
These objects are immutable and implement the Stringable interface, allowing their use in string contexts, such as echo $num.
use BcMath\Number;
$price = new Number('199.99');
$discount = new Number('50.00');
$finalPrice = $price - $discount;
echo $finalPrice; // '149.99'
var_dump($finalPrice > $price); // false
New classes, interfaces, and functions
New JIT implementation based on the IR Framework - RFC
The new JIT implementation based on the IR Framework simplifies the code, improves error detection, makes advanced optimizations easier, and attracts outside experts, reducing complexity and dependence on a small number of developers.
New request_parse_body() function - RFC
The request_parse_body() function allows parsing multipart/form-data in other HTTP methods, such as PUT and PATCH, using PHP's native functionality, currently limited to POST. This avoids manual parsing in your code, improves performance, and makes it easy to reuse POST endpoints.
New bcceil(), bcdivmod(), bcfloor(), and bcround() functions - RFC
New RoundingMode Enum for round() with 4 new rounding modes: TowardsZero, AwayFromZero, NegativeInfinity, and PositiveInfinity - RFC
New http_get_last_response_headers(), http_clear_last_response_headers(), and fpow() functions.
New mb_trim(), mb_ltrim(), mb_rtrim(), mb_ucfirst(), and mb_lcfirst() functions.
Deprecations and backwards-incompatible changes
Implicitly nullable parameter types are now deprecated - RFC
Implicitly nullable types have been deprecated, marking an important step toward greater clarity and consistency in the language.
Now, you must explicitly declare that a type can be null using ?T or T|null.
This change lays the groundwork for fully removing this feature in PHP 9.
// Before (valid, but now raises a deprecation notice):
function foo(string $var = null): void {
// $var can be null, but the type doesn't explicitly reflect that.
}
// Now (recommended):
function foo(?string $var = null): void {
// Explicit declaration that $var can be null.
}
// Or
function foo(string|null $var = null): void {
// Valid alternative using union types.
}
Functions with signatures like function foo(string $var = null) now generate the following compile-time notice in PHP 8.4:
// Deprecated: Implicitly marking parameter $var as nullable is deprecated, the explicit nullable type must be used instead
Using _ as a class name is now deprecated.
The E_STRICT constant is deprecated.
Behavior change in exit() usage - RFC
In PHP 8.4, exit (and die) was turned from a language construct into a real function with the signature:
function exit(string|int $status = 0): never {}
Future ideas:
- Deprecate using
exitwithout parentheses. - Run
finallyblocks when usingexit(). - Allow disabling
exit()/die()viadisable_functionsinINI.
Final thoughts
In my opinion, PHP keeps shipping solid versions that meet the community's demands.
The new features added are extremely useful, solving important problems.
Following the language's core is something I really enjoy, even without understanding everything in detail. It's clear that a lot of improvements are being implemented, bringing more performance to the runtime.
I don't even need to get into the old debate about what PHP is or isn't; the language has proven its worth on its own ;)
Be sure to check out EVERYTHING on the official site.
Cheers, and see you next time!