Named constructors as multiple constructors alternative

Unlike other programming languages, PHP does not allow for multiple constructors in a class. However, defining multiple constructors can be beneficial in various scenarios, such as creating an object from different data types. If this functionality is not available in PHP, named constructors can be used as an alternative solution.

What is a named constructor? It is a static method that we can use to create an object instance. These methods have the advantage of being more explicit than the basic constructor, as they can add meaning to the object’s construction.

Consider the following example:

readonly class Color
{
    public function __construct(
        public int $red,
        public int $blue,
        public int $green,
    ) {}
}

The previous code defines an object that stores a color. This color consists of three levels: red, green and blue. The main constructor fills in these corresponding values. However, sometimes it may be useful to create a color from its hexadecimal value. In this case, we could introduce a constructor called fromHexCode to do this task:

readonly class Color
{
    public static function fromHexCode(string $code): self
    {
        $code = ltrim($code, '#');

        $red = hexdec(substr($code, 0, 2));
        $blue = hexdec(substr($code, 2, 2));
        $green = hexdec(substr($code, 4, 2));

        return new self($red, $blue, $green);
    }

    public function __construct(
        public int $red,
        public int $blue,
        public int $green,
    ) {}
}

Named constructors can simplify object creation by providing default values based on the context. They also encapsulate the complex creation logic, making the code easier to read and understand.

This technique is also popular in Domain Driven Design. It can help clarify the business process that led to the data creation. For example, consider user registration in an application:

class User
{
    public static function fromRegistration(string $name, string $email, string $password): self
    {
        $user = new self($name, $email, $password);

        // additionnal business logic related to registration

        return $user;
    }

    public static function fromSocialLogin(string $name, string $email): self
    {
        $user = new self($name, $email);

        // additionnal business logic related to social login

        return $user;
    }

    private function __construct(
        public string $name,
        public string $email,
        public ?string $password = null,
    ) {}
}

This last example introduces a User class with a private constructor. To create an object instance, a named constructor must be used. The constructor choice will depend on the business context. This approach allows us to see user creation requirements depends on a specific use case.

When a user registers via the application, it should provide a name, an email and a password. However if the same user is logged through a social login, we only need the name and the email. The password is not necessary when using an external login system.

In summary, using named constructors can significantly improve the code base and enhance code readability. I highly recommend that you explore this technique as it under-exploited.