Function Inlining in Zend Engine

Published On01 Apr 2021

Zend Engine (PHP) special function inlining

Modern PHP is fast! It has several performance features such as OPCache, JIT, and other improvements at the compilation step to make smart optimizations for many PHP applications.

Inspecting the OPCodes is an easy way to make sure that PHP can make the best optimizations as possible. With the OPCodes listed, it becomes clearer if a given PHP snippet takes the shortest number of OPCodes necessary to perform the intended task.

PHP has several over 30 such functions at the moment, that make use of special OPCodes, or otherwise inlined to improve performance.


One example to show this effect is the strlen function. It returns the length of a given string, and PHP tries to optimize preemptively.

if (strlen('Test') < 2) {
    echo "Test";
}

In this snippet, the strlen function is called on a static string literal, and PHP can eliminate this block completely because the length of the Test string is fixed, and the comparison value is also a static value.

This is better revealed with the OPCode dump.

Prior to optimization

php -d opcache.opt_debug_level=0x10000 test.php
0000 JMPZ bool(false) 0002
0001 ECHO string("Test")
0002 RETURN int(1)

After optimization

php -d opcache.opt_debug_level=0x20000 test.php
0000 RETURN int(1)

This example works the other way too, by getting rid of the unnecessary JMP/JMPZ/JMPNZ OPCodes that would be otherwise used in a PHP if block.

if (strlen('Test') < strlen('Test Test')) {
    echo "Test";
}
0000 ECHO string("Test")
0001 RETURN int(1)

Because strlen('Test') < strlen('Test Test') always evaluates to true, the optimized OPCode does not contain any jumps.

Fully-Qualified Function Names

One caveat with the special function handling is that when a special function call is made inside a namespace, the engine cannot use the special handling because it is possible that a function with the same name is declared later in the program.

namespace Foo;

if (strlen('Test') < strlen('Test Test')) {
    echo "Test";
}

Notice how the if block is inside the Foo namespace. The OPCode dump reveals that PHP did not apply the special handling here:

php -d opcache.opt_debug_level=0x20000 test.php
0000 INIT_NS_FCALL_BY_NAME 1 string("Foo\strlen")
0001 SEND_VAL_EX string("Test") 1
0002 V1 = DO_FCALL_BY_NAME
0003 INIT_NS_FCALL_BY_NAME 1 string("Foo\strlen")
0004 SEND_VAL_EX string("Test Test") 1
0005 V2 = DO_FCALL_BY_NAME
0006 T0 = IS_SMALLER V1 V2
0007 JMPZ T0 0009
0008 ECHO string("Test")
0009 RETURN int(1)

Most of the modern PHP code bases use namespaces, and to bring back the special handling, the function names can be prefixed with a backslash (\), or aliased with the use function clause. They are often called as "Fully-Qualified Function Names".

namespace Foo;
if (\strlen('Test') < \strlen('Test Test')) {
    echo "Test";
}

or

namespace Foo;
use strlen;
if (strlen('Test') < strlen('Test Test')) {
    echo "Test";
}

Both snippets above signal the engine that the strlen calls are indeed for the internal strlen function, which allows the engine to go ahead and inline them.

With FQFNs, the engine can again apply its magic:

0000 ECHO string("Test")
0001 RETURN int(1)

The gist of this article is that, when calling PHP internal functions, specially functions with inlining features, always make the fully-qualified function names to enable engine optimizations on them.

List of Functions with Special Handling

All of the following functions have special handling within the Zend Engine, and can benefit from them if called as a Fully-Qualified Function Name.

List up to date until PHP 8.1. Source

Recent Articles on PHP.Watch

All ArticlesFeed 
PHP 8.4 Installation and Upgrade guide for Ubuntu and Debian

PHP 8.4 Installation and Upgrade guide for Ubuntu and Debian

A guide for Debian and Ubuntu on how to install PHP 8.4 on a new server or how to upgrade an existing PHP setup to PHP 8.4.
How to fix `mysql_native_password` not loaded errors on MySQL 8.4

How to fix mysql_native_password not loaded errors on MySQL 8.4

How to fix the SQLSTATE[HY000] [1524] Plugin 'mysql_native_password' is not loaded errors caused in MySQL 8.4 no longer enabling the mysql_native_password plugin by default.
How to fix PHP Curl HTTPS Certificate Authority issues on Windows

How to fix PHP Curl HTTPS Certificate Authority issues on Windows

On Windows, HTTPS requests made with the Curl extension can fail because Curl has no root certificate list to validate the server certificates. This article discusses the secure and effective solutions, and highlights bad advice that can leave PHP applications insecure.
Subscribe to PHP.Watch newsletter for monthly updates

You will receive an email on last Wednesday of every month and on major PHP releases with new articles related to PHP, upcoming changes, new features and what's changing in the language. No marketing emails, no selling of your contacts, no click-tracking, and one-click instant unsubscribe from any email you receive.

Support PHP.Watch — If you find the articles, version information, Codex, and other PHP.Watch contributions useful, consider supporting through GitHub Sponsors. Your sponsorship helps dedicate more time to creating valuable content and improving the PHP community. Together, we can keep the momentum going — thank you for your support!

Thanks to the highest tier sponsor: @TomasVotruba for your generous support to keep PHP.Watch moving 💜