thePHP.cc Logo Deutsch Contact
How do you name constructors?

How do you name constructors?

Despite a greatly improved type system in versions 7 and 8, PHP does not support constructor overloading. If an object has to be created in different ways, so-called named constructors are used. Are there any best practices for naming them?

Every last Friday of the month we have Office Hours, which is an open video conference where we invite you to casually talk to us about professional software development with PHP and related technologies. I'm happy to observe our Office Hours gradually turning into a meeting point for the German PHP community.

Last week during Office Hours, a discussion arose about naming Named Constructors. I gave some examples of how I name Named Constructors in my projects. Since I received questions about this via email over the weekend, I've written down my thoughts here.

from() Methods

A widespread way of naming is from() , or variations derived from it. variations. Since named constructors are traditionally mainly for value objects, this approach seems like a good idea, because the method name makes clear, that an object is created from other objects or from scalar data.

Up to version 7, PHP was a less type-safe language, but now you can also write type-safe code these days. So where you used to need method names like fromInt(...) or fromString(...) , you can now achieve more type safety by using a scalar type declaration, keeping the same level of readability: fromString($value) then becomes from(string $value) .

But this again raises the problem that we cannot overload the from() method. So what do we do if we want to be able to create an object from different types? If we're honest, in a type-safe world, there aren't that many good reasons to do it anymore. Let's take the following example:

class   SomeValueObject
{
     public   static   function   fromInt ( int   $value ) :   self
     {
         ...
     }
 
     public   static   function   fromString ( string   $value ) :   self
     {
         ...
     }
}

If we alternatively offer only one factory method

class   SomeValueObject
{
     public   static   function   from ( string   $value ) :   self
     {
         ...
     }
}

then, of course, one can argue that the caller is now responsible for the type cast and that the error handling that may be required for this will result in duplicated code. After all, we write

$object   =   SomeValueObject :: from ( (string)   $integer ) ;

wherever the value object must be created. However: in an increasingly type-safe world, an int is already an int , because we can give our parameter object (which is often also the request object) methods that return certain types:

final   class   ProcessFormRequest
{
     ...
 
     public   function   parameterAsString ( string   $name ) :   string
     {
         return   (string)   $this -> parameter ( $name ) ;
     }
 
     public   function   parameterAsInt ( string   $name ) :   int
     {
         return   (int)   $this -> parameter ( $name ) ;
     }
 
     ...
}

If we view a variable as a dynamic type, as was the case in classic PHP development, then the problem of type conversion occurs relatively late, as in this example, when the value object is created. This is because we have passed a value with an unspecified type pretty far into the application.

More Type Safety

If we think instead rather of typed variables, then we should specify the type of a parameter as early as possible. This is where the ProcessFormRequest shown above helps us, which we can, for example, design as a wrapper around (or adapter for) the Request object of the framework we are using:

final   class   ProcessFormRequest
{
     private   HttpRequestOfYourFramework   $httpRequest ;
 
     ...
 
     public   function   parameterAsString ( string   $name ) :   string
     {
         return   (string)   $this -> httpRequest -> get ( $name ) ;
     }
 
     ...
}

The problem of having to create a value object from different scalar types deep within the application then no longer arises, because type conversion has moved much closer to the "front line" of the program.

In this sense, method names like create() or make() , which are commonly used in common frameworks and projects, are probably just as good as from() . However, this mainly applies to classic value objects. We discuss services and infrastructure objects below.

Even Smaller Objects

Another frequently cited use case for named constructors is to generate an HTTP request in two ways, once from parameters and once from the superglobal variables. More or less every framework does this to decouple the application from the global system state in the form of the superglobal variables:

class   HttpRequest
{
     public   static   function   fromSuperglobals ( ) :   self
     {
         ...
     }
 
     public   static   function   from ( ... ) :   self
     {
         ...
     }
}

I was happy and satisfied with such a solution for a long time, until I realized that my request object is still too big, because it is responsible for two different things, testing and production.

One can argue whether writing code specifically for testing is a good idea. However, I would argue that it is an essential task of the framework to promote and facilitate testing of the application. Whether this should lead to such a close integration of test code and production code as is the case in Laravel, for example, is another question that I won't go into here today.

If - back in our example - we split the real and the "fake" HTTP request into two objects, then we have two smaller and more specific objects, each serving only one purpose. This is a better design.

For example, in my HTTP framework for content delivery, there are two separate classes for this purpose that implement a common interface, namely

final   class   RealHttpRequest   implements   HttpRequest
{
     public   static   function   fromSuperglobals ( ) :   self
     {
         ...    
     }
 
     ...
}

and

final   class   FakeHttpRequest   implements   HttpRequest
{
     private   string   $method ;
     private   string   $path ;
     private   array   $formData ;
 
     public   static   function   get ( string   $path ) :   self
     {
         return   new   self ( 'GET' ,   $path ) ;
     }
 
     public   static   function   head ( string   $path ) :   self
     {
         return   new   self ( 'HEAD' ,   $path ) ;
     }
 
     public   static   function   post (
         string   $path ,
         array   $formData
     ) :   self
     {
         return   new   self ( 'POST' ,   $path ,   $formData ) ;
     }
 
     ...
}

The "fake" HTTP request provides several explicit factory methods to generate the appropriate request as needed, for example:

$request   =   FakeHttpRequest :: get ( '/the-url-path' ) ;

The bootstrap file of the application, on the other hand, says:

$request   =   RealHttpRequest :: fromSuperglobals ( ) ;

We definitely benefit from better readability when we offer multiple factory methods. Here, however, it is no longer a question of what data we want to generate the request from, but what kind of request we want to generate.

Different Types of Creation

Even though we have already accepted that the creation of a value object from different scalar data is no longer a common use case per se, there is still often the requirement to create an object in different ways. Whenever serialization or persistence is in play, we need to be able to create an object repeatedly in its domain-oriented lifecycle after the initial creation for technical reasons, namely when it is loaded from persistence.

Let's take a UUID as an example. The domain-oriented generation of a UUID roughly corresponds to rolling a random number. We name the constructor accordingly:

final   class   UUID
{
     ...
 
     public   static   function   generate ( ) :   self
     {
         ...
     }
 
     ...
}

If we want to recreate an object representing this UUID later, we use:

final   class   UUID
{
     ...
 
     public   static   function   from ( string   $uuid ) :   self
     {
         ...
     }
 
     ...
}

Another example would be an object that represents an event. Initially, the event is created from the existing data:

class   EmailVerificationRequestedEvent   implements   Event
{
     ...
 
     public   static   function   from (
         AccountId   $accountId ,
         EmailAddress   $emailAddress
     ) :   self
     {
         ...
     }
 
     ...
}

If the event is later loaded from an event store where it was stored serialized as JSON, we use another constructor:

class   EmailVerificationRequestedEvent   implements   Event
{
     ...
 
     public   static   function   fromJson ( Json   $json ) :   self
     {
         ...
     }
 
     ...
}

The Json class generically encapsulates a Json document and is responsible for both json_decode() and clean error handling when accessing data from the document.

Of course, in this example you could also implement a separate data mapper that creates the event object from the JSON data. But I think it's better not to have to edit the mapper and the event object itself in parallel, but to have the mapping code directly in the fromJson() method and thus in the object itself. (For entities or even aggregates my opinion would be completely different!).

Infrastructure

In some of my projects I have meanwhile switched to writing a named constructor for all objects, even though this may be technically superfluous. To create an object that is supplied with collaborators via the constructor using dependency injection, I use the method name collaboratingWith() :

$dispatcher   =   SynchronousEventDispatcher :: collaboratingWith (
     $eventHandlerLocator
) ;

An event dispatcher is not created from a locator, but works together with it. The from() would definitely no longer be a suitable name here, also make() or create() would not be very appropriate in my opinion.

As another example we have here the factory of a framework, which has to cooperate with the application factory, because only the latter can can create the objects written by the application developer:

final   class   PeregrineFactory   implements   Factory
{
     ...
 
     public   static   function   collaboratingWith (
         ApplicationFactory   $applicationFactory
     ) :   self
     {
         return   new   self ( $applicationFactory ) ;
     }
 
     ...
}

I like that the naming suggests that the objects are collaborating at eye level , so to speak. I also feel that the naming distinction between from() and collaboratingWith() helps me to better distribute the responsibilities between the individual objects, respectively to make the objects more concise.

Exceptions

I also use named constructors or factory methods consistently for exceptions. One must always remember that PHP does not write the line in the stacktrace that contains the throw , but the number of the line that contains the new . Of course, you will only notice that there is a difference if you use named constructors, because otherwise the new and the throw are usually on the same line. But if you know this and then look into the next frame of the stacktrace to find out where an exception was thrown, then this is no problem.

So, to create an exception, we can also use a static factory method or a named constructor:

throw   LongbowException :: noCommandSpecifiedFor ( $commandHandler ) ;

I find this makes the code compact and very readable, plus it's even a little easier to find specific call locations and. Most importantly, I have all exception messages concentrated in one place, namely in the exception class itself, and can much more easily ensure consistency and uniformity there than if the texts are scattered across the codebase.

By the way, exceptions are again a very good example of objects that are created from different data:

class   LongbowException   extends   RuntimeException
{
     public   static   function   noCommandSpecifiedFor (
         string   $commandHandler
     ) :   self
     {
         return   new   self ( sprintf ( '...' ,   $commandHandler ) ) ;
     }
 
     public   static   function   factoryHasNoMethod (
         object   $factory ,  
         string   $shortName
     ) :   self
     {
         ...
     }
 
     public   static   function   noEventSpecifiedFor (
         string   $eventHandler
     ) :   self
     {
         ...
     }
}

However, nobody would think of calling the methods from() here, would they?

Parameterless Constructors

There remain a few edge cases where finding "good" names is a problem, for example constructors which have no parameters:

$factory   =   Factory :: from ( ) ;

That does not look nice, does it? I have solved that problem like this:

class   Factory   implements   ApplicationFactory
{
     ...
 
     public   static   function   withDefaults ( ) :   self
     {
         return   new   self (
             Configuration :: from (
                 Environment :: fromSuperglobals ( )
             )
         ) ;
     }
 
     ...
}

Here the name emphasizes that the factory is initialized with default values. Of course, I can do this differently if the use case requires it:

class   Factory   implements   ApplicationFactory
{
     ...
 
     public   static   function   with (
         Configuration   $configuration
     ) :   self
     {
         ...
     }
 
     ...
}

Especially for top-level calls, for example where you initialize the application in the bootstrap file, I find parameterless constructors great. For objects in the middle of the application I usually see it differently.

Constructors and Factory Methods

To answer the question "What is your constructor called?", you have to ask what purpose the constructor serves. Especially in PHP it is not the case that the constructor creates the object, it is just an interceptor method that is called automatically after the object has already been created (otherwise $this in the constructor would not work). So a constructor initializes, configures or parameterizes an object, depending on what you want to call it.

A static factory method looks the same on the outside as a named constructor, but serves the purpose of selecting an implementation without the client needing to know its concrete class name. The transition between the two concepts is smooth and there is some overlap. When I call Language::en() , it matters less whether I get back a Language instance or an instance of English , which is a subclass of Language . Is Language::en() now a named constructor or a factory method?

In fact, it doesn't really matter, we in particular don't care if we evolve from one to the other in the future. This is exactly one big advantage of the whole thing. If we "lock away" the new statements by relegating them to static methods, then we've stripped our code a bit further from implementation details (here: which classes exist or what are their names). This is forward-compatible because it allows us to change the structure of our code in the future by introducing or removing subclasses, and without having to modify the calling code.

All in all, it may not be such a good idea to enforce a universal "one size fits all" rule for naming named constructors, especially since we don't really separate them from factory methods in everyday consideration. It seems much more sensible to choose method names that are more differentiated and technically meaningful. In this context, we should always remember that even the designation "constructor" is misleading, because nothing is created here, but only an object is initialized.

Addendum

It was just brought to my attention that Andreas Möller , one of the visitors to our Office Hours, had already taken the discussion on Friday as an opportunity to write this blogpost . Thanks for your thoughts on the topic, Andreas.