PHP 8.0: What's New and Changed

Version StatusUnsupported
Release Date2020-11-26

PHP 8.0 is a major version update and a remarkable milestone in PHP, as it brings several new features to type system, syntax, error handling, strings, object-oriented programming, and more.

It is the efforts of hundreds of people coming together to shape the future of a programming language that powers a significant portion of the Internet web sites and applications.

PHP tries to be conservative with changes that can break a majority of the applications, and yet, it brings several new major features to PHP 8.0.

Features such as Named Parameters, JIT, Attributes, and Constructor Properties bring major improvements and syntax changes, while several minor improvements such as resource to object migrations, improved error handling, and changes and improvements in operators and engine comparisons help seamlessly reduce the chances of overlooked bugs.

Thank you for all the contributors, of whom there are hundreds, for all your efforts, in PHP core, documentation, libraries, testing tools, extensions, authors, package maintainers, PHP developers, and everyone else including you ❤.


Major New Features

Named Parameters

PHP 8.0 allows named parameters in function/method calls in addition to traditional positional parameters.

str_contains(needle: 'Bar', haystack: 'Foobar');

This makes the function/method parameter names part of the public API. The non-standardized DocBlock @no-named-arguments expresses that the library does not provide backwards-compatibility for named parameters.


Attributes

Attributes allows declaring meta-data for functions, classes, properties, and parameters. Attributes map to PHP class names (declared with an Attribute itself), and they can be fetched programmatically with PHP Reflection API.

#[CustomAttribute]
class Foo {
    #[AnotherAttribute(42)]
    public function bar(): void {}
}

Attributes makes it easy and performant to declare Attributes/annotations that previously required storing them in DocBlock comments, and parsing the string to infer them.


Constructor Properties

A new syntax to declare class properties right from the class constructor (__construct magic method).

class User {
    public function __construct(private string $name) {}
}

In the constructor, PHP 8.0 supports declaring the visibility (public, private, or protected) and type. Those properties will be registered as class properties with same visibility and type they are declared in the constructor.

This backwards-incompatible feature can help reduce boilerplate code when declaring value-object classes.


Just-In-Time Compilation

PHP Opcache supports JIT. It's disabled by default, and if enabled, JIT compiles and caches native instructions. It does not make a noticeable difference in IO-bound web applications, but provides a performance boost for CPU-heavy applications.

# Enabling JIT in php.ini
opcache.enable=1
opcache.jit_buffer_size=100M
opcache.jit=tracing

Note that JIT is still new, and had bug fixes as late as a day before PHP 8.0.0 release. It makes debugging and profiling more difficult with the added extra layer.


Union Types

Union Types extend type declarations (return types, parameters, and class properties) to declare more than one type.

function parse_value(string|int|float): string|null {}

It also supports false as a special type (for Boolean false), a trait that's prevalent in legacy code that did not use Exceptions.


Null-safe Operator

Null-safe operator provides safety in method/property chaining when the return value or property can be null.

return $user->getAddress()?->getCountry()?->isoCode;

The ?-> null-safe operator short-circuits the rest of the expression if it encounters a null value, and immediately returns null without causing any errors.


match expressions

Match expressions are similar to switch blocks, but match blocks provide type-safe comparisons, supports a return value, does not require break statements to break-out, and supports multiple matching values. it also guarantees that at least one branch is matched, ensuring all cases are accounted for.

$response = match('test') {
    'test' => $this->sendTestAlert(),
    'send' => $this->sendNuclearAlert(),
};

Not all switch blocks might convert well to match blocks. Code that requires backwards-compatibility, switch blocks with multiple statements (as opposed to single-line expressions), or expects fall-through functionality still fits the switch statements.


WeakMaps

A WeakMap allows to store and associate arbitrary values for object keys, but without preventing the garbage collector from clearing it the object falls out of scope in everywhere else.

A WeakMap is similar to SplObjectStorage, as in both WeakMap and splObjectStorage use objectss as the key, and allows storage of arbitrary values. However, a WeakMap does not prevent the object from being garbage collected.


New Functions and Classes

PHP 8.0 introduces a few new functions to ease string inspections (contains, starts with sub-string, or ends with sub-string) to replace the meticulous strpos() !== false calls that are less readable, and error-prone due to weak type comparisons.


Function Description Example
str_contains Haystack contains the needle somewhere str_contains('Foobar', 'Foo')
str_starts_with Haystack starts with the needle string str_starts_with('PHP 8.0', 'PHP')
str_ends_with Haystack ends with needle string str_ends_with('PHP 8.0', '8.0')

PHP 8.0 also brings functions such as fdiv, get_resource_id, get_debug_type, and preg_last_error_msg

Function Description
fdiv Float division supporting IEEE-754 standard on Floating-Point Arithmetic.
get_resource_id Returns the internal ID for a given PHP resource
get_debug_type Returns the internal type of a passed variable
preg_last_error_msg Returns a human-friendly error message for the last preg operation.

New Stringable interface

The new Stringable interface is automatically added to all classes that implement __toString method, and those explicitly declare that they implements Stringable.

With the Stringable interface, it is now easy to declare types as string|Stringable for on functions that can accept/return strings or objects with a __toString() method.


New PhpToken Tokenizer class

The new PhpToken class provides a more fluent Object-Oriented interface as an alternative to the legacy array-based to token_get_all function.


Type System Improvements

PHP 8.0 improves typing system with the addition of Union Types and the mixed type.

Union Types

Union Types extend type declarations (return types, parameters, and class properties) to declare more than one type.

function parse_value(string|int|float): string|null {}

It also supports false as a special type (for Boolean false), a trait that's prevalent in legacy code that did not use Exceptions.


New mixed pseudo type

PHP 8.0 brings mixed type, that was already being widely used in DocBlock comments.

function dd(mixed $var): void {
    var_dump($var);
}

mixed type can be used to indicate that it accepts any type, or can return any type. In a class/interface context, mixed type plays by the same rules of Liskov Substitution Principle.


static return type for class methods

static return type, already support as a DocBlock return type, is now supported in PHP 8.0. The static return type declares an object of the called class will be returned.

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

Error Handling Improvements

A major and backwards-incompatible change in PHP is that internal functions now throw exceptions on type errors or value errors.

This corrects PHP's historical behavior of emitting a warning, and returning null when it encounters a value that it cannot use. This behavior is often undesired because PHP warnings does not halt the execution of the remaining block.


Internal function warnings now throw TypeError and ValueError exceptions

Almost entirety of PHP internal functions now enforce type checking, and instead of warnings, PHP now throws TypeError or ValueError exceptions. On legacy code bases, this change can cause issues, now that the errors are more boldly and unforgivably handled.


throw in expressions

Prior to PHP 8.0, it was not possible to throw exceptions from an expression (e.g a ternary statement). This is now allowed in PHP 8.0.

$value = isset($_GET['value'])
    ? $_GET['value']
    : throw new \InvalidArgumentException('value not set');

catch exceptions only by type

It is possible to catch exceptions by their type, without capturing the exception object.

try {}
catch(TypeError) {
  // Did not catch the $exception object
}

Default error reporting is set to E_ALL

PHP 8.0 default configuration is to show all error messages by default. It was configured to hide deprecation and strict warnings in older versions.

PHP Startup Errors are displayed by default

PHP now displays startup-errors (failure to load dynamic extensions, invalid INI configurations, etc) by default.

Assertions throw exceptions by default

PHP assertions (assert()) now throw exceptions on assertion failures. Prior to PHP 8.0, it required an explicit configuration that was disabled by default.


@ Error Suppression operator does not silent fatal errors

PHP 8.0 corrects the behavior of @ error suppression operator, where it silenced fatal errors, which lead to a script failure, because the @ operator does not prevent fatal errors, but rather hide the error message display.


Resource to Object Migration

One of the long-term efforts in PHP development was to move away from the resource types. They were difficult to deal with, and even in PHP 8.0, does not offer typing support.

PHP resource objects do not play well with garbage-collector well either, which resulted in memory leaks in resource objects such as xml.

In PHP 8.0, some of the most used extensions changes moves away from the traditional resource objects to standard PHP classes.

In PHP 8.0, they are work as value-objects as opposed to fully-features classes with methods in them. Majority of these classes do not allow instantiating with the new Foo() construct either, and must be instantiated with the existing functions that returned resource objects in prior versions.

PHP 8.0's resource to object migration is quite seamless, as in all functions return and accept the new objects, and behave by the same semantics of the previous resource objects.

Extension resource (PHP < 8.0) object (PHP >= 8.0)
Curl Curl CurlHandle
Curl curl_multi CurlMultiHandle
Curl curl_share CurlShareHandle
GD gd GdImage
Sockets Socket Socket
Sockets AddressInfo AddressInfo
OpenSSL OpenSSL key OpenSSLAsymmetricKey
OpenSSL OpenSSL X.509 OpenSSLCertificate
OpenSSL OpenSSL X.509 CSR OpenSSLCertificateSigningRequest
XMLWriter xmlwriter XMLWriter
XML xml XMLParser

PHP Object Oriented Programming changes

PHP 8.0 is the first major version to be strict on Liskov Substitution Principle violations. Prior to PHP 8.0, PHP was not consistent on how it handled incompatible method signatures.

In PHP 8.0, all signature mismatches, including abstract traits, result in a fatal error. Further, it enforces signatures for PHP magic methods.


Fatal errors on incompatible method signatures

PHP 8.0 throws fatal errors when Liskov Substitution Principle is not followed when classes are extended, or interfaces are implemented.

Prior to PHP 8.0, incompatible signatures only emitted a warning.

class Foo {
    public function process(stdClass $item): array{}
}

class SuperFoo extends Foo{
    public function process(array $items): array{}
    //                      ^^^^^ mismatch
}
Fatal error: Declaration of SuperFoo::process(array $items): array must be compatible with Foo::process(stdClass $item): array in ... on line ...

Class magic method signatures are strictly enforced

From PHP 8.0 and later, magic methods (e.g __toString(), __get(), etc), if they declare types, must implement the signature PHP expects. This is to avoid the smallest chance of the user declaring a magic method that doesn't follow the semantic meaning.

class Foo {
    public function __toString(): object {
    }
}

Declarations like Foo::__toString(): object was allowed in previous PHP versions, but PHP 8.0 and throws an exception if the signature does not meet the requirements.


Calling non-static class methods statically result in a fatal error

PHP 8.0 no longer allows calling class methods as a static method.

class Foo {
    public function bar() {}
}
Foo::bar();

Previous versions emitted a deprecation notice, but from PHP 8.0 and later, this results in a fatal error.


Inheritance rules are not applied to private class methods

PHP 8.0 relaxes the signature, abstract, and static flag enforcement for private class methods. This change comes from the rationale that private methods are just that: Private.

From PHP 8.0, it is now allowed for the child classes to declare abstract methods, and change static/flags for private methods.


::class magic constant is now supported on objects

The ::class magic constant returns the fully-qualified class name. This was only allowed on class names (such as Foo\Bar::class), but in PHP 8.0, the ::class magic constant works on instantiated objects too.


String-related changes

In PHP 8.0, there are several subtle changes that might be not obvious at first, but can result in quite unexpected results.

A major difference in PHP 8.0 is that, PHP now considers there is an empty string between every character in a given string.

Prior to PHP 8.0, checking for an empty string needle ("") was not allowed, but in PHP 8.0, PHP will happily accept it, and return that there is indeed an empty string between each character.

The multi-byte handling, or functions like strlen still returns same values as the older versions, but all functions that check for a substring in a given string are changed.

In addition, PHP 8.0 changes how the string concatenation operator priorities, and supports new modifiers in sprintf functions such as %h and %H modifiers and * width and precision modifier.

substr, iconv_substr, grapheme_substr return empty string on out-of-bound offsets

These functions clamp the needle and offset parameters to the string length, and returns an empty string instead of returning false.

substr('FooBar', 42, 3); // "" in PHP >=8.0, false in PHP < 8.0
mb_substr('FooBar', 42, 3); // "" in PHP >=8.0, false in PHP < 8.0
iconv_substr('FooBar', 42, 3); // "" in PHP >=8.0, false in PHP < 8.0
grapheme_substr('FooBar', 42, 3); // "" in PHP >=8.0, false in PHP < 8.0

+/- operators take higher precedence when used with concat (.) operator

When the mathematical + and - operators are used in the same expression with the concatenation operator (.), the + and - operators take higher precedence. This resulted in a deprecation notice in PHP versions prior to 8.0, but now it happens silently and as per the warning.

echo 35 + 7 . '.' . 0 + 5;
// 42.5 in PHP >= 8.0
// 47 in PHP <= 8.0

Locale-independent float to string casting

When a float value is coerced to string, PHP no longer uses the locale. Prior to PHP 8.0, PHP considered the current locale (which is not thread-safe) or system locale when doing so. This resulted in inconsistent string outputs because certain locales, specially European ones, swap the thousand separator and decimal sign from the US locale.
For locale-aware float to string cast, the %f modifier in printf class of functions can be used.

In addition, the new %h and %H modifiers for printf class of functions provide locale-indepdent variants of %g and %G modifiers.


$thanks ❤

PHP 8.0 is an amazing effort from hundreds of awesome people. It's a major milesone in PHP's history. Thank you to everyone who helped from code, to documentation, to conferences, to articles to all of the developers.

PHP 8 Thanks name cloud
Download posters



New Features in PHP 8.0

Syntax/Functionality Changes in PHP 8.0

Deprecations in PHP 8.0

Removed Features and Functionality in PHP 8.0