Lazy Objects (PHP 8.4)
Also Known As
lazy proxy
lazy ghost
lazy initialization
ReflectionClass lazy
TL;DR
PHP 8.4 native lazy object proxies defer initialisation until first access — enabling zero-cost dependency injection of services that may never be used in a request.
Explanation
PHP 8.4 adds ReflectionClass::newLazyProxy() and ReflectionClass::newLazyGhost() to the core. A lazy ghost is the real object class with initialisation deferred; a lazy proxy wraps a real instance created on demand. Both defer expensive construction (DB connections, API clients, heavy config parsing) until the first property access or method call. This is the pattern that DI containers have implemented manually for years — now it is a first-class language feature.
Common Misconception
✗ Lazy objects are just lazy loading — lazy objects are native VM-level proxies; property access on a lazy object transparently triggers initialisation with no interface changes to the consuming code.
Why It Matters
A DI container that injects 50 services into a controller only needs to construct the 3 actually used per request — lazy objects make this automatic at the language level.
Common Mistakes
- Using lazy objects for services that are always accessed — the proxy overhead is wasted if initialisation always happens.
- Not understanding that lazy ghost requires the real class, while lazy proxy can wrap any compatible class.
- Serialising lazy objects before initialisation — behaviour depends on whether the object has been triggered.
- Triggering initialisation in the constructor of the surrounding class — defeats the purpose.
Code Examples
✗ Vulnerable
// Eager construction — all services built even if unused:
class OrderController {
public function __construct(
private ReportingService $reporting, // Heavy — connects to BI DB
private AuditService $audit, // Heavy — reads config files
private NotificationService $notify, // Heavy — loads templates
) {} // All three built on every request, even simple reads
}
✓ Fixed
// PHP 8.4 lazy proxy — only constructed if accessed:
$container->bind(ReportingService::class, function() {
$reflector = new ReflectionClass(ReportingService::class);
return $reflector->newLazyProxy(
fn() => new ReportingService($this->get(BiDatabase::class))
);
});
// OrderController receives the proxy
// ReportingService only constructed when $this->reporting->method() called
References
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
16 Mar 2026
Edited
22 Mar 2026
Views
23
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 0
No pings yet today
Amazonbot 7
Perplexity 3
ChatGPT 3
Ahrefs 2
Unknown AI 2
Google 1
Also referenced
How they use it
crawler 17
crawler_json 1
Related categories
⚡
DEV INTEL
Tools & Severity
🟢 Low
⚙ Fix effort: Medium
⚡ Quick Fix
Use lazy initialisation for expensive objects in DI containers — PHP 8.4 adds native lazy object support via ReflectionClass; otherwise use proxy generators like ocramius/proxy-manager
📦 Applies To
PHP 8.4+
web
cli
symfony
laravel
🔗 Prerequisites
🔍 Detection Hints
Expensive service instantiated eagerly on every request even when not used; DI container initialising all services unconditionally
Auto-detectable:
✗ No
blackfire
symfony-profiler
⚠ Related Problems
🤖 AI Agent
Confidence: Low
False Positives: Medium
✗ Manual fix
Fix: Medium
Context: Class
Tests: Update