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.
<?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.
<?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
[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.
staticvariable initializers.- Global
constvalues. - Attribute arguments.
It is not allowed in class constants or property defaults, because those are evaluated at compile time.
<?php
class Dependency {}
function build(Dependency $d = new Dependency()): string
{
return get_class($d);
}
echo build();
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.
<?php
function redirect(string $url): never
{
// header("Location: $url");
exit("Redirecting to $url");
}
redirect("/home");
Redirecting to /home
Pure intersection types
PHP 8.1 added A&B types. See Union, Nullable and Other Advanced Types.
Null object pattern
NullLogger, NullCache) is a neat way to make dependencies optional without if ($x !== null) checks everywhere.