Quick Links

PHP 8.1 was released in November 2021 as the latest minor version of the PHP language. It adds several new language features alongside some smaller improvements and performance enhancements. There are a few breaking changes to be aware of but most upgrades from PHP 8.0 should be straightforward.

New Features

This year's PHP update adds several new features to enhance developer productivity. Here's what you'll be able to use after you upgrade.

Enums

Enum types are finally part of the PHP language. They let you specify that a value must be one of a set of predefined constants.

Compared with regular constants, enums give you built-in validation when used as method parameters and return values. They may also have backing values and methods that attach extra behavior.

# PHP 8.0
    

class PostStatus {

const Draft = 1;

const Published = 2;

}

# PHP 8.0 - Cannot typehint that the value

# of `$PostStatus` must be defined

# within `PostStatus`.

class Post {

public function __construct(

public string $Headline,

public int $PostStatus) {}

}

# PHP 8.1

enum PostStatus {

case Draft = 1;

case Published = 2;

}

# PHP 8.1 - Now it's impossible to pass an

# invalid value as the status.

class Post {

public function __construct(

public string $Headline,

public PostStatus $PostStatus) {}

}

Readonly Properties

The new

        readonly
    

keyword marks a class property as immutable. Readonly properties can only be written to once. Trying to change their value after initialization will throw an error.

This simplifies the creation of simple immutable value objects. Previously you needed to add repetitive methods to a class if you wanted to expose property values without allowing modification. Now you can make the properties

        public
    

without risking unintentional mutations.

# PHP 8.0 - This quickly becomes repetitive!
    

class Post {

public function __construct(

protected string $Headline) {}

public function getHeadline() : string {

return $this -> Headline;

}

}

# PHP 8.1

class Post {

public function __construct(

public readonly string $Headline) {}

}

Intersection Types

PHP's type system now understands intersections. These let you specify that a value must implement multiple interfaces.

This is useful in cases where code needs to call instance methods that are defined by two different interfaces. Previously you needed to create a new interface extending both of the desired ones. This wasn't always practical in cases where you couldn't modify the target class to implement your new interface.

Intersections are defined by combining two or more types with an ampersand haracter. They add more flexibility to the type system after the introduction of union types in PHP 8.0. One caveat is you can't currently use intersection and union types together -

        Countable&Stringable|CustomType
    

is off limits in this release.

# PHP 8.0
    

interface CountableString extends Countable, Stringable {}

public function countAndReport(CountableString $value) : void {

echo "$value: " . count($value);

}

# PHP 8.1

public function countAndReport(Countable&Stringable $value) : void {

echo "$value: " . count($value);

}

Fibers

Fibers are mechanism to facilitate concurrent execution. They make it easier to implement resumable code blocks that can be suspended from anywhere in the stack.

The Fibers API is relatively low-level. It's not expected that end user developers will interact with it on a regular basis. Instead, Fibers will be integrated into libraries that offer async APIs and event loop implementations. They're a control flow system that enables simplified higher-level abstractions.

Fibers offer a path to async function calls that look like regular synchronous operations. You can cut out the boilerplate associated with promises and callbacks. Fibers handle suspending and resuming the code at the appropriate points, offering one API for blocking and non-blocking implementations of operations.

# PHP 8.0 - A promise-based HTTP client
    

$response = $httpClient -> request("http://example.com")

-> then(function (Response $Response) {

return $Response -> getBody();

});

print $response;

# PHP 8.1 - An HTTP client using Fibers internally

$response = $httpClient -> request("https://example.com");

print $response -> getBody();

First-Class Callables

Callables are now first-class citizens in the PHP language. This means you can directly assign functions to variables, without using the old array syntax or explicit closure creation.

This change will be most useful in functional programming contexts. Passing functions around is cleaner and easier with direct variable assignment.

# PHP 8.0
    

$builtin = Closure::fromCallable("array_filter");

$method = [$this, "getData"];

# PHP 8.1

$builtin = array_filter(...);

$method = $this -> getData(...);

The

        ...
    

replacing the function's parameters is a mandatory part of the syntax. This signals you want to assign the function to the variable, instead of calling the function and assigning its return value.

Using New In Initializers

Another new feature is the

        new
    

keyword in initializers. You can now use object instances as default parameter values:

# PHP 8.0
    

class SendNewsletterAction {

protected Mailer $Mailer;

public function __construct(?Mailer $Mailer=null) {

$this -> Mailer = $Mailer ?? new SmtpMailer();

}

}

# PHP 8.1

class SendNewsletterAction {

public function __construct(

protected readonly Mailer $Mailer=new SmtpMailer()) {}

}

The

        new
    

keyword will also work with static variables, global constants, and attribute arguments. It gives you more flexibility and convenience when you want an object instance as a default value.

Final Class Constants

The

        final
    

keyword is now supported for class constants. You can mark individual constants as

        final
    

without sealing the entire class. Previously child classes were free to overwrite the value of constants inherited from parents.

# PHP 8.0 - Cannot disallow this without 
    

# making the whole class `final`

class DemoClass {

public const DemoConstant = 1;

}

class ChildClass extends DemoClass {

public const DemoConstant = 2;

}

# PHP 8.1 - Just the constant can be `final`.

class DemoClass {

final public const DemoConstant = 1;

}

class ChildClass extends DemoClass {

// Raises a fatal error

public const DemoConstant = 2;

}

Others

Besides the big ticket items listed above, PHP 8.1 also adds a few convenience features that help to made code more self-documenting. You can write octal numbers with an explicit notation, such as

        0o14
    

, and use the new

        never
    

return type to denote functions that will throw an exception or exit the current script. This helps inform dead code detection in static analysis software.

There are a few new functions in the standard library.

        array_is_list()
    

lets you check whether an array is a list (

        ["foo", "bar"]
    

) or associative (

        ["foo" => "bar"]
    

), providing a built-in implementation for a function that's commonly found in existing userland code.

        fsync()
    

and

        fdatasync()
    

let you synchronize file pointer changes back to the underlying storage media. Sodium support for the XChaCha20 and Ristretto255 hashing algorithms has been added too.

Finally, this release brings the prospect of performance improvements. Optimizations including a class inheritance cache could deliver a free performance boost of up to 8%. This would be most noticeable in heavily object-oriented codebases with many parent-child relationships. PHP will now cache these links so it's not continually rebuilding them.

Backwards Incompatible Changes

As a minor version bump, there are a few breaking changes and deprecation to be aware of before you upgrade. Here are the ones most likely to be an issue in your codebase:

  • Overwriting the
            $_GLOBALS
        
    array is no longer allowed. You can still write to individual keys but can't reassign
            $_GLOBALS
        
    itself.
  • If you extend a built-in class, your overridden methods must declare a return type that's compatible with the original one. A deprecation warning will be emitted if you specify an incompatible type. A
            #[ReturnTypeWillChange]
        
    attribute is currently supported as a way to avoid this for libraries that need to support multiple PHP versions.
  • Passing
            null
        
    to non-nullable parameters of internal functions has been deprecated. This was previously allowed but was inconsistent with typehint enforcement for user-created functions.
  • Implicit casts from
            int
        
    to
            float
        
    have been deprecated - this affects scenarios such as
            $array[10.1]
        
    , where the mantissa will be lost as arrays must have integer keys.
  • As part of PHP's ongoing work to remove
            resource
        
    types, several function groups now work with new objects instead of resources. These changes apply to the FileInfo, FTP, IMAP, LDAP, PgSQL and PSpell APIs.
  • Functions that work with HTML entities such as
            htmlspecialchars()
        
    and
            htmlentities()
        
    now escape apostrophes by default, so
            '
        
    will become
            '
        
    .
  • Directly calling static methods on traits is deprecated; you should call the method on a class that uses the trait instead.

Other removals and deprecations affect individual APIs including PDO, MySQLi, and Standard. You should review the entire migration guide before you upgrade your project.

Taken together, this cycle's changes are fairly innocuous. Codebases that were already conformant with modern PHP best practices shouldn't experience too many issues when moving to 8.1. Popular frameworks including Symfony and Laravel already support PHP 8.1 too.

Conclusion

PHP 8.1 adds many new features that make the development experience easier and more streamlined. Enums have long been a missing piece of the type system while readonly properties and

        new
    

in initializers will make it quicker to write new classes.

Fibers help to make async PHP more approachable while first-class callables facilitate streamlined function references when practicing functional programming techniques. All these changes further mature PHP as a flexible language that offers strong safety guarantees for your code while still being simple to work with.