What’s New in PHP 8 (Features, Improvements, and the JIT Compiler)


PHP 8 is expected to be released in December 2020 and will bring us a whole bunch of powerful features and great language improvements.

Many RFCs have already been approved and implemented, so it’s time for us to dive into some of the most exciting additions that should make PHP faster and more reliable.

As PHP 8 is still under development, we could see several changes before the final release. We’ll keep track of these changes and update this post regularly, so make sure you don’t miss anything about PHP 8 and check this post again from time to time.

So, what features and improvements should we expect with PHP 8? What’s the biggest thing coming with PHP 8, the next major release of the language?


Let’s dive in!

PHP JIT (Just in Time Compiler)

The most acclaimed feature coming with PHP 8 is the Just-in-time (JIT) compiler. What is JIT all about?

The RFC proposal describes JIT as follows:

“PHP JIT is implemented as an almost independent part of OPcache. It may be enabled/disabled at PHP compile time and at run-time. When enabled, native code of PHP files is stored in an additional region of the OPcache shared memory and op_array→opcodes[].handler(s) keep pointers to the entry points of JIT-ed code.”

So, how did we get to JIT and what is the difference between JIT vs OPcache?

To better understand what JIT is for PHP, let’s take a quick look at how PHP executes from the source code to the final result.

The PHP execution is a 4 stage process:

    • Lexing/Tokenizing: First, the interpreter reads the PHP code and builds a set of tokens.
    • Parsing: The interpreter checks if the script matches the syntax rules and uses tokens to build an Abstract Syntax Tree (AST), which is a hierarchical representation of the structure of source code.
  • Compilation: The interpreter traverses the tree and translates AST nodes into low-level Zend opcodes, which are numeric identifiers determining the type of instruction performed by the Zend VM.
  • Interpretation: Opcodes are interpreted and run on the Zend VM.

The following image shows a visual representation of the basic PHP execution process.

So, how does OPcache make PHP faster? And what changes in the execution process with JIT?

The OPcache Extension

PHP is an interpreted language. This means, when a PHP script runs, the interpreter parses, compiles, and executes the code over and over again on each request. This may result in wasting CPU resources and additional time.

This is where the OPcache extension comes in to play:

“OPcache improves PHP performance by storing precompiled script bytecode in shared memory, thereby removing the need for PHP to load and parse scripts on each request.”

With OPcache enabled, the PHP interpreter goes through the 4 stage process mentioned above only the first time the script runs. Since PHP bytecodes are stored in shared memory, they are immediately available as low-level intermediate representation and can be executed on the Zend VM right away.

As of PHP 5.5, the Zend OPcache extention is available by default and you can check if you have it correctly configured by simply calling phpinfo() from a script on your server or checking out your php.ini file (see OPcache configuration settings).



OPcache has been recently improved with the implementation of preloading, a new OPcache feature added with PHP 7.4. Preloading provides a way to store a specified set of scripts into OPcache memory “before any application code is run“, but it doesn’t bring tangible performance improvement for typical web-based applications.

You can read more about preloading in our introduction to PHP 7.4.

With JIT, PHP moves a step forward.

JIT — The Just in Time Compiler

Even if opcodes are in the form of low-level intermediate representation, they still have to be compiled into machine code. JIT “doesn’t introduce any additional IR (Intermediate Representation) form”, but uses DynASM (Dynamic Assembler for code generation engines) to generate native code directly from PHP byte-code.

In short, JIT translates the hot parts of the intermediate code into machine code. Bypassing compilation, it’d be able to bring considerable improvements in performance and memory usage.

Zeev Surasky, co-author of the PHP JIT proposal, shows how much calculations would be faster with JIT:

But, would JIT effectively improve WordPress performance?

JIT for Live Web Apps

According to the JIT RFC, the just in time compiler implementation should improve PHP performance. But would we really experience such improvements in real-life apps like WordPress?

The early tests show that JIT would make CPU-intensive workloads run significantly faster, however, the RFC warns:

“… like the previous attempts – it currently doesn’t seem to significantly improve real-life apps like WordPress (with opcache.jit=1235 326 req/sec vs 315 req/sec).

It’s planned to provide additional effort, improving JIT for real-life apps, using profiling and speculative optimizations.”

With JIT enabled, the code wouldn’t be run by the Zend VM, but by the CPU itself, and this would improve speed in calculation. Web apps like WordPress also rely on other factors like TTFB, database optimization, HTTP requests, etc.

So, when it comes to WordPress and similar apps, we shouldn’t expect a great boost in PHP execution speed. Nevertheless, JIT could bring several benefits for developers.

According to Nikita Popov:

“The benefits of the JIT compiler are roughly (and as already outlined in the RFC):

  • Significantly better performance for numerical code.
  • Slightly better performance for “typical” PHP web application code.
  • The potential to move more code from C to PHP, because PHP will now be sufficiently fast.”

So, while JIT will hardly bring huge improvements to WordPress performance, it’ll be upgrading PHP to the next level, making it a language many functions could now be written directly in.

The downside, though, would be the greater complexity that can lead to increasing costs in maintenance, stability, and debugging. According to Dmitry Stogov:

“JIT is extremely simple, but anyway it increases the level of the whole PHP complexity, risk of new kind of bugs and cost of development and maintenance.”

The proposal to include JIT in PHP 8 passed with 50 to 2 votes.


PHP 8 Improvements and New Features

Apart from JIT, we can expect many features and improvements with PHP 8. The following list is our handpicked selection of the upcoming additions and changes that should make PHP more reliable and efficient.

Constructor Property Promotion

As a result of an ongoing discussion about how to improve object ergonomics in PHP, the Constructor Property Promotion RFC proposes a new and more concise syntax that will simplify the property declaration, making it shorter and less redundant.

This proposal only relates to promoted parameters, i.e. those method parameters prefixed with publicprotected and private visibility keywords.

Currently, all properties have to be repeated several times (at least four times) before we can use them with objects. Consider the following example from the RFC:

class Point {
    public int $x;
    public int $y;
    public int $z;

    public function __construct(
        int $x = 0,
        int $y = 0,
        int $z = 0,
    ) {
        $this->x = $x;
        $this->y = $y;
        $this->z = $z;

According to Nikita Popov, the author of the RFC, we have to write the property name at least four times in three different places: the property declaration, the constructor parameters, and the property assignment. This syntax is not particularly usable, especially in classes with a good number of properties and more descriptive names.

This RFC proposes to merge the constructor and the parameter definition. So, as of PHP 8, we have a more usable way of declaring parameters and the code seen above can change as shown below:

class Point {
    public function __construct(
        public int $x = 0,
        public int $y = 0,
        public int $z = 0,
    ) {}

And that’s it. So we have a new way to promote properties that is shorter, more readable, and less prone to errors. According to Nikita:

It’s a simple syntactic transformation that we’re doing. But that reduces the amount of boilerplate code you have to write for value objects in particular…

The property declaration is transformed as we’d explicitly declared those properties and we can use the Reflection API to introspect property definitions before the execution (see Desugaring):

Reflection (and other introspection mechanisms) will observe the state after desugaring. This means that promoted properties will appear the same way as explicitly declared properties, and promoted constructor arguments will appear as ordinary constructor arguments.

// before desugaring
class Point {
    public function __construct(public int $x = 0) {}

// after desugaring
class Point {
    public int $x;

    public function __construct(int $x = 0) {
        $this->x = $x;


We don’t have any limitations in using inheritance in conjunction with promoted parameters. Anyway, there’s not a particular relation between parent and child class constructors. According to Nikita:

Usually, we say that methods always have to be compatible with the parent method. […] but this rule does not apply for the constructor. So the constructor really belongs to a single class, and constructors between parent and child class do not have to be compatible in any way.

Here is an example:

class Test {
    public function __construct(
        public int $x = 0
    ) {}

class Child extends Test {
    public function __construct(
        public int $y = 0,
        public int $z = 0,
    ) {

What’s Not Allowed With Promoted Properties

Promoted properties are allowed in non-abstract constructors and traits, but there are several limitations worth mentioning here.

Abstract Constructors

Promoted properties are not allowed in abstract classes and interfaces:

abstract class Test {
    // Error: Abstract constructor.
    abstract public function __construct(private $x);
interface Test {
    // Error: Abstract constructor.
    public function __construct(private $x);

One of the most notable constraints is related to nullability. Previously, when we used a type that wasn’t explicitly nullable, but with a null default value, the type was implicitly nullable. But with property types, we don’t have this implicit behavior because promoted parameters require a property declaration, and the nullable type must be explicitly declared. See the following example from the RFC:

class Test {
    // Error: Using null default on non-nullable property
    public function __construct(public Type $prop = null) {}

    // Correct: Make the type explicitly nullable instead
    public function __construct(public ?Type $prop = null) {}
Callable Type

As callable is not a supported type for properties, we are not allowed to use the callable type in promoted properties:

class Test {
    // Error: Callable type not supported for properties.
    public function __construct(public callable $callback) {}
The var Keyword Is Not Allowed

Only a visibility keyword can be used with promoted parameters, so declaring constructor properties with the var keyword is not allowed (see the following example from the RFC):

class Test {
    // Error: "var" keyword is not supported.
    public function __construct(var $prop) {}
No Duplications Allowed

We can combine promoted properties and explicit properties in the same class, but properties cannot be declared twice:

class Test {
    public string $prop;
    public int $explicitProp;

    // Correct
    public function __construct(public int $promotedProp, int $arg) {
        $this->explicitProp = $arg;

    // Error: Redeclaration of property.
    public function __construct(public string $prop) {}
Variadic Parameters Are Not Allowed

The reason here is that the declared type is different from the variadic parameter, which is actually an array:

class Test {
    // Error: Variadic parameter.
    public function __construct(public string ...$strings) {}

Further Readings

For a closer view at Costructor Property Promotion, listen to this interview with Nikita Popov. For an in-depht overview of object ergonomics in PHP, see this post and the following interview with Larry Garfield.

Validation for Abstract Trait Methods

Traits are defined as “a mechanism for code reuse in single inheritance languages such as PHP”. Typically, they are used to declare methods that can be used in multiple classes.

A trait can also contain abstract methods. These methods simply declare the method’s signature, but the method’s implementation must be done within the class using the trait.

According to the PHP manual,

“Traits support the use of abstract methods in order to impose requirements upon the exhibiting class.”

This also means that the signatures of the methods must match. In other words, the type and the number of required arguments need to be the same.

Anyway, according to Nikita Popov, author of the RFC, signature validation is currently enforced only spottily:

  • It is not enforced in the most common case, where the method implementation is provided by the using class: https://3v4l.org/SeVK3
  • It is enforced if the implementation comes from a parent class: https://3v4l.org/4VCIp
  • It is enforced if the implementation comes from a child class: https://3v4l.org/q7Bq2

The following example from Nikita relates to the first case (not enforced signature):

trait T {
	abstract public function test(int $x);
class C {
	use T;

	// Allowed, but shouldn't be due to invalid type.
	public function test(string $x) {}

With that being said, this RFC proposes to always throw a fatal error if the implementing method is not compatible with the abstract trait method, regardless of its origin:

Fatal error: Declaration of C::test(string $x) must be compatible with T::test(int $x) in /path/to/your/test.php on line 10

This RFC has been unanimously approved.

Incompatible Method Signatures

In PHP, inheritance errors due to incompatible method signatures throw either a fatal error or a warning depending on what is causing the error.

If a class is implementing an interface, incompatible method signatures throw a fatal error. According to Object Interfaces documentation:

“The class implementing the interface must use a method signature which is compatible with LSP (Liskov Substitution Principle). Not doing so will result in a fatal error.”

Here is an example of an inheritance error with an interface:

interface I {
	public function method(array $a);
class C implements I {
	public function method(int $a) {}

In PHP 7.4, the code above would throw the following error:

Fatal error: Declaration of C::method(int $a) must be compatible with I::method(array $a) in /path/to/your/test.php on line 7

A function in a child class with an incompatible signature would throw a warning. See the following code from the RFC:

class C1 {
	public function method(array $a) {}
class C2 extends C1 {
	public function method(int $a) {}

In PHP 7.4, the code above would simply throw a warning:

Warning: Declaration of C2::method(int $a) should be compatible with C1::method(array $a) in /path/to/your/test.php on line 7

Now, this RFC proposes to always throw a fatal error for incompatible method signatures. With PHP 8, the code we saw earlier above would prompt the following:

Fatal error: Declaration of C2::method(int $a) must be compatible with C1::method(array $a) in /path/to/your/test.php on line 7

Arrays Starting With a Negative Index

In PHP, if an array starts with a negative index (start_index < 0), the following indices will start from 0 (more on this in array_fill documentation). Look at the following example:

$a = array_fill(-5, 4, true);

In PHP 7.4 the result would be the following:

array(4) {

Now, this RFC proposes to change things so that the second index would be start_index + 1, whichever the value of start_index.

In PHP 8, the code above would result in the following array:

array(4) {

With PHP 8, arrays starting with a negative index change their behavior. Read more about backward incompatibilities in the RFC.

Union Types 2.0

Union types accept values that can be of different types. Currently, PHP doesn’t provide support for union types, with the exception of the ?Type syntax and the special iterable type.

Before PHP 8, union types could only be specified in phpdoc annotations, as shown in the following example from the RFC:

class Number {
	 * @var int|float $number
	private $number;

	 * @param int|float $number
	public function setNumber($number) {
		$this->number = $number;

	 * @return int|float
	public function getNumber() {
		return $this->number;

Now, the Union types 2.0 RFC proposes to add support for union types in function signatures, so that we won’t rely on inline documentation anymore, but would define union types with a T1|T2|... syntax instead:

class Number {
	private int|float $number;

	public function setNumber(int|float $number): void {
		$this->number = $number;

	public function getNumber(): int|float {
		return $this->number;

As explained by Nikita Popov in the RFC,

“Supporting union types in the language allows us to move more type information from phpdoc into function signatures, with the usual advantages this brings:

  • Types are actually enforced, so mistakes can be caught early.
  • Because they are enforced, type information is less likely to become outdated or miss edge-cases.
  • Types are checked during inheritance, enforcing the Liskov Substitution Principle.
  • Types are available through Reflection.
  • The syntax is a lot less boilerplate-y than phpdoc.”

Union types support all available types, with some limitations:

  • The void type could not be part of a union, as void means that a function does not return any value.
  • The null type is only supported in union types but it’s usage as a standalone type is not allowed.
  • The nullable type notation (?T) is also allowed, meaning T|null, but we are not allowed to include the ?T notation in union types (?T1|T2 is not allowed and we should use T1|T2|null instead).
  • As many functions (i.e. strpos()strstr()substr(), etc.) include false among the possible return types, the false pseudo-type is also supported.

You can read more about Union Types V2 in the RFC.

Consistent Type Errors for Internal Functions

When passing a parameter of illegal type, internal and user-defined functions behave differently.

User-defined functions throw a TypeError, but internal functions behave in a variety of ways, depending on several conditions. Anyway, the typical behavior is to throw a warning and return null. See the following example in PHP 7.4:

var_dump(strlen(new stdClass));

This would result in the following warning:

Warning: strlen() expects parameter 1 to be string, object given in /path/to/your/test.php on line 4

If strict_types is enabled, or argument information specifies types, the behavior would be different. In such scenarios, the type error is detected and results in a TypeError.

This situation would lead to a number of problems well explained in the RFC’s issues section.

To remove these inconsistencies, this RFC proposes to make the internal parameter parsing APIs to always generate a ThrowError in case of a parameter type mismatch.

In PHP 8, the code above throws the following error:

Fatal error: Uncaught TypeError: strlen(): Argument #1 ($str) must be of type string, object given in /path/to/your/test.php:4
Stack trace:
#0 {main}
  thrown in /path/to/your/test.php on line 4

throw Expression

In PHP, throw is a statement, so it’s not possible to use it in places where only an expression is allowed.

This RFC proposes to convert the throw statement into an expression so that it can be used in any context where expressions are allowed. For example, arrow functions, null coalesce operator, ternary and elvis operators, etc.

See the following examples from the RFC:

$callable = fn() => throw new Exception();

// $value is non-nullable.
$value = $nullableValue ?? throw new InvalidArgumentException();
// $value is truthy.
$value = $falsableValue ?: throw new InvalidArgumentException();

Weak Maps

A weak map is a collection of data (objects) in which keys are weakly referenced, meaning that they are not prevented from being garbage collected.

Need a blazing-fast, secure, and developer-friendly hosting for your sites? ALCHosting is built with WordPress developers in mind and provides plenty of tools and a powerful dashboard.

PHP 7.4 added support for weak references as a way to retain a reference to an object that doesn’t prevent the object itself from being destroyed. As pointed out by Nikita Popov,

“Raw weak references are only of limited usefulness by themselves and weak maps are much more commonly used in practice. It is not possible to implement an efficient weak map on top of PHP weak references because the ability to register a destruction callback is not provided.”

That’s why this RFC introduces a WeakMap class to create objects to be used as weak map keys that can be destroyed and removed from the weak map if there aren’t any further references to the key object.

In long-running processes, this would prevent memory leaks and improve performance. See the following example from the RFC:

$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 42;

With PHP 8, the code above would produce the following result (see the code in action here):

object(WeakMap)#1 (1) {
	array(2) {
		object(stdClass)#2 (0) {

If you unset the object, the key is automatically removed from the weak map:


Now the result would be the following:

object(WeakMap)#1 (0) {

For a closer look at Weak maps, see the RFC. The proposal was unanimously approved.

Trailing Comma in Parameter List

Trailing commas are commas appended to lists of items in different contexts. PHP 7.2 introduced trailing commas in list syntax, PHP 7.3 introduced trailing commas in function calls.

PHP 8 now introduces trailing commas in parameter lists with functions, methods, and closures, as shown in the following example:

class Foo {
	public function __construct(
		string $x,
		int $y,
		float $z, // trailing comma
	) {
		// do something

This proposal passed with 58 to 1 votes.

Allow ::class syntax on objects

In order to fetch the name of a class, we can use the FooBar::class syntax. This RFC proposes to extend the same syntax to objects so that it’s now possible to fetch the name of the class of a given object as shown in the example below:

$object = new stdClass;
var_dump($object::class); // "stdClass"
$object = null;
var_dump($object::class); // TypeError

With PHP 8, $object::class provides the same result as get_class($object). If $object is not an object, it throws a TypeError exception.

This proposal was unanimously approved.

Attributes v2

Attributes, also known as annotations, are a form of structured metadata that can be used to specify properties for objects, elements, or files.

Until PHP 7.4, doc-comments were the only way to add metadata to declarations of classes, functions, etc. Now, the Attributes v2 RFC introduces attributes for PHP defining them as a form of structured, syntactic metadata that can be added to declarations of classes, properties, functions, methods, parameters, and constants.

Attributes are added before the declarations they refer to. See the following examples from the RFC:

class Foo
	public const FOO = 'foo';

	public $x;

	public function foo(<<ExampleAttribute>> $bar) { }

$object = new <<ExampleAttribute>> class () { };

function f1() { }

$f2 = <<ExampleAttribute>> function () { };

$f3 = <<ExampleAttribute>> fn () => 1;

Attributes can be added before or after a doc-block comment:

/** docblock */
function foo() {}

Each declaration may have one or more attributes and each attribute may have one or more associated values:

<<FewArguments('Hello', 'World')>>
function foo() {}

See the RFC for a deeper overview of PHP attributes, use cases, and alternative syntax.

New PHP Functions

PHP 8 brings several new functions to the language:


Before PHP 8, strstr and strpos were the typical options for developers to search for a needle inside a given string. Problem is, both functions aren’t considered very intuitive and their usage can be confusing for new PHP developers. See the following example:

$mystring = 'Managed WordPress Hosting';
$findme = 'WordPress';
$pos = strpos($mystring, $findme);

if ($pos !== false) {
	echo "The string has been found";
} else {
	echo "String not found";

In the example above we used the !== comparison operator, which also checks if two values are of the same type. This prevents us to get an error if the position of the needle is 0:

“This function may return Boolean FALSE, but may also return a non-Boolean value which evaluates to FALSE. […] Use the === operator for testing the return value of this function.”

Furthermore, several frameworks provide helper functions to search for a value inside a given string (see Laravel Helpers documentation as an example).

Now, this RFC proposes the introduction of a new function allowing to search inside a string: str_contains.

str_contains ( string $haystack , string $needle ) : bool

Its usage is pretty straightforward. str_contains checks if $needle is found in $haystack and returns true or false accordingly.

So, thanks to str_contains, we can write the following code:

$mystring = 'Managed WordPress Hosting';
$findme   = 'WordPress';

if (str_contains($mystring, $findme)) {
	echo "The string has been found";
} else {
	echo "String not found";

Which is more readable and less prone to errors (see this code in action here).
At the time of this writing, str_contains is case-sensitive, but this could change in the future.

The str_contains proposal passed with 43 to 9 votes.

str_starts_with() and str_ends_with()

In addition to the str_contains function, two new functions allow to search for a needle inside a given string: str_starts_with and str_ends_with.

These new functions check if a given string starts or ends with another string:

str_starts_with (string $haystack , string $needle) : bool
str_ends_with (string $haystack , string $needle) : bool

Both functions return false if $needle is longer than $haystack.

According to Will Hudgins, the author of this RFC,

“The str_starts_with and str_ends_with functionality is so commonly needed that many major PHP frameworks support it, including Symfony, Laravel, Yii, FuelPHP, and Phalcon.”

Thanks to them, we could now avoid using sub-optimal and less intuitive functions like substrstrpos. Both functions are case sensitive:

$str = "WordPress";
if (str_starts_with($str, "Word")) echo "Found!";

if (str_starts_with($str, "word")) echo "Not found!";

You can see this code in action here.

This RFC has been approved with 51 to 4 votes.


get_debug_type is a new PHP function that returns the type of a variable. The new function works in quite a similar way as the gettype function, but get_debug_type returns native type names and resolves class names.

That’s a good improvement for the language, as gettype() is not useful for type checking.

The RFC provides two useful examples to better understand the difference between the new get_debug_type() function and gettype(). The first example shows gettype at work:

$bar = [1,2,3];

if (!($bar instanceof Foo)) { 
	throw new TypeError('Expected ' . Foo::class . ', got ' . (is_object($bar) ? get_class($bar) : gettype($bar)));

With PHP 8, we could use get_debug_type, instead:

if (!($bar instanceof Foo)) { 
	throw new TypeError('Expected ' . Foo::class . ' got ' . get_debug_type($bar));

The following table shows returning values of get_debug_type and gettype:

Value gettype() get_debug_type()
1 integer int
0.1 double float
true boolean bool
false boolean bool
null NULL null
“WordPress” string string
[1,2,3] array array
A class with name “FooBar” object FooBar
An anonymous class object class@anonymous

Additional RFCs

At the time of this writing, several RFCs targeted for PHP 8 are still in draft and/or pending implementation. We’ll add them as soon as their status changes to “Implemented”.

Here is a quick list of additional approved improvements that will be part of PHP 8:

  1. Stringable interface: this RFC introduces a Stringable interface that is automatically added to classes implementing the __to String() method. The main goal here is to use the string|Stringable union type.
  2. New DOM Living Standard APIs in ext/dom: this RFC proposes to implement the current DOM Living Standard to the PHP DOM extension by introducing new interfaces and public properties.
  3. Static return type: PHP 8 introduces the usage of static as return type next to self and parent types.
  4. Variable Syntax Tweaks: this RFC resolves some residual inconsistencies in PHP’s variable syntax.



What a ride! In this post, we covered all the key changes and improvements expected with the release of PHP 8. The most awaited of which is surely the Just in Time compiler, but there’s so much more coming with PHP 8.

Make sure to bookmark this blog post as we’ll add our favorites to the list as soon as they are approved. ????

Now it’s your turn: are you ready to test the upcoming PHP features? Which one is your favorite? Drop a line in the comments section below.

If you enjoyed this article, then you’ll love ALCHosting’s WordPress hosting platform. Turbocharge your website and get 24/7 support from our veteran WordPress team. Our Google Cloud powered infrastructure focuses on auto-scaling, performance, and security. Let us show you the ALCHosting difference! Check out our plans

Like it? Share with your friends!


What's Your Reaction?

confused confused
fail fail
fun fun
geeky geeky
hate hate
lol lol
love love
omg omg
win win
ALC Hosting Networks
Web hosting and domain registration company in the Philippines that guarantees 100% server hardware uptime. We are trusted by top corporations and government agencies. At ALC Hosting, you are assured of fast loading time, and spam-free email.