Constructors
A constructor defines what an Object needs at the moment it is created. PHP
calls the special __construct() Method automatically when code uses new.
Why constructors exist
Section titled “Why constructors exist”Typed Properties without initial values are uninitialized:
final class Order{ public int $id; public float $total;}Reading $order->id before assigning it throws an Error. It is not silently
treated as null, because null is not an int.
A constructor makes the required starting State explicit:
final class Order{ public int $id; public float $total;
public function __construct(int $id, float $total) { $this->id = $id; $this->total = $total; }}
$order = new Order(15, 500);Calling new Order() without the required Arguments throws an
ArgumentCountError; callers cannot receive an incompletely initialized Order.
An untyped declared Property behaves differently: without an explicit value,
it starts as null. A typed Property—even a nullable one—must receive a value
or an explicit = null default before it can be read.
Constructor Property Promotion
Section titled “Constructor Property Promotion”PHP can declare and initialize Properties directly in the constructor:
final class Order{ public function __construct( public int $id, public float $total, ) {}}The visibility keyword (public, protected, or private) makes the
Parameter a promoted Property. Without visibility it remains an ordinary
constructor Parameter.
Required and optional values
Section titled “Required and optional values”A Parameter without a default is required. A Parameter with a default is optional and normally follows the required Parameters:
final class Order{ public function __construct( public int $id, public float $total, public ?string $note = null, ) {}}The default is used only when the caller omits that Argument. ?string allows
string or null.
Named Arguments
Section titled “Named Arguments”Named Arguments connect values to Parameter names:
$order = new Order( total: 500, id: 15,);Their order can differ from the declaration, and optional Arguments can be
selected clearly. The trade-off is that Parameter names become part of the
caller’s API: renaming $id to $orderId breaks callers that use id:.
Constructor Injection
Section titled “Constructor Injection”An Object should receive a Dependency it requires to do its work rather than exist in an unusable, incomplete form:
final class CheckoutService{ public function __construct( private PaymentGateway $paymentGateway, ) {}}The Dependency is explicit, the Service does not create a concrete payment provider internally, and callers cannot construct the Service without a PaymentGateway. Dependency relationships are covered in depth later.
Named constructors and static factories
Section titled “Named constructors and static factories”Sometimes a named creation operation communicates intent better than raw constructor Arguments:
$order = Order::createDraft();Use a named constructor when it represents a genuine creation path with a meaningful name. Do not create a factory for every Class or every possible status. A transition such as activating an existing Order may belong in an instance Behavior instead.
Practical example
Section titled “Practical example”final class Product{ public function __construct( public int $id, public float $price, public ?string $note = null, ) {}}
$product = new Product( price: 5.00, id: 5,);The Product is created with id = 5, price = 5.0, and note = null.
Common mistakes
Section titled “Common mistakes”- Allowing an Object to exist before its required State is initialized.
- Treating an uninitialized typed Property as if it were
null. - Forgetting that a nullable typed Property still needs initialization.
- Putting optional Parameters before required ones.
- Renaming Parameters without considering Named-Argument callers.
- Creating required Dependencies inside the dependent Class.
- Introducing factories where a clear constructor is sufficient.
Summary
Section titled “Summary”Constructors establish the Object’s required starting State and Dependencies. Property Promotion removes repetitive declarations, defaults model optional values, and Named Arguments improve readability at the cost of making Parameter names part of the public calling contract.
Knowledge check
Section titled “Knowledge check”- Why is a typed Property without a value not automatically
null? - What turns a constructor Parameter into a promoted Property?
- What trade-off comes with Named Arguments?
- Why should a required Dependency be received through the constructor?