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

Named Constructor Pattern

php PHP 5.0+ Intermediate

Also Known As

static factory method factory method named constructor

TL;DR

Static factory methods with descriptive names that replace overloaded constructors — making object creation intent clear when multiple creation paths exist.

Explanation

PHP constructors have a single name (__construct) and cannot be overloaded. Named constructors are static methods that communicate intent: Money::fromCents(500), Money::fromDollars(5.00), DateRange::lastMonth(), User::createGuest(). They can return different subtypes, enforce invariants, or accept different input formats. Combined with a private constructor, they become the only way to instantiate the class — enforcing all construction passes through validated paths.

Common Misconception

Named constructors are just static methods — they communicate intent by name, enforce all construction through validated paths, and can return different subtypes, none of which constructors support.

Why It Matters

new Money(500, 'cents') vs Money::fromCents(500) — the named constructor makes the unit explicit and prevents the ambiguity that causes subtle currency calculation bugs.

Common Mistakes

  • Not making the constructor private when named constructors are the intended API — both paths remain available.
  • Named constructors that accept the same parameters as the constructor — adds verbosity without clarity.
  • Omitting validation in named constructors — they are the perfect place to enforce class invariants.
  • Using static methods for everything instead of identifying which cases genuinely need named constructors.

Code Examples

✗ Vulnerable
// Ambiguous constructor — is 500 cents or dollars?
class Money {
    public function __construct(private int $amount, private string $currency) {}
}
new Money(500, 'USD'); // $500 or 500 cents? Who knows
✓ Fixed
// Named constructors — intent is explicit:
class Money {
    private function __construct(
        private readonly int $cents,
        private readonly string $currency,
    ) {}

    public static function fromCents(int $cents, string $currency): self {
        if ($cents < 0) throw new InvalidArgumentException('Amount cannot be negative');
        return new self($cents, $currency);
    }

    public static function fromDollars(float $dollars, string $currency): self {
        return new self((int) round($dollars * 100), $currency);
    }

    public static function zero(string $currency): self {
        return new self(0, $currency);
    }
}

Added 15 Mar 2026
Edited 22 Mar 2026
Views 32
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings F 2 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 0 pings T 0 pings F 3 pings S 0 pings S 0 pings M 0 pings T 1 ping W 0 pings T 2 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 1 ping S
No pings yesterday
Amazonbot 8 Google 5 Perplexity 3 Ahrefs 2 Unknown AI 2 Majestic 1
crawler 21
DEV INTEL Tools & Severity
🟢 Low ⚙ Fix effort: Low
⚡ Quick Fix
Add static named constructors (Money::fromCents(500), Email::fromString('user@example.com')) to make object creation intent-clear and allow domain validation in the constructor
📦 Applies To
PHP 5.0+ web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
new Money(500, 'cents') — what does 500 mean? Named constructor makes it Money::fromCents(500)
Auto-detectable: ✗ No phpstan
⚠ Related Problems
🤖 AI Agent
Confidence: Low False Positives: High ✗ Manual fix Fix: Low Context: Class

✓ schema.org compliant