Skip to content

PHP Tutorial Modern PHP Features

new in Initializers and the never Type

Lesson 231 of 369 2 min read By Deepak Paswan Updated Oct 5, 2026

Before PHP 8.1, a default value had to be a plain constant. You could not write new in a default. The usual workaround was a null default and a check inside the function.

index.php
PHP
<?php
class Service
{
    private Logger $logger;

    public function __construct(?Logger $logger = null)
    {
        $this->logger = $logger ?? new NullLogger();   // the workaround
    }
}

PHP 8.1 allows new directly in the default.

index.php
PHP
<?php
interface Logger
{
    public function log(string $message): void;
}

class NullLogger implements Logger
{
    public function log(string $message): void {}
}

class EchoLogger implements Logger
{
    public function log(string $message): void
    {
        echo "[log] $message\n";
    }
}

class Service
{
    public function __construct(private Logger $logger = new NullLogger()) {}

    public function run(): void
    {
        $this->logger->log("Service ran");
    }
}

(new Service())->run();                    // uses NullLogger, prints nothing
(new Service(new EchoLogger()))->run();    // prints the message
Output
OUTPUT
[log] Service ran

The NullLogger default means the class always has a logger, so it never has to check for null.

Where new is allowed

  • Parameter default values.
  • static variable initializers.
  • Global const values.
  • Attribute arguments.

It is not allowed in class constants or property defaults, because those are evaluated at compile time.

index.php
PHP
<?php
class Dependency {}

function build(Dependency $d = new Dependency()): string
{
    return get_class($d);
}

echo build();
Output
OUTPUT
Dependency

What can you put after new

Only a class name that PHP can resolve at compile time, with arguments that are themselves constant expressions. You cannot use variables or function calls as arguments.

The never return type

PHP 8.1 also added never for functions that do not return, because they always throw or exit. You met it in Return Types.

index.php
PHP
<?php
function redirect(string $url): never
{
    // header("Location: $url");
    exit("Redirecting to $url");
}

redirect("/home");
Output
OUTPUT
Redirecting to /home

Pure intersection types

PHP 8.1 added A&B types. See Union, Nullable and Other Advanced Types.

lightbulb
Null object pattern
A “do nothing” default object (NullLogger, NullCache) is a neat way to make dependencies optional without if ($x !== null) checks everywhere.