What’s new in PHP 8.1

Enumerations

// PHP < 8.1
class Status
{
    const DRAFT = 'draft';
    const PUBLISHED = 'published';
    const ARCHIVED = 'archived';
}
function acceptStatus(string $status) {...}
// PHP 8.1
enum Status
{
    case Draft;
    case Published;
    case Archived;
}
function acceptStatus(Status $status) {...}

Readonly Properties

// PHP < 8.1
class BlogData
{
    private Status $status;
   
    public function __construct(Status $status) 
    {
        $this->status = $status;
    }
    
    public function getStatus(): Status 
    {
        return $this->status;    
    }
}
// PHP 8.1
class BlogData
{
    public readonly Status $status;
   
    public function __construct(Status $status) 
    {
        $this->status = $status;
    }
}

First-class Callable Syntax

// PHP < 8.1
$foo = [$this, 'foo'];
$fn = Closure::fromCallable('strlen');
// PHP 8.1
$foo = $this->foo(...);
$fn = strlen(...);

New in initializers

// PHP < 8.1
class Service 
{
    private Logger $logger;
 
    public function __construct(
        ?Logger $logger = null,
    ) {
        $this->logger = $logger ?? new NullLogger();
    }
}
// PHP 8.1
class Service 
{
    private Logger $logger;
    
    public function __construct(
        Logger $logger = new NullLogger(),
    ) {
        $this->logger = $logger;
    }
}

Pure Intersection Types

// PHP < 8.1
function count_and_iterate(Iterator $value) {
    if (!($value instanceof Countable)) {
        throw new TypeError('value must be Countable');
    }

    foreach ($value as $val) {
        echo $val;
    }

    count($value);
}
// PHP 8.1
function count_and_iterate(Iterator&Countable $value) {
    foreach ($value as $val) {
        echo $val;
    }

    count($value);
}

Never return type

// PHP < 8.1
function redirect(string $uri) {
    header('Location: ' . $uri);
    exit();
}
 
function redirectToLoginPage() {
    redirect('/login');
    echo 'Hello'; // <- dead code
}
// PHP 8.1
function redirect(string $uri): never {
    header('Location: ' . $uri);
    exit();
}
 
function redirectToLoginPage(): never {
    redirect('/login');
    echo 'Hello'; // <- dead code detected by static analysis 
}

Final class constants

// PHP < 8.1
class Foo
{
    public const XX = "foo";
}

class Bar extends Foo
{
    public const XX = "bar"; // No error
}
// PHP 8.1
class Foo
{
    final public const XX = "foo";
}

class Bar extends Foo
{
    public const XX = "bar"; // Fatal error
}

Explicit Octal numeral notation

// PHP < 8.1

016 === 16; // false because `016` is octal for `14` and it's confusing
016 === 14; // true
// PHP 8.1

0o16 === 16; // false — not confusing with explicit notation
0o16 === 14; // true

Fibers

// PHP < 8.1
$httpClient->request('https://example.com/')
        ->then(function (Response $response) {
            return $response->getBody()->buffer();
        })
        ->then(function (string $responseBody) {
            print json_decode($responseBody)['code'];
        });
// PHP 8.1
$response = $httpClient->request('https://example.com/');
print json_decode($response->getBody()->buffer())['code'];

Array unpacking support for string-keyed arrays

// PHP < 8.1
$arrayA = ['a' => 1];
$arrayB = ['b' => 2];
$result = array_merge(['a' => 0], $arrayA, $arrayB);
// ['a' => 1, 'b' => 2]
// PHP 8.1
$arrayA = ['a' => 1];
$arrayB = ['b' => 2];
$result = ['a' => 0, ...$arrayA, ...$arrayB];
// ['a' => 1, 'b' => 2]
Scroll to Top