The case for singleton objects, façades, and helper functions

Posted on by Matthias Noback

Last year I took several Scala courses on Coursera. It was an interesting experience and it has brought me a lot of new ideas. One of these is the idea of a singleton object (as opposed to a class). It has the following characteristics:

  • There is only one instance of it (hence it's called a "singleton", but isn't an implementation of the Singleton design pattern).
  • It doesn't need to be explicitly instantiated (it doesn't have the traditional static getInstance() method). In fact, an instance already exists when you first want to use it.
  • There doesn't have to be built-in protection against multiple instantiations (as there is and can only be one instance, by definition).

Converting this notion to PHP is impossible, but if it was possible, you could do something like this:

namespace {
    class Foo
    {
        public function bar(): void
        {
            // ...
        }
    }

    object Foo
    {
        public function baz(): void
        {
            // ...
        }
    }
}

Since object Foo would already be an object, its methods won't have to be marked static in order to be used anywhere.

// Use a local instance of class Foo:
$foo = new Foo();
$foo->bar();

// Use the globally available singleton object (requires no instantiation):
Foo->baz();

Inside object Foo you can also use $this if you like:

object Foo
{
    private $dependency;

    public function baz(): void
    {
        $this->dependency->doSomething();
    }
}

Of course such a collaborating object needs to be instantiated, for which we could define a parameter-less constructor (in Scala this would not require a constructor method, but the initialization could take place where you define the class properties itself). We could simulate the fact that it is impossible to instantiate object Foo yourself by making its constructor private:

object Foo
{
    private $dependency;

    private function __construct()
    {
        $this->dependency = // new ...();
    }

    public function baz(): void
    {
        $this->dependency->doSomething();
    }
}

An interesting characteristic of singleton objects is that they are companion objects, very much like friend objects. If you define both a regular class and a singleton object with the same name, they have access to each other's properties and methods, even the private ones:

class Foo
{
    public function bar(): void
    {
        Foo->privateHelperMethod();
    }
}

object Foo
{
    private function privateHelperMethod(): void
    {
        // ...
    }
}

The case for singleton objects

Regular classes offer the advantage of using dependency injection (DI) in order to change an object's behavior, without changing its code (what we mean by the Open/Closed Principle). As I think this setup is always preferable over hard-coded dependencies, I've ran into many cases where I wanted to offer both options, in order to improve the user experience.

For example, a couple of months ago I created a JSON serializer, based on some very simple design principles and assumptions (which would deserve a blog post on its own I think). What's important in the context of this article is that users of this library should be able to serialize or deserialize objects with one simple function call. It should work without any configuration, as long as their objects are in line with the assumptions of the library itself. Something like this:

$serializedObject = JsonSerializer::serialize($object);

$deserializedObject = JsonSerializer::deserialize(get_class($object), $serializedObject);

Very soon though, the main class started to require some dependencies (which could be injected as constructor arguments). This completely messed up the user experience, since instead of using public static methods, the client would have to instantiate a JsonSerializer and call a public (instance) method on it:

$serializer = new JsonSerializer(
    new DefaultTypeResolver(),
    // ...
);
$serializedObject = $serializer->serialize($object);

It would be a shame to create more than one instance of the JsonSerializer, as it would not be possible to reuse dependencies, or configuration. Soon we're back at remembering why we started using dependency injection in the first place. The client would be better off if it would get the JsonSerializer injected as a constructor argument, pre-configured, ready-to-use.

However, some solutions don't call for the overhead that comes with dependency injection, in particular the educational code I often write these days, which is usually trying to prove entirely different points than "use dependency injection".

To allow two different usages, one with static methods (to allow global usage), one with instance methods (to allow local, as well as potentially customized, usage), you could go for a setup like this:

class JsonSerializer
{
    private function __construct()
    {
        $this->typeResolver = new DefaultTypeResolver();

        // set up other dependencies
    }

    public static serialize($object): string
    {
        return (new self())->doSerialize($object);
    }

    private function doSerialize($object): string
    {
        // do the real work
    }
}

// Usage:
JsonSerializer::serialize($object);

This doesn't reuse dependencies and creates new instances of JsonSerializer all the time. It also doesn't allow for customization of behavior, so it doesn't yet offer the alternative option to users: setting up an instance of JsonSerializer themselves and injecting it where they need it. You could easily adapt the class to allow for this to happen though:

class JsonSerializer
{
    public function __construct(TypeResolver $typeResolver)
    {
        $this->typeResolver = $typeResolver;

        // set up other dependencies
    }

    private static createInstance(): JsonSerializer
    {
        return new self(
            new DefaultTypeResolver()
        );
    }

    public static serialize($object): string
    {
        return self::createInstance()->doSerialize($object);
    }

    public function doSerialize($object): string
    {
        // do the real work
    }
}

// Usage (with default dependencies):
JsonSerializer::serialize($object);

// Or (with custom dependencies):
$serializer = new JsonSerializer(
    new CustomTypeResolver()
);
$serializer->doSerialize($object);

Several things that start to feel wrong about this:

  1. Creating and configuring an instance of JsonSerializer has now become a responsibility of the class itself. Mixing these concerns makes the class feel a bit cluttered to me. Even more so, since they could be easily moved to a dedicated factory, which knows how to set up a JsonSerializer instance for you. Anyway, this is not how we/I usually write classes.
  2. We have serialize() and doSerialize() methods, which is not really nice, since one of them is just a proxy, and the other one is the real deal, but has do in front of it (which feels old-fashioned and really silly, it could be anything of course, but I'd rather have no pre-/postfix at all).

This is where I felt there really was a need for a singleton object! One that is pre-configured, always and globally available, e.g.

class JsonSerializer
{
    private $typeResolver;

    public function __construct(TypeResolver $typeResolver)
    {
        $this->typeResolver = $typeResolver;
    }

    public function serialize($object): string
    {
        // do the real work
    }
}

object JsonSerializer
{
    private $serializer;

    private function __construct()
    {
        $this->serializer = new JsonSerializer(
            new DefaultTypeResolver()
        );
    }

    public function serialize($object): string
    {
        return $this->serializer->serialize($object);
    }

    // ...
}

// Usage (DI-ready):
$serializer = new JsonSerializer(
    new CustomTypeResolver(),
    ...
);
$serializer->serialize($object);

// Or (stand-alone):
JsonSerializer->serialize($object);

Of course, that's not possible with PHP. We don't have singleton objects. The go-to solution for all things singleton is static, which takes away the option to work with a constructor:

class JsonSerializer
{
    private static $serializer;

    public static function serialize($object): string
    {
        if (self::$serializer === null) {
            self::$serializer = new JsonSerializer(
                new DefaultTypeResolver()
            );
        }

        return self::$serializer->serialize($object);
    }

    // ...
}

For everything there is only one instance in use, but this gives us really ugly and cluttered code. By the way, there are slight variations which may or may not make things better, e.g. using a local static variable for the serializer:

class JsonSerializer
{
    public static function serialize($object): string
    {
        static $serializer;

        $serializer = $serializer ?? new JsonSerializer(
            new DefaultTypeResolver()
        );

        return $serializer->serialize($object);
    }

    // ...
}

This design is "pretty nice", and it finally enables clients to use the JsonSerializer as global/static service or as an injected service. The only problem is that you can't have two classes with the same name in one namespace. So we should pick one of the following solutions:

  • Rename one of these classes (if that would make sense at all).
  • Move one class to a different namespace (the last part of which might be \Singleton).

There's one remaining question to me: what if we want to allow some clients to inject a JsonSerializer instance, and some clients to use the singleton? What if we also want to enforce the actual instance of JsonSerializer used by the singleton (the one that gets stored in its private static $serializer attribute), to be the exact same instance as the one clients get injected? It would mean maximum reuse and if we have a customized instance in one place, we allow the singleton implementation to use that exact same instance.

This sounds like a hard problem, but it's actually quite easy to solve. We could just add a setJsonSerializer() method to the singleton class:

class JsonSerializer
{
    private static $serializer;

    /**
     * @internal This method is not part of the client API
     */
    public static function setSerializer(JsonSerializer $serializer): void
    {
        self::$serializer = $serializer;
    }

    public static function serialize($object): string
    {
        if (self::$serializer === null) {
            self::$serializer = new JsonSerializer(
                new DefaultTypeResolver()
            );
        }

        return self::$serializer->serialize($object);
    }

    // ...
}

Façades

Maybe you noticed it already, but with this approach we're getting really close to the concept of a façade, as you may know it from working with Laravel.

The only difference is that, instead of setting an instance of one service by calling some static set[...]() method on the singleton class, a Laravel application sets a service locator on the singleton class (which is called a "façade" in Laravel jargon). Equivalent code for this functionality would look something like this:

class JsonSerializer
{
    private static $serviceLocator;

    public static function setServiceLocator(ServiceLocator $serviceLocator): void
    {
        self::$serviceLocator = $serviceLocator;
    }

    public static function serialize($object): string
    {
        return self::$serviceLocator->get('json_serializer')->serialize($object);
    }

    public static function deserialize(string $type, string $data): object
    {
        return self::$serviceLocator->get('json_serializer')->serialize($type, $data);
    }

    // ...
}

Of course, Laravel has a base class which contains the bulk of the "resolve" logic that would otherwise have to be duplicated over and over again.

Helper functions

Laravel goes even further with this concept and introduces helper functions, which use façades behind the scenes, yet make using complicated services even simpler. In our case, these helper functions could look something like this:

function json_serialize($object): string
{
    return JsonSerializer::serialize($object);
}

function json_deserialize(string $type, string $data): object
{
    return JsonSerializer::deserialize($type, $data);
}

Though they are mostly simple proxies, these functions are great in hiding complicated code and dependencies and providing a "drop-in" service wherever you are in the code base.

Conclusion

I'm not advocating the use of façades and helper functions per se. I assume they may have good use in specific parts of your application (just as the Laravel documentation states, by the way). My point is that, as long as you build into your libraries both options, you can offer a great developer experience, providing an out-of-the-box working solution of a useful service, which can still be used in a project that uses dependency injection everywhere.

PHP Design singleton façades Laravel object design
Comments
This website uses MailComments: you can send your comments to this post by email. Read more about MailComments, including suggestions for writing your comments (in HTML or Markdown).
Eugene Leonovich

Thanks for the nice article, it led me to do some refactoring on my msgpack library :)> We have serialize() and doSerialize() methods, which is not really nice, since one of them is just a proxy, and the other one is the real deal, but has do in front of itFor the record, you can workaround this with the magic __call() method ( https://3v4l.org/Ojjsu ), which, of course, doesn't solve the proxy issue but probably introduces new ones.

Sebastiaan Stok

The Singleton pattern is mainly usable in the absolute outer shell of the application, I can imagine in Scala (which is mostly used on multi process systems) this is actually helpful to prevent creating more then one instance of a System that by design cannot be created more then once, but this system will life in the out shell were no inner layers would know about this Singleton object.

Other then that it's a recipe for trouble.

Another simple way for facades in Symfony would be to access to the Container statically as done in https://github.com/theofidr.... It avoids to have to write facades manually which is tedious and boring.For the helper functions, I would recommend you to add a complete example:

  • show the file path
  • add a namespace (don't encourage functions in a global scope as Laravel does please)
  • how to use a namespaced function (most PHPIDE helps but some people don't know you need to do use function Foo\bar;)
  • how to register the file to ComposerNice article :)
mrclay

I highly recommend all these videos: https://youtu.be/-FRm3VPhse... He convinced me to ditch this pattern.

Matthias Noback

Seems like an interesting video, thanks.

J7mbo

I have had similar considerations as you did in your Singleton example. You write everything SOLID, then your end user has to wire them all together just to use your class.What I did was provide a factory, which takes optional constructor parameters to override functionality; and if you don't pass them in they are instantiated for the user instead. So you still provide optional extension points, but by default all the user needs to do is call create() and the default implementation is constructed and provided (which is almost the same as "one simple function call", and is the perfect use case for a factory).So no having to argue that the often-considered (in PHP-land) singleton anti-pattern has a use case - this is only one way to avoid that.

Matthias Noback

For sure! I've seen this in the Doctrine ORM for example, where complicated object construction can be replaced with a single call to a factory method. You could have builders for that too. However, I think that singleton objects wouldn't be a good idea for complicated library setups like Doctrine ORM, since you will never on-the-fly need an ORM ;)