← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

new in Initializers (PHP 8.4)

PHP PHP 8.4+ Intermediate
debt(d7/e3/b3/t5)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). The detection_hints.tools list is empty. The misconception — that the default object is shared like a static variable — produces no error or warning; behavior is silently correct (fresh instance per call) but the developer's mental model is wrong. The side-effect misuse (DB connections as defaults) is silent in production until users hit it. No standard linter rule is cited, and the version-mismatch case is caught by a parse error (d1), but the behavioral traps require code review or runtime observation.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix shows a straightforward one-component replacement: swap the nullable parameter + null-coalescing body pattern for the new initializer syntax. It touches the function signature and potentially removes a body line, but stays within one function/component. Not quite e1 because the refactor requires identifying all such patterns across a component rather than a single-line swap.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3). This is an optional syntactic feature; once adopted in a signature it imposes a minor ongoing cost — maintainers must understand the fresh-instance semantics and the PHP 8.4 version requirement. It does not spread across the codebase structurally unless widely adopted. The applies_to scope is broad (web, cli) but the choice is per-function, not architectural.

t5 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'notable trap' (t5). The misconception field explicitly states the canonical wrong belief: developers expect the default object to be shared across calls like a static variable (singleton-like), but each call gets a fresh instance. This is a documented gotcha that contradicts intuitions formed by other languages or by analogies to static defaults, but it is a single well-known edge case rather than a catastrophic systematic misunderstanding.

About DEBT scoring →

Also Known As

new in defaults new in initializers PHP 8.4 new expression

TL;DR

PHP 8.4 allows 'new ClassName()' expressions in default parameter values, attribute arguments, and static property initialisers — removing the need for null defaults combined with late assignment in constructors.

Explanation

PHP previously required that default parameter values, attribute arguments, and property initialisers be compile-time constants — scalar values, arrays, class constants, and null. Creating a default object value required a null default and manual assignment in the constructor body. PHP 8.4 allows 'new' expressions in these positions. 'function foo(Logger $log = new NullLogger())' is now valid. The object is created fresh for each call where the default is needed. This also works in property promotion: 'public function __construct(private Logger $log = new NullLogger()) {}' — clean null-object pattern without boilerplate.

Common Misconception

The default object is shared across all calls like a static variable. It is not — 'new NullLogger()' in a default creates a fresh instance for each invocation that uses the default. This is the opposite of a singleton.

Why It Matters

This feature eliminates a common boilerplate pattern: nullable parameters with manual instantiation. The null object pattern — where you provide a 'do nothing' default implementation — becomes a one-liner. Code that previously required three or four lines (nullable param + docblock + null check in body + assignment) now expresses intent cleanly in the signature.

Common Mistakes

  • Using new in initializers in PHP < 8.4 — parse error; check version before adopting.
  • Expecting the same instance to be reused across calls — each call that triggers the default creates a new object.
  • Using new with classes that have constructor side effects (DB connections, file handles) as defaults — the side effect runs on every call that omits the argument.
  • Forgetting this works in attribute arguments too — attribute classes that accept object dependencies can now have clean defaults.

Code Examples

✗ Vulnerable
<?php
// ❌ Pre-8.4 null default + manual instantiation boilerplate
class OrderService
{
    private Logger $logger;
    private Cache $cache;

    public function __construct(
        ?Logger $logger = null,
        ?Cache $cache = null
    ) {
        $this->logger = $logger ?? new NullLogger();
        $this->cache  = $cache  ?? new ArrayCache();
    }
}
✓ Fixed
<?php
// ✅ PHP 8.4 — new in initializers, clean constructor promotion
class OrderService
{
    public function __construct(
        private Logger $logger = new NullLogger(),
        private Cache  $cache  = new ArrayCache(),
    ) {}
}

// Also valid in standalone functions
function sendNotification(
    string $message,
    Mailer $mailer = new SmtpMailer(),
): void {
    $mailer->send($message);
}

Added 23 Mar 2026
Views 36
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 1 ping W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 1 ping W 0 pings T 0 pings F 1 ping S 0 pings S 2 pings M 0 pings T 0 pings W 0 pings T 2 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Amazonbot 7 Ahrefs 3 SEMrush 3 Scrapy 3 Perplexity 2 Google 2 ChatGPT 1 Claude 1 Meta AI 1 Majestic 1
crawler 22 crawler_json 2
DEV INTEL Tools & Severity
⚙ Fix effort: Low
⚡ Quick Fix
Replace 'function log(?Logger $l = null) { $l ??= new NullLogger(); }' with 'function log(Logger $l = new NullLogger()) {}' in PHP 8.4+.
📦 Applies To
PHP 8.4+ web cli


✓ schema.org compliant