News

PHP 8.1 has landed! ๐ŸŽ‰ Here’s what’s new.

PHP 8.1 is now available fleet wide! It's faster and comes with some great new features.

We are pleased to announce that PHP 8.1 support has been tested and added to all WordFleet servers. You can switch individual sites over to it whenever you are ready.

As PHP 8.1 is hot off the press, we’re keeping the default version for new sites pegged at 8.0. We’ll update the default to 8.1 in the coming weeks, but you can switch new and existing sites to 8.1 at any time. Check out the migration notes below before you do.

Note that most of the content below has been adapted from the official PHP announcement. We are listing most new features here for convenience. Please do read the original post for full details. All credit here goes to the PHP devs โค๏ธ

PHP 8.1 key features include:

๐Ÿš€ Performance Boosts

PHP 8.1 further improves upon the performance gains from its predecessor. According to the official PHP 8.1 announcement, WordPress sites can expect to see a general performance increase of 3.5% relative to PHP 8.0. The Symphony Demo app saw an impressive speed increase of 23%.

๐Ÿงฎ Enumerations (enums)

Enums are a common feature in most other languages. They allow developers to mix type safety with numeric constants. This ensures that you can’t put invalid values where a specific enum type belongs.

class PostStatus
{
  const DRAFT = 1;
  const PUBLISHED = 2;
  const ARCHIVED = 3;
}

function doSomethingWithStatus(int $status) { ... }
enum Status
{
  case Draft;
  case Published;
  case Archived;
}

function doSomethingWithStatus(Status status) { ... }

๐Ÿšซ Read-only Properties

Prior to PHP 8.1, in order to get read-only properties, you would be required to make a property private and expose its value via a getter function. Now, you can use the readonly modifier to ensure that the property is available for direct access but not mutable once assigned.

class BlogPost
{
  private Status $status;

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

  public function getStatus(): Status
  {
    return $this->status;
  }
}
class BlogPost
{
  public readonly Status $status;

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

โœˆ๏ธ First-class Callable Syntax

You can now reference any function, including with its $this context, by placing an ellipsis between the function call paranthesis ().

// A reference to $this->getName()
$getThisName = [$this, 'getName'];
$getThisName();

// A reference to the function strlen
$strlen = Closure::fromCallable('strlen');
$str = 'foo';

echo $strlen($str); // 3
$getThisName = $this->getName(...);
$getThisName();

$strlen = strlen(...);
$str = 'foo';

echo $strlen($str); // 3

๐Ÿ†• in initializers

You can now use the new keyword to initialize:

  • Default parameter values
  • Static variables
  • Global constants
  • Attribute arguments
class Service
{
    private Logger $logger;

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

This also makes it possible to use nested attributes.

class User
{
    /**
     * @Assert\All({
     *     @Assert\NotNull,
     *     @Assert\Length(min=5)
     * })
     */
    public string $name = '';
}
class User
{
    #[\Assert\All(
        new \Assert\NotNull,
        new \Assert\Length(min: 6))
    ]
    public string $name = '';
}

๐Ÿšฆ Pure Intersection Types

Intersection types are useful when a particular input value must satisfy multiple type constraints simultaneously.

The announcement notes that it is not currently possible to mix intersection and union types together such as A&B|C.

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);
}
function count_and_iterate(Iterator&Countable $value) {
    foreach ($value as $val) {
        echo $val;
    }

    count($value);
}

๐Ÿ›‘ Static analyzers rejoice! A new return type: never

You can now specify never as the return type of a function to indicate that it halts execution of the program, indicating the code below it is effectively dead.

function redirect(string $uri) {
    header('Location: ' . $uri);
    exit();
}

function redirectToLoginPage() {
    redirect('/login');

    // This code is dead, but IDEs may not warn you as such.
    echo 'Hello';
}
function redirect(string $uri): never {
    header('Location: ' . $uri);
    exit();
}

function redirectToLoginPage(): never {
    redirect('/login');

    // This code is easily marked as dead by most static analyzers.
    echo 'Hello'; 
}

๐Ÿ”’ Final class constraints

You can now ensure that class constants are final, guaranteeing that attempts to overwrite them in child classes cause a fatal error.

class Birb
{
    public const CONSTANT = "Arthur";
}

class Duck extends Birb
{
    // No error. The constant has been replaced.
    public const CONSTANT = "Diana";
}
class Birb
{
  final public const CONSTANT = "Arthur";
}

class Duck extends Birb
{
  // A fatal error. What have you done?
  public const CONSTANT = "Diana";
}

๐Ÿ™ Explicit octal numeral notation

Not very exciting, but much less confusing. You may prefix octal notation with 0o. No 0w0 notation yet though, sadly.

016 === 16; // false because `016` is octal for `14` and it's confusing
016 === 14; // true
0o16 === 16; // false โ€” not confusing with explicit notation
0o16 === 14; // true

๐Ÿ“ฆ Array unpacking support for string-keyed arrays

PHP used to support unpacking inside arrays, but only if the arrays had integer keys. PHP 8.1 adds support to unpack arrays with string keys too.

$arrayA = ['a' => 1];
$arrayB = ['b' => 2];

$result = array_merge(['a' => 0], $arrayA, $arrayB);

// ['a' => 1, 'b' => 2]
$arrayA = ['a' => 1];
$arrayB = ['b' => 2];

$result = ['a' => 0, ...$arrayA, ...$arrayB];

// ['a' => 1, 'b' => 2]

๐Ÿงต Fibers

Oh baby. The exciting stuff for framework developers like ourselves. Fibers are the building blocks for implementing lightweight cooperative concurrency.

Fibers themselves don’t function like goroutines in programming languages like Go. You will need a proper event loop to run them, but they allow blocking and non-blocking implementations to share the same API.

This effectively eliminates:

  • Alternate versions of functions suffixed with Async or Sync.
  • The usual boilerplate around promises.

Most developers won’t use fibers directly in their day to day. However, this addition will make developing concurrent applications much more fluent as popular frameworks and libraries begin to use them.

๐Ÿ“– Want to read more?

There’s more information available, including new deprecations and backwards compatibility breaks, in the official PHP 8.1 announcement post.

There are some detailed notes on deprecations and backwards compatibility breakages, so definitely give it a read. Or don’t. I’m just a blog, not a cop.

๐Ÿšš Migration Notes

For existing sites, we highly recommend that you test it on a dev or staging site first. This will ensure that your site functions correctly with the new update, especially if you have an older theme or a lot of plugins.

You’re probably fine if you’re already running smoothly on PHP 8.0. Regardless, we do recommend testing as there have been a few deprecations and backwards compatibility breakages in this update.

๐ŸŽŠ Conclusion

PHP 8.1 is an exciting update to the language, and we’re pleased to announce our support of it fleet wide. We hope you’ll test it out soon.

WordFleet offers premium managed WordPress hosting with no site or visitor limits. Prices start at $12 / server per month.