PHP Constructor
What Is a Constructor? In simple terms: A constructor is a special method that runs automatically when an object is created with new . It receives the data the object needs and sets it up, so the obje
What Is a Constructor?
In simple terms: A constructor is a special method that runs automatically when an object is created with new. It receives the data the object needs and sets it up, so the object is complete and valid from the very first moment.
Technically: A constructor is a method named __construct(). PHP calls it immediately after allocating a new object, passing along the arguments given to new ClassName(...). It typically assigns properties, validates input, and stores dependencies. A constructor does not return a value; the result of new is always the object itself.
Why Constructors Matter
Without a constructor, objects are created empty and filled in afterwards:
$user = new User();
$user->name = 'Asha';
// forgot to set the email...
Between creation and assignment — or forever, if a line is forgotten — the object is incomplete. A constructor makes the required data part of creating the object:
$user = new User('Asha', 'asha@example.com'); // cannot be created without both values
Defining a Constructor
Syntax
class ClassName
{
public function __construct(Type $param1, Type $param2 = default)
{
// initialize properties
}
}
Example
<?php
class User
{
private string $name;
private string $email;
public function __construct(string $name, string $email)
{
$name = trim($name);
if ($name === '') {
throw new InvalidArgumentException('Name is required.');
}
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
throw new InvalidArgumentException("Invalid email: $email");
}
$this->name = $name;
$this->email = strtolower($email);
}
public function name(): string
{
return $this->name;
}
public function email(): string
{
return $this->email;
}
}
$user = new User(' Asha ', 'Asha@Example.com');
echo $user->name() . ' <' . $user->email() . ">\n";
foreach ([['', 'bala@example.com'], ['Bala', 'bala@']] as [$name, $email]) {
try {
new User($name, $email);
} catch (InvalidArgumentException $e) {
echo "Error: " . $e->getMessage() . "\n";
}
}
Output
Asha <asha@example.com>
Error: Name is required.
Error: Invalid email: bala@
How It Works
new User(' Asha ', 'Asha@Example.com')creates the object and calls__construct()with the two arguments.- The constructor normalizes the values (trims the name, lowercases the email) and validates them.
- If validation fails, the constructor throws an exception. The
newexpression then does not produce an object at all — invalid users simply cannot exist. - Because the typed properties are always assigned in the constructor, they can never be read in an uninitialized state.
Missing Arguments
Calling new User('Asha') without the email throws an ArgumentCountError, just like calling a function with too few arguments. Required constructor parameters are therefore checked by PHP itself.
Optional Parameters and Named Arguments
Constructor parameters support defaults and named arguments like any function:
<?php
class Notification
{
private string $channel;
private string $priority;
private ?DateTimeImmutable $sendAt;
public function __construct(
private string $message,
string $channel = 'email',
string $priority = 'normal',
?DateTimeImmutable $sendAt = null,
) {
$this->channel = $channel;
$this->priority = $priority;
$this->sendAt = $sendAt;
}
public function describe(): string
{
$when = $this->sendAt?->format('d M Y H:i') ?? 'immediately';
return "[{$this->priority}] via {$this->channel}, $when: {$this->message}";
}
}
echo (new Notification('Your order has shipped'))->describe() . "\n";
echo (new Notification('Payment failed', priority: 'high', channel: 'sms'))->describe() . "\n";
echo (new Notification('Class reminder', sendAt: new DateTimeImmutable('2026-10-17 18:30')))->describe() . "\n";
Output
[normal] via email, immediately: Your order has shipped
[high] via sms, immediately: Payment failed
[normal] via email, 17 Oct 2026 18:30: Class reminder
- Defaults cover the common case, so simple notifications need only a message.
- Named arguments make calls with several optional settings readable and let callers skip parameters.
private string $messagein the parameter list is constructor property promotion — it declares and assigns the property in one step (see Constructor Property Promotion).
Named Constructors (Static Factory Methods)
Sometimes an object can be created from different kinds of input. A constructor can have only one signature, but static factory methods with descriptive names can offer several ways to create an object:
<?php
final class Money
{
private function __construct(private int $paise) {}
public static function fromRupees(float $rupees): self
{
return new self((int) round($rupees * 100));
}
public static function fromPaise(int $paise): self
{
return new self($paise);
}
public function add(Money $other): self
{
return new self($this->paise + $other->paise);
}
public function format(): string
{
return '₹' . number_format($this->paise / 100, 2);
}
}
$total = Money::fromRupees(1299.50)->add(Money::fromPaise(50));
echo $total->format() . "\n";
try {
$money = new Money(500);
} catch (Error $e) {
echo $e->getMessage() . "\n";
}
Output
₹1,300.00
Call to private Money::__construct() from global scope
Why This Design Helps
- The private constructor forces callers to use the factories, whose names say what the number means —
fromRupees(1299.50)versusfromPaise(50). With a public constructor,new Money(500)would be ambiguous: 500 rupees or 500 paise? - Money is stored internally as integer paise, which avoids floating-point rounding errors in calculations.
add()returns a new object instead of changing the existing one, makingMoneyan immutable value object.
DateTimeImmutable::createFromFormat() is a built-in example of a named constructor.
Constructors and Dependencies
Objects often need other objects — services — to do their work. The constructor is the natural place to receive them. This is called dependency injection:
<?php
interface Notifier
{
public function send(string $to, string $message): void;
}
final class ConsoleNotifier implements Notifier
{
public function send(string $to, string $message): void
{
echo "To $to: $message\n";
}
}
final class OrderService
{
public function __construct(private Notifier $notifier) {}
public function confirm(int $orderId, string $email): void
{
// ... save the confirmation ...
$this->notifier->send($email, "Order #$orderId is confirmed.");
}
}
$service = new OrderService(new ConsoleNotifier());
$service->confirm(501, 'asha@example.com');
Output
To asha@example.com: Order #501 is confirmed.
OrderService does not create its notifier; it receives one. In production, an email or SMS notifier is passed in; in tests, a fake one. Frameworks build and inject these dependencies automatically through a service container.
Since PHP 8.1, parameter defaults may also create objects: public function __construct(private Notifier $notifier = new ConsoleNotifier()).
What Belongs in a Constructor
| Do | Avoid |
|---|---|
| Assign required data and dependencies | Heavy work: database queries, HTTP requests, file processing |
| Validate and normalize input | Printing output |
| Establish a valid initial state | Starting long-running processes |
Constructors should be fast and predictable. If creating an object requires loading data, do that in a separate method or a factory that then passes the loaded data to the constructor.
Common Mistakes
Misspelling __construct
Effect: __constuct() or _construct() is just an ordinary method that never runs automatically.
Fix: Use exactly two underscores: __construct.
Old-Style Constructors
Effect: In PHP 8, a method with the same name as the class (function User()) is no longer a constructor.
Fix: Always use __construct().
Leaving Objects Half-Initialized
Effect: Required properties are set later (or never), causing uninitialized-property errors.
Fix: Require essential data as constructor parameters.
Returning Values From the Constructor
Effect: The return value is ignored; new always produces the object.
Fix: Use static factory methods when you need alternative creation logic.
Doing Too Much Work
Effect: Slow, untestable object creation with hidden side effects.
Fix: Keep constructors simple; move work into methods or factories.
Best Practices
- Require all essential data in the constructor so objects are always valid.
- Validate and normalize input in the constructor and throw exceptions for invalid data.
- Receive dependencies through the constructor instead of creating them inside the class.
- Use named constructors (static factories) when there are several meaningful ways to create an object.
- Keep constructors free of heavy work and side effects.
Official reference: https://www.php.net/manual/en/language.oop5.decon.php#language.oop5.decon.constructor