You get a bonus - 1 coin for daily activity. Now you have 1 coin

What's new in PHP 8

Lecture



PHP 8, the new major version of PHP, is expected to be released on December 3, 2020. It is currently under very active development, so a lot is likely to change in the coming months.

In this post I will keep an up-to-date list of what's expected: new features, performance improvements, and breaking changes. Since PHP 8 is a new major version, there is a good chance your code could break. However, if you have been keeping up with the latest releases, upgrading shouldn't be too difficult, since most of the breaking changes were already deprecated in earlier 7.* versions.

Besides breaking changes, PHP 8 also has a number of nice new features, such as the JIT compiler and union types; and that's not all!

New features expected in PHP 8

PHP 8 is still under active development, so this list will grow and change over time.

Whats new in PHP 8


Union Types rfc

Given PHP's dynamically typed nature, there are many cases where union types can be useful. Union types are a set of two or more types that indicate that any of them may be used.

public function foo(Foo|Bar $input): int|float;

Note that void can never be part of a union type, since it indicates "no return value at all". In addition, nullable unions can be written using |null or using the existing ? notation:

public function foo(Foo|null $foo): void;

public function bar(?Bar $bar): void;

JIT compiler

The JIT compiler promises significant performance improvements, though not always in the context of web requests.

No exact benchmarks have been made yet, but they will certainly come.

How does it work?

There is a so-called "monitor" that will watch the code while it runs. When this monitor detects repeated parts of your code, it flags these parts as "warm" or "hot", depending on frequency.

These hot parts can be compiled into optimized machine code and used on the fly instead of the actual code.

The JIT compiler can significantly improve your program's performance, but doing this correctly is not easy.

However, since PHP is most often used in a web context, we should also measure the impact of JIT there.

It turns out that, unfortunately, there is much less code involved when handling a web request. This doesn't mean JIT can't improve web performance at all, but we won't see gains similar to the fractal example.

Is this a reason to abandon JIT? Definitely not! There are good arguments for adding it, even though it might not have the performance impact we're hoping for.

  • It opens the door to using PHP as a very performant language outside the web.
  • Over time JIT can be improved, just like our code.

These are solid arguments in favor of JIT.

However, having machine code as output will make it harder to debug possible bugs in PHP's JIT compiler.


Attributes

Attributes, commonly known as annotations in other languages, offer a way to add metadata to classes without parsing docblocks.

Here is an example of what attributes look like, from the RFC:

use App\Attributes\ExampleAttribute;

<>
class Foo
{
    <>
    public const FOO = 'foo';

    <>
    public $x;

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

    public function __construct($value)
    {
        $this->value = $value;
    }
}

Static return type

Although returning self was already possible, static was not a valid return type until PHP 8.

Given PHP's dynamically typed nature, this feature will be useful for many developers.

class Foo
{
    public function test(): static
    {
        return new static();
    }
}

Throwing exceptions in expressions

This RFC turns throw from a statement into an expression, which allows exceptions to be thrown in many new places:

$triggerError = fn () => throw new MyError();

$foo = $bar['offset'] ?? throw new OffsetDoesNotExist('offset');

Weak references (WeakMap) rfc

Built on top of the weak references RFC added in PHP 7.4, a WeakMap implementation is added in PHP 8, in which WeakMaps store references to objects that don't prevent those objects from being garbage collected.

Take the example of an ORM: they often implement caches that hold references to entity classes to improve the performance of relationships between entities. These entity objects cannot be collected as long as the cache holds a reference to them, even if the cache is the only reference to them.

If this caching layer uses weak references and maps instead, PHP will garbage collect these objects when nothing else references them anymore. Especially in the case of ORMs, which can manage several hundred if not thousands of objects per request, weak maps can offer a better, more resource-friendly way to work with these objects.

Here is what weak maps look like, an example from the RFC:

class Foo
{
    private WeakMap $cache;

    public function getSomethingWithCaching(object $obj): object
    {
        return $this->cache[$obj]
           ??= $this->computeSomethingExpensive($obj);
    }
}

::class availability for objects rfc

A small but useful new feature: it's now possible to use ::class on objects,

instead of using get_class() on them. This works the same way as get_class().

$foo = new Foo();

var_dump($foo::class);

Trailing comma in parameter lists rfc

While this was already possible when calling a function, parameter lists still lacked support for a trailing comma. This is now allowed in PHP 8, which means you can do the following:

public function(
    string $parameterA,
    int $parameterB,
    Foo $objectfoo,
) {
    // …
}

Creating DateTime objects from an interface

You could already create a DateTime object from a DateTimeImmutable object using DateTime::createFromImmutable($immutableDateTime), but the reverse was harder. By adding DateTime::createFromInterface() and DatetimeImmutable::createFromInterface() there is now a generalized way to convert DateTime and DateTimeImmutable objects into each other.

DateTime::createFromInterface(DateTimeInterface $other);

DateTimeImmutable::createFromInterface(DateTimeInterface $other);

New Stringable interface rfc

The Stringable interface can be used to type-hint anything that is a string or implements __toString(). In addition, whenever a class implements __toString(), it automatically implements the interface behind the scenes, and there is no need to implement it manually.

class Foo
{
    public function __toString(): string
    {
        return 'foo';
    }
}

function bar(Stringable $stringable) { /* … */ }

bar(new Foo());
bar('abc');

New str_contains() function rfc

Some might say it's long overdue, but we finally no longer need to rely on strpos to find out whether a string contains another string.

Instead of this:

if (strpos('string with lots of words', 'words') !== false) { /* … */ }

You can now do this

if (str_contains('string with lots of words', 'words')) { /* … */ }

New str_starts_with() and str_ends_with() functions rfc

Two other long-overdue additions, these two functions are now added to the core.

str_starts_with('haystack', 'hay'); // true
str_ends_with('haystack', 'stack'); // true

New fdiv() function

The new fdiv() function does something similar to the fmod() and intdiv() functions, allowing division by 0. Instead of an error, you get INF, -INF, or NAN, depending on the case.


New get_debug_type() function rfc

get_debug_type() returns the type of a variable. It seems like gettype() would fit the bill?

But get_debug_type() returns more useful output for arrays, strings, anonymous classes, and objects.

For example, calling gettype() on a class \Foo\Bar will return object. Using get_debug_type() will return the class name instead.

A full list of differences between get_debug_type() and gettype() can be found in the RFC.


Abstract trait method improvements rfc

Traits will be able to contain abstract methods that must be implemented by the classes using them.

However, there is a caveat: before PHP 8, the signature of these method implementations was not checked.

The following used to be valid:

trait Test {
    abstract public function test(int $input): int;
}

class UsesTrait
{
    use Test;

    public function test($input)
    {
        return $input;
    }
}

PHP 8 will perform proper method signature checking when using a trait and implementing its abstract methods.

This means you now need to write this instead:

class UsesTrait
{
    use Test;

    public function test(int $input): int
    {
        return $input;
    }
}

Object implementation of token_get_all() rfc

The token_get_all() function returns an array of values. This RFC adds a PhpToken class with a PhpToken::getAll() method. This implementation works with objects instead of plain values. It consumes less memory and is easier to read.


Syntax changes rfc

From the RFC: "The uniform variable syntax RFC resolved a number of inconsistencies in PHP's variable syntax.

This RFC intends to address a small portion of the missed cases".

https://wiki.php.net/rfc/variable_syntax_tweaks


Type annotations for internal functions

Many people have asked for proper type annotations to be added for all internal functions.

This has been a long-standing issue, and it can finally be resolved thanks to all the changes made to PHP in previous versions

. This means internal functions and methods will have full type information in reflection.

Breaking changes

As mentioned earlier: this is a major upgrade, and therefore there will be breaking changes. The best thing to do is look at the full list of breaking changes in the UPGRADING document.

https://github.com/php/php-src/blob/master/UPGRADING#L20

Many of these breaking changes were already deprecated in previous 7.* versions, so if you've kept up over the last few years, moving to PHP 8 shouldn't be too hard.


Consistent type errors rfc

User-defined functions in PHP already generate TypeErrors, but internal functions did not — they instead issued warnings and returned null. Starting with PHP 8, the behavior of internal functions has become consistent.


Reclassified warning mechanism rfc

A number of errors that previously only triggered warnings or notices have been converted into proper errors. The following warnings have been changed.

  • Undefined variable: Error exception instead of notice
  • Undefined array index: warning instead of notice
  • Division by zero: DivisionByZeroError exception instead of warning
  • Attempt to increment/decrement property "%s" of non-object: Error exception instead of warning
  • Attempt to modify property "%s" of non-object: Error exception instead of warning
  • Attempt to assign property "%s" of non-object: Error exception instead of warning
  • Creating default object from empty value: Error exception instead of warning
  • Trying to get property "%s" of non-object: warning instead of notice
  • Undefined property: %s::$%s: warning instead of notice
  • Cannot add element to the array as the next element is already occupied: Error exception instead of warning
  • Cannot unset offset in a non-array variable: Error exception instead of warning
  • Cannot use a scalar value as an array: Error exception instead of warning
  • Only arrays and Traversables can be unpacked: TypeError exception instead of warning
  • Invalid argument supplied for foreach(): TypeError exception instead of warning
  • Illegal offset type: TypeError exception instead of warning
  • Illegal offset type in isset or empty: TypeError exception instead of warning
  • Illegal offset type in unset: TypeError exception instead of warning
  • Array to string conversion: warning instead of notice
  • Resource ID #%d used as offset, casting to integer (%d): warning instead of notice
  • String offset cast occurred: warning instead of notice
  • Uninitialized string offset: %d: warning instead of notice
  • Cannot assign an empty string to a string offset: Error exception instead of warning
  • Supplied resource is not a valid stream resource: TypeError exception instead of warning

The @ operator no longer suppresses fatal errors

It's quite possible this change may reveal errors that were previously hidden before PHP 8.

Be sure to set display_errors=Off on your production servers!


Default error reporting level

It is now E_ALL instead of everything except E_NOTICE and E_DEPRECATED. This means many errors that were previously silently ignored may now appear, even though they may have already existed before PHP 8.


Default PDO error mode rfc

From the RFC: the current default error mode for PDO is silent. This means that when an SQL error occurs, no errors or warnings are raised and no exceptions are thrown unless the developer implements their own explicit error handling.

This RFC changes the default error mode, which will change to PDO::ERRMODE_EXCEPTION in PHP 8.


Concatenation precedence rfc

Although this change was already deprecated in PHP 7.4, it now takes effect. If you had written something like this:

echo "sum: " . $a + $b;

PHP would previously interpret it like this:

echo ("sum: " . $a) + $b;

PHP 8 will make it interpreted like this:

echo "sum: " . ($a + $b);

Stricter type checks for arithmetic and bitwise operators rfc

Before PHP 8 it was possible to apply arithmetic or bitwise operators to arrays, resources, or objects. This is no longer possible and will throw a TypeError:

[] % [42];
$object + 4;

Reflection method signature changes

Three class method signatures in reflection have been changed:

ReflectionClass::newInstance($args);
ReflectionFunction::invoke($args);
ReflectionMethod::invoke($object, $args);

They are now:

ReflectionClass::newInstance(...$args);
ReflectionFunction::invoke(...$args);
ReflectionMethod::invoke($object, ...$args);

The upgrading guide states that if you extend these classes and still want to support both PHP 7 and PHP 8, the following signatures are allowed:

ReflectionClass::newInstance($arg = null, ...$args);
ReflectionFunction::invoke($arg = null, ...$args);
ReflectionMethod::invoke($object, $arg = null, ...$args);

Several minor deprecations

During the development of PHP 7.*, several deprecations were introduced that are now finalized in PHP 8.

  • Deprecations from PHP 7.2
  • Deprecations from PHP 7.3
  • Deprecations from PHP 7.4

Comments

To leave a comment

If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Lectures and tutorial on "Running server side scripts using PHP as an example (LAMP)"

Terms: Running server side scripts using PHP as an example (LAMP)