Pumpkin spice is in the air, so it’s time for a new version of PHP, the server-side scripting language that powers our favorite CMS, WordPress. Leading up to the November 21 GA launch of version 8.4, PHP’s developers unveiled numerous early versions of the new codebase, including a handful of release candidates since an August feature freeze.
Along with the new features, improvements, and deprecations, we anticipate this time of year, 2024 saw tweaks to PHP’s release cycle, with the end of security releases for all currently supported versions synced to the end of the year instead of its GA birthday.
What’s more, that support was extended by a year, meaning you could be using PHP 8.4 safely through 2028 (with two years of security and bug fixes and two years of just security fixes).
While you might be able to spend more time with PHP 8.4, you probably want to learn what’s new in this release right now. So, let’s jump in.
New features and improvements in PHP 8.4
The new features included in the release of PHP 8.3 last year will seem low-key when compared to some of the additions found in 8.4:
Property hooks
Property hooks bring a whole new approach to handling “getters” and “setters” in PHP object-oriented programming (OOP), allowing you to simplify the structure of your class files.
As an example of what property hooks can replace, the simple class below includes the properties $size
and $flavor
. They have private
visibility to protect them from direct access outside the resulting object. That’s why public getter and setter methods mediate access to the properties:
class Coffee
{
private string $size;
private string $flavor;
public function __construct(string $size, string $flavor) {
$this->size = $size;
$this->flavor = $flavor;
}
// "Setting" coffee size and flavor
public function setSize(string $size): void {
$this->size = $size;
}
public function setFlavor(string $flavor): void {
$this->flavor = $flavor;
}
// "Getting" coffee size and flavor
public function getSize(): string {
return $this->size;
}
public function getFlavor(): string {
return $this->flavor;
}
} // End of class
// Make some coffee
$coffee = new Coffee('Small', 'Pumpkin Spice');
print $coffee->getSize() . ' ' . $coffee->getFlavor(); // Prints "Small Pumpkin Spice"
// Change order
$coffee->setSize('Grande');
$coffee->setFlavor('Mocha');
print $coffee->getSize() . ' ' . $coffee->getFlavor(); // Prints "Grande Mocha"
Or, maybe your class has many properties, and instead of writing many getter and setter methods, you use PHP’s _get
and _set
magic methods. You might even sort things out in a somewhat messy switch
statement like this excerpt below.
// __set magic method example
public function __set(string $key, $value): void
switch ($key) {
case 'size':
$this->size = $value;
break;
case 'flavor':
$this->flavor = $value;
break;
default:
throw new InvalidArgumentException('Invalid input');
}
}
// Later, we can change the coffee order like this:
$coffee->size="Grande";
$coffee->flavor="Mocha";
Whichever approach you choose, the more properties you have in your class, the further the code used to manipulate them will be from their definitions near the top of your class file. What’s more, some implementations of the _get
and _set
magic methods can unexpectedly provide access to private or protected properties in your object that you hadn’t intended to expose.
The new property hooks feature bundles getter and setter functionality with the properties themselves. In the property hooks example below, you’ll notice that the $size
and $flavor
properties of the Coffee class are now public. But we’ve also added some basic validation to the set
hooks, differentiating them from direct assignments.
// Property definitions at the top of our Coffee class
class Coffee
{
public string $flavor {
set(string $value) {
if (strlen($value) > 16) throw new InvalidArgumentException('Input is too long');
$this->flavor = $value;
}
}
public string $size {
set(string $value) {
if (! in_array($value, array(‘Small’, ‘Grande’))) throw new InvalidArgumentException('Not a valid size');
$this->size = $value;
}
}
// Rest of the Coffee class
}
// Define a coffee
$coffee = new Coffee();
$coffee->size="Grande";
$coffee->flavor="Pumpkin spice";
Likewise, as you can see below, a get
hook can pack functionality into what appears to be an ordinary reference to an object property.
// Simplified Coffee class
class Coffee
{
public string $flavor {
get {
return $this->flavor . ' Spice';
}
}
}
// Create a flavor
$coffee = new Coffee();
$coffee->flavor="Pumpkin"; // Stores the value "Pumpkin"
print $coffee->flavor; // Prints "Pumpkin Spice"
Unlike the PHP magic methods, property hooks can be used in interfaces and abstract classes. An interface example:
interface Coffee
{
public string $size { get; set; }
public string $flavor { get; set; }
}
Asymmetric visibility
Publicly visible getter and setter methods we looked at earlier represent the traditional approach to accessing private and protected properties within their classes.
A nifty feature of PHP 8.4 is the ability of a property to have different levels of visibility depending on the context in which it is accessed. So, a property might be public when being read but private or protected when being set.
Check this out:
class Coffee
{
public private(set) string $flavor="Pumpkin Spice";
}
$coffee = new Coffee();
print $coffee->flavor; // Prints "Pumpkin Spice"
$coffee->flavor="Mocha"; // Error (visibility)
Above, the class’s $flavor
property is public except in a setting context. It’s pretty simple already, but asymmetric visibility even has a bit of a shortcut:
class Coffee
{
// public is assumed when the context is not setting
private(set) string $flavor="Pumpkin Spice";
}
You can use property hooks and asymmetric visibility in combination for tremendous flexibility in working with object properties of various visibilities.
Chaining new
without parentheses
Speaking of shorthands, calling new
and chaining methods used to require placing its invocation in parentheses, like this:
$coffee = (new Coffee())->getFlavor()->getSize();
PHP 8.4 allows this:
$coffee = new Coffee()->getFlavor()->getSize();
It may seem like a minor change, but dropping just two parentheses makes that much easier to read and debug.
New functions for finding array items
From the “You mean we couldn’t already do this?” department, PHP 8.4 introduces the function array_find()
, which can search array elements for members matching conditions expressed in a callback function. The function returns the value of the first element matching the callback’s test.
The new release includes three other related functions:
array_find_key()
: Likearray_find()
, but the return value is the matching element’s key instead of the value of the elements itself.array_all()
: Returnstrue
if every element in the array being tested matches the callback’s test.array_any()
: Returnstrue
if at least one of the elements in the array matches the callback’s test.
Note that the last two functions return boolean indicators instead of array keys or content.
Here are some quick examples:
$array = [
'a' => 'Mocha',
'b' => 'Caramel',
'c' => 'Maple',
'd' => 'Pumpkin'
];
// Find the first flavor name that is 5 characters long
var_dump(array_find($array, function (string $value) {
return strlen($value) == 5;
})); // Returns “Mocha,” even though “Maple” is the same length
// Find the array key for the first flavor with a name longer than 5 characters.
var_dump(array_find_key($array, function (string $value) {
return strlen($value) > 5;
})); // Returns “b”
// Check to see if any flavor name is less than 5 characters long
var_dump(array_any($array, function (string $value) {
return strlen($value)
HTML5 parsing
HTM5 is the defacto standard for the structure of modern web pages, but PHP’s Document Object Model (DOM) parsing technology had stalled at HTML 4.01.
Rather than upgrading the existing DOMDocument
class that works with the older HTML standards, PHP 8.4 comes with a new DomHTMLDocument
class that is HTM5-ready.
You can import the contents of an HTML5 page like this:
$document = DomHTMLDocument::createFromString($html)
In addition to the createFromString($html)
constructor above, the class also supports createFromFile($path)
and createEmpty()
The new parser recognizes semantic HTML5 tags like main
, article
and section
that are now familiar to most of us.
Multibyte trim functions
Another addition in PHP 8.4 that seems like it was a long time coming is multibyte support in trim functions:
mb_trim()
mb_ltrim()
mb_rtrim()
Like the long-standing PHP trim()
function, mb_trim
removes white space and some special characters, like line feeds, from both ends of a string that may contain multibyte characters. The other functions trim either the left or right ends of a string.
Deprecations in PHP 8.4
Each release of PHP brings with it a laundry list of features and functions (some pretty obscure) that are flagged for eventual removal from the platform. One higher-profile deprecation in PHP 8.4 is non-cookie session tracking.
Deprecation of GET/POST sessions
While cookies are generally the preferred method for tracking user sessions, PHP has supported fixing session ID data in GET/POST parameters. To enable session tracking via parameters in URLs, the PHP setting session.use_only_cookies
is disabled, and the setting session.use_trans_sid
may be enabled.
With PHP 8.4, either of those states for the settings will trigger a deprecation warning that may appear in your website logs. When PHP 9 is released, these settings will no longer be available.
Other deprecations (and removals) in PHP 8.4
Below is a list of functionality targeted for deprecation by the team behind PHP 8.4. (Some include links to more information on the features.,
- Formally deprecate soft-deprecated
DOMDocument
andDOMEntity
properties. - Removed
DOMImplementation::getFeature($feature, $version)
. - Deprecate
DOM_PHP_ERR
constant. - Deprecate The “S” tag in
unserialize()
. - Deprecate
session.sid_length
andsession.sid_bits_per_character
. - Deprecate
SplFixedArray::__wakeup()
. - Deprecate
xml_set_object()
andxml_set_*_handler()
with string method names. - Deprecate passing null and false to
dba_key_split()
. - Deprecate passing incorrect data types for options to ext/hash functions.
- Deprecate constants
SUNFUNCS_RET_STRING
,SUNFUNCS_RET_DOUBLE
,SUNFUNCS_RET_TIMESTAMP
. - Deprecate proprietary CSV escaping mechanism.
- Deprecate
E_STRICT
constant. - Deprecate
strtok()
. - Deprecate returning non-string values from a user output handler.
- Deprecate producing output in a user output handler.
- Deprecate
file_put_contents()
with$data
as an array. - Deprecate
mysqli_ping()
andmysqli::ping()
- Deprecate
mysqli_refresh()
. - Deprecate
mysqli_kill()
. - Deprecate the second parameter to
mysqli_store_result()
. - Deprecate
lcg_value()
. - Deprecate
uniqid()
. - Deprecate
md5()
,sha1()
,md5_file()
, andsha1_file()
. - Deprecate passing
E_USER_ERROR
totrigger_error()
. - Deprecate using a single underscore (“_”) as a class name.
- Deprecate
SOAP_FUNCTIONS_ALL
constant and passing it toSoapServer::addFunction()
.
Summary
PHP 8.4 comes with some interesting changes. We’re excited to get this release onto our servers soon for our annual PHP benchmarking — our testing with various PHP-based content management systems.
We’re also interested to see when developers begin incorporating some of PHP 8.4’s new features into their projects, particularly property hooks.
Which PHP 8.4 features are your favorites? Share your thoughts with our community in the comments!