Get "PHP 8 in a Nuthshell" (Soon includes PHP 8.4)
Amit Merchant

Amit Merchant

A blog on PHP, JavaScript, and more

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

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 return 2 since 2 is the nearest integer bigger than 1.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 return 1 since 1 is the nearest integer smaller than 1.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 return 2 since 2 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 return 1 since 1 is towards zero and is the nearest integer.

Read RFC

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.

Read RFC

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.

Read RFC

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.

Read RFC

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 as trim() 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 as ltrim() 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 as rtrim() 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

Read RFC

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 */
}

Read RFC

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 as ucfirst() but for multibyte strings.

function mb_ucfirst(string $string, ?string $encoding = null): string

  • mb_lcfirst() - Same as lcfirst() 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̈"
}

New http_(get|clear)_last_response_headers() functions

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];

Read RFC

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 returns NULL.
  • array_find_key - Returns the key of the first element for which the $callback returns true.
  • array_any - Returns true if any of the elements in the array pass the $callback test. Otherwise, it returns false.
  • array_all - Returns true if all of the elements in the array pass the $callback test. Otherwise, it returns false.

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 and set. 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 = 'john@example.com';
echo $user->email; // john@example.com

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)

Read RFC

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
}

Read RFC

Learn the fundamentals of PHP 8 (including 8.1, 8.2, and 8.3), the latest version of PHP, and how to use it today with my new book PHP 8 in a Nutshell. It's a no-fluff and easy-to-digest guide to the latest features and nitty-gritty details of PHP 8. So, if you're looking for a quick and easy way to PHP 8, this is the book for you.

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.

Comments?