Naming constructors in PHP

namingphp

The chances are that you are already aware of the concept of named constructors. If not, take a look at Matthias Verraes' excellent article Named Constructors in PHP.

When it comes to consistently naming constructors, I currently apply the following rules for different types of objects.

Services

When a service requires dependencies, use the default __construct() method for instantiation.

<?php

declare(strict_types=1);

namespace Example;

final class AccountService
{
    public function __construct(
        private readonly AccountEventStore $accountEventStore,
        private readonly AccountEventDispatcher $accountEventDispatcher,
    ) {
    }

    // ...
}

Why? Typically, a service has a single way of construction. Consequently, it only needs a single constructor.

During yesterday's Office Hours of The PHP Consulting Company, Stefan Priebsch shared that he experimented with named constructors for services. Stefan marks the __construct() method as private and uses a named constructor collaboratingWith() instead, which sounds like an idea that I might give a try.

<?php

declare(strict_types=1);

namespace Example;

final class AccountService
{
    private function __construct(
        private readonly AccountEventStore $accountEventStore,
        private readonly AccountEventDispatcher $accountEventDispatcher,
    ) {
    }

    public static function collaboratingWith(
        AccountEventStore $accountEventStore,
        AccountEventDispatcher $accountEventDispatcher,
    ): self {
        return new self(
            $accountEventStore,
            $accountEventDispatcher,
        );
    }

    // ...
}

Stefan also pointed out that marking the __construct() method as private and using a named constructor instead has the advantage that developers cannot invoke the __construct() method on an already instantiated object, which is technically possible. Stefan notes that an RFC proposing to disallow multiple constructor calls has become inactive. Hence, marking the __construct() method as private and using a named constructor takes away one more possibility for a PHP developer to shoot themselves in the foot.

Following our conversation, Stefan has published How do you name constructors?, where he provides additional examples, explanations, and reasoning.

Exceptions

When implementing a custom exception, allow instantiation via a named constructor that suits the purpose, never override the default constructor.

<?php

declare(strict_types=1);

namespace Example;

use Doctrine\ORM;

final class CouldNotFindUser extends \RuntimeException
{
    public static function identifiedBy(UserId $userId): self
    {
        return new self(\sprintf(
            'Could not find a user identified by "%s".',
            $userId->toString(),
        ));
    }

    public static function withEmailAddress(EmailAddress $emailAddress): self
    {
        return new self(\sprintf(
            'Could not find a user with email-address "%s".',
            $emailAddress->toString(),
        ));
    }
}

Why? Depending on the scenario, you may want to throw an exception of the same type, but based on different inputs. Named constructors allow doing just that. When you override the default constructor, it will be harder for cases where you are not ready to extract a named constructor yet or want to pass in an exception code, a previous exception, or both during the construction of the exception.

Entities

When an ORM entity requires other entities or values, use the default __construct() method for instantiation.

In a typical project using the Doctrine ORM an entity also has only a single way of construction. I have chosen to use the default __construct() method for this kind of entity.

<?php

declare(strict_types=1);

namespace Example;

use Doctrine\ORM;

#[ORM\Mapping\Entity]
#[ORM\Mapping\Table(name="user_reset_password_token")]
class UserResetPasswordToken
{
    #[ORM\Mapping\Column(
        length: 255,
        name: 'token',
        nullable: false,
        type: 'example_token',
        unique: true,
    )]
    #[ORM\Mapping\Id]
    private Token $token;

    #[ORM\Mapping\JoinColumn(
        name: 'user_id',
        nullable: false,
        onDelete: 'CASCADE',
        referencedColumnName: 'id',
        unique: true,
    )]
    private User $user;

    #[ORM\Mapping\Column(
        name: 'expires_at',
        nullable: false,
        type: 'datetime_immutable',
    )]
    private \DateTimeImmutable $expiresAt;

    public function __construct(
        private Token $token,
        private User $user,
        private \DateTimeImmutable $expiresAt,
    ) {
    }

    // ...
}

Although I am currently still working on projects using the Doctrine ORM, I have become more interested in working on projects using message-driven architectures.

For an event-sourced entity, mark the default __construct() method as private, add a named constructor create() that initiates the lifecycle of the entity, and add a named constructor fromEvents() that allows reconstituting the entity from a list of events.

<?php

declare(strict_types=1);

namespace Example;

final class Order
{
    /**
     * @var array<int, OrderEvent>
     */
    private array $events = [];

    private function __construct(OrderEvent ...$events)
    {
        foreach ($events as $event) {
            $this->apply($event);
        }
    }

    public static function create(
        OrderId $orderId,
        OrderStartedAt $orderStartedAt,
    ): self {
        $order = new self();

        $order->record(OrderStarted::create(
            $orderId,
            $orderStartedAt,
        ));

        return $orderId;
    }

    public static function fromEvents(OrderEvent ...$orderEvents): self
    {
        return new self(...$orderEvents);
    }

    // ...
}

Value Objects

I distinguish two three types of value objects:

Value Objects wrapping a primitive value

When a value object wraps a single primitive value, such as

  • a bool
  • a float
  • an int
  • a string

or an immutable object from the Standard PHP Library (SPL) , for example,

  • an instance of DateTimeImmutable

mark the default __construct() method as private and add named constructors with corresponding counterparts for accessing the wrapped value (or a representation of it).

For example, to allow instantiation of a Name value object from a string value, I add a constructor with the name fromString() and a corresponding method toString() that provides access to the wrapped string value.

<?php

declare(strict_types=1);

namespace Example;

final class Name
{
    private function __construct(private readonly string $value)
    {
    }

    /**
     * @throws \InvalidArgumentException
     */
    public static function fromString(string $value): self
    {
        if ('' === \trim($value)) {
            throw new \InvalidArgumentException('Value can not be blank or empty.');
        }

        return new self($value);
    }

    public function toString(): string
    {
        return $this->value;
    }
}

As another example, to allow instantiation of a DateOfBirth value object from an instance of DateTimeImmutable, I add a named constructor fromDateTimeImmutable() and a corresponding accessor toDateTimeImmutable() that provides access to the wrapped DateTimeImmutable instance.

<?php

declare(strict_types=1);

namespace Example;

final class DateOfBirth
{
    private function __construct(private readonly \DateTimeImmutable $value)
    {
    }

    public static function fromDateTimeImmutable(\DateTimeImmutable $value): self
    {
        return new self(\DateTimeImmutable::createFromFormat(
            '!Y-m-d',
            $value->format('Y-m-d'),
        ));
    }

    public function toDateTimeImmutable(): \DateTimeImmutable
    {
        return $this->value;
    }
}

Why bother with a toDateTimeImmutable() method when the field is already readonly and not make the field public instead?

From the perspective of a user, the internal value does not matter. The only thing that matters to a user is that they can create the value object from some primitive value and obtain a primitive representation of the value object when necessary. In the example above, the DateOfBirth value object could use a string as internal representation, three ints, or three strings. If I exposed the internal representation directly by making the field public, I would need to change every place that references the field(s). By encapsulating the primitive entirely, I am still open to changing the internal representation without making these changes anywhere else.

When it later turns out that I want to construct a DateOfBirth from a string value, I can easily add a constructor with the name fromString(), and if need be, a corresponding accessor toString() that returns an appropriate string representation.

<?php

declare(strict_types=1);

namespace Example;

final class DateOfBirth
{
    private function __construct(private readonly \DateTimeImmutable $value)
    {
    }

    // ...

    /**
     * @throws \InvalidArgumentException
     */
    public static function fromString(string $value)
    {
        try {
            $parsed = \DateTimeImmutable::createFromFormat(
                '!Y-m-d',
                $value,
            );
        } catch (\Exception) {
            throw new \InvalidArgumentException(\sprintf(
                'Value "%s" does not appear to be a valid date of birth.',
                $value,
            ));
        }

        // ...

        return new self($parsed);
    }

    public function toString(): string
    {
        return $this->value->format('Y-m-d');
    }
}

Value objects composing primitive values

I am still on the fence about whether a value object that composes more than one primitive value is a good idea. Generally, I prefer value objects to wrap a single primitive value or compose other value objects.

Why? For me, value objects serve two purposes:

  • traceability
  • enforcing invariants
  • expressive operations

For example, a legacy codebase might use an int or a string for representing the concept of an International Bank Account Number (IBAN). When I slowly replace usages of the int or string value with an Iban type, the concept becomes traceable throughout the codebase. Before the change, the only indication I was dealing with an IBAN was the variable name, perhaps even with a different spelling. I now have a concrete type and can easily find usages with an IDE or static code analysis. In other words, at this time, without enforcing invariants or providing expressive operations, introducing a value object already has the great benefit of traceability.

Step by step, I could enforce invariants using guard clauses and add methods that allow expressive operations.

If I composed multiple primitives into a value object and cared about these primitives, then I would not benefit from traceability and expressive operations anymore.

For example, I could compose two DateTimeImmutables into a Duration value object to model the duration of a specific event.

<?php

declare(strict_types=1);

namespace Example;

final class Duration
{
    private function __construct(
        public readonly \DateTimeImmutable $start,
        public readonly \DateTimeImmutable $end,
    ) {
    }

    /**
     * @throws \InvalidArgumentException
     */
    public static function create(
        \DateTimeImmutable $start,
        \DateTimeImmutable $end,
    ): self {
        // ...

        return new self(
            $start,
            $end,
        );
    }
}

Given proper enforcement of invariants (an event can not end before it starts), the Duration object works fine, and the concept of a Duration is now clearly more traceable than passing around two DateTimeImmutables. However, if I care about the concept of a Start and End, and when I want to distinguish between a Start, an End, and an ordinary instance of DateTimeImmutable, and want to provide expressive operations for these values, it will make more sense to introduce additional value objects.

<?php

declare(strict_types=1);

namespace Example;

final class Duration
{
    private function __construct(
        public readonly Start $start,
        public readonly End $end,
    ) {
    }

    /**
     * @throws \InvalidArgumentException
     */
    public static function create(
        Start $start,
        End $end,
    ): self {
        if (!$start->isBefore($end)) {
            // ...
        }

        return new self(
            $start,
            $end,
        );
    }
}

Value objects composing other value objects

When a value object composes other value objects, mark the default __construct() method as private and add a named constructor create().

I have found that this kind of value object only has a single way of construction. However, instead of using the default __construct() method, I prefer to use a named constructor create() for this kind of value object.

<?php

declare(strict_types=1);

namespace Example;

final class Attendee
{
    private function __construct(
        public readonly Name $name,
        public readonly DateOfBirth $dateOfBirth,
    ) {
    {
    }

    public static function create(
        Name $name,
        DateOfBirth $dateOfBirth,
    ): self {
        return new self(
            $name,
            $dateOfBirth,
        );
    }
}

Why create() and not fromNameAndDateOfBirth()? When I name the method fromNameAndDateOfBirth(), and later decide to compose additional value objects, it would be consistent to reflect the change by changing the method name. However, the method name would become longer and longer, and I would like to avoid that.

Consistency

Using named constructors for value objects, even when these value objects have only a single way of construction, provides a consistent naming scheme. Consistency is key to making it easier for developers to join, understand, and contribute to a project.

Do you find this article helpful?

Do you have feedback?

Do you need help with your PHP project?