About

PHP 8.0 - What's New and Changed?

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 the same visibility and type they are declared in the constructor.

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

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.

The 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 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 backward-compatibility, switch blocks with multiple statements (as opposed to single-line expressions), or expects fall-through functionality still fits the switch statements.

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

Function

Description

Example

Haystack contains the needle somewhere

str_contains('Foobar', 'Foo')

Needle starts with haystack string

str_starts_with('PHP 8.0', 'PHP)

Needle ends with haystack string

str_ends_with('PHP 8.0', '8.0)

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.

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.

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, already supports 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();
    }
}

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');

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

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

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 ...

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.

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.

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.

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.

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

Last updated