A constructor is the method that initializes an object when it is created.
Until now, we have created empty objects and then filled in their data line by line.
$dron = new Dron();
$dron->modelo = "Mavic"; // Manual filling... boring and dangerous.The problem? You might forget to set the model and have an “incomplete” Dron flying around.
To avoid this, we use the Constructor. It is a Magic Method (starting with a double underscore __) that runs automatically at the exact moment you use new.
The classic way (the “boilerplate”)
Before PHP 8, writing constructors was an exercise in repetitive typing. You had to write the variable name four times.
Look at this User example. You have to define the property, pass it as an argument, and then assign it.
// OLD CODE (Verbose)
class Usuario {
// 1. Declaration
public string $nombre;
public string $email;
// 2. Arguments
public function __construct(string $nombre, string $email) {
// 3. and 4. Assignment
$this->nombre = $nombre;
$this->email = $email;
}
}If you have a class with 10 properties, this becomes a block of code that adds no value.
Constructor property promotion (the PHP 8 revolution)
Since PHP 8, we can avoid this repetition by using property promotion.
PHP allows us to define visibility (public, private, protected) directly in the constructor arguments.
By doing this, PHP understands: “Ah, you want me to create a property with this name and automatically assign the received value to it.”
Look how clean it is:
// MODERN CODE (Clean)
class Usuario {
// No need to declare anything above!
public function __construct(
public string $nombre,
public string $email,
) {}
// The {} body is usually empty; PHP handles the assignment automatically.
}The result is identical to the previous code, but we have saved 10 lines of “noise.”
Combining with readonly
Property promotion truly shines when we use Value Objects or DTOs, where the data should not change.
We can combine public + readonly in the constructor to create immutable classes in record time.
class Producto {
public function __construct(
public readonly string $sku,
public readonly string $nombre,
public float $precio, // Price can change, so it is not readonly
) {}
}
$monitor = new Producto("MON-001", "Monitor 4K", 299.99);
echo $monitor->nombre; // "Monitor 4K"
// $monitor->sku = "OTRO"; // Fatal Error (It is readonly)Instantiation with named arguments
Now that our constructors have mandatory parameters, using Named Arguments (which we saw in Module 3) when doing new is an excellent practice. It provides incredible clarity.
// Without knowing the order, the code is readable
$usuario = new Usuario(
email: "[email protected]",
nombre: "Luis"
);Validations in the constructor
The constructor is not only for assigning data; it is also for validating it. It is the bouncer at the nightclub: if the data is not valid, the object does not get in (an exception is thrown).
class Usuario {
public function __construct(
public string $email
) {
// Validation: The object is not created if the email is invalid
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException("Invalid email: $email");
}
}
}With this approach, if the object exists, it has passed the constructor’s validation. Other external rules, like the uniqueness of the email in the database, must be checked separately.