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 Comments

Docker build patterns

Posted on by Matthias Noback

The "builder pattern"

As a programmer you may know the Gang-of-Four Builder design pattern. The Docker builder pattern has nothing to do with that. The builder pattern describes the setup that many people use to build a container. It involves two Docker images:

  1. a "build" image with all the build tools installed, capable of creating production-ready application files.
  2. a "service" image capable of running the application.

Sharing files between containers

At some point the production-ready application files need to be copied from the build container to the host machine. There are several options for that.

1. Using docker cp

This is what the Dockerfile for the build image roughly looks like:

# in Dockerfile.build

# take a useful base image
FROM ...

# install build tools
RUN install build-tools

# create a /target directory for the executable
RUN mkdir /target

# copy the source code from the build context to the working directory
COPY source/ .

# build the executable
RUN build --from source/ --to /target/executable

To build the executables, simply build the image:

docker build \
    -t build \              # tag the image as "build"
    -f Dockerfile.build \   # use Dockerfile.build
    .                       # use current directory as build context

In order to be able to reach in and grab the executable, you should first create a container (not a running one) based on the given image:

docker create \
    --name build \     # name the container "build"
    build              # use the "build" image

You can now copy the executable file to your host machine using docker cp:

docker cp build:/target/executable ./executable

2. Using bind-mount volumes

I don't think making the compile step part of the build process of the container is good design. I like container images to be reusable. In the previous example, when the source files are modified, you need to rebuild the build image itself. But I'd just like to run the same build image again.

This means that the compile step should instead be moved to the ENTRYPOINT or CMD instruction. And that the source/ files shouldn't be part of the build context, but mounted as a bind-mount volume inside the running build container:

# in Dockerfile.build
FROM ...
RUN install build-tools

ENTRYPOINT build --from /project/source/ --to /project/target/executable

This way, we should first build the build image, then run it:

# same build process
docker build \
    -t build \
    -f Dockerfile.build \
    .

# now we *run* the container
docker run \
    --name build \
    --rm \                     # remove the container after running it
    -v `pwd`:/project \        # bind-mount the entire project directory
    build

Every time you run the build container it will compile the files in /project/source/ and produce a new executable in /project/target/. Since /project is a bind-mount volume, the executable file is automatically available on the host machine in target/ - there's no need to explicitly copy it from the container.

Once the application files are on the host machine, it will be easy to copy them to the service image, since that is done using a regular COPY instruction.

The "multi-stage build"

A feature that has just been introduced to Docker is the "multi-stage build". It aims to solve the issue that for the above build process you need two Dockerfiles, and a (Bash) script to coordinate the build process, and get the files where they need to be, with a short detour via the host filesystem.

With a multi-stage build (see Alex Elis's introductory article on this feature), you can describe the build process in one file:

# in Dockerfile

# these are still the same instructions as before
FROM ...
RUN install build-tools
RUN mkdir /target
RUN build --from /source/ --to /target/executable

# another FROM; this defines the actual service container
FROM ...
COPY --from=0 /target/executable .
CMD ["./executable"]

There is only one image to be built. The resulting image will be the one defined last. It will contain the executable copied from the first, intermediate "build" image (which will be disposed afterwards).

Note that this requires the source files to be inside the build context. Also note that the build image itself is not reusable; you can't run it again and again after you've made changes to the code; you have to build the image again. Since Docker will cache previously built image layers, this should still be fast, but it's something to be aware of.

Pipes & filters

I recently saw this question passing by on Twitter:

People suggested to use bind-mount volumes, as described above. Nobody suggested docker cp. But the question prompted me to think of some other solution for getting generated files out of a container: why not stream the file to stdout? It has several major advantages:

  1. The data doesn't have to end up in a file anymore, only later to be moved/deleted anyway - it can stay in memory (which offers fast access).
  2. Using stdout allows you to send the output directly to some other process, using the pipe operator (|). Other processes may modify the output, then do the same thing, or store the final result in a file (inside the service image for example).
  3. The exact location of files becomes irrelevant. There's no coupling through the filesystem if you only use stdin and stdout. The build container wouldn't have to put its files in /target, the build script wouldn't have to look in /target too. They just pass along data.

In case you want to stream multiple files between containers, I think good-old tar is a very good option.

Take the following Dockerfile for example, which creates an "executable", then wraps it in a tar file which it streams to stdout:

FROM ubuntu
RUN mkdir /target
RUN echo "I am an executable" > /target/executable
RUN echo "I am a supporting file" > /target/supporting-file
ENTRYPOINT tar --create /target

To build this image, run:

docker build -t build -f docker/build/Dockerfile ./

Now run a container using the build image:

docker run --rm -v `pwd`:/project --name build build

The archive generated by tar will be sent to stdout. It can then be piped into another process, like tar itself, to extract the files again:

docker run --rm -v `pwd`:/project --name build build \
    | tar --extract --verbose

If you want another container to accept an archive, pipe it in through stdin (create the container in interactive mode):

docker run --rm -v `pwd`:/project --name build build \
    | docker run -i [...]

Conclusion

We discussed several patterns for building Docker images. I prefer separate build files (instead of a multi-stage build with one Dockerfile). And as an alternative for writing files to a bind-mount volume, I really like the option to make the build image stream a tar archive.

I hope there was something useful for you in here. If you find anything that can be improved/added, please let me know!

Docker Docker Comments

Making money with open source, etc.

Posted on by Matthias Noback

So, here's a bit of a personal blog post for once.

Symfony trademark policy

I saw this tweet:

Meaning: "Following a formal notice, I removed the tutorials that are related to Symfony from the Site. There will be no future videos on the Framework."

I got mad. I had heard of other people having trouble with Sensiolabs' trademark policy and I once had difficulty getting permission to use "Symfony" in my "Hexagonal Symfony training tour" (which became the "Hexagonal Architecture training tour"). So I tweeted about it:

I thought it would be good to speak up. It's a really scary thing to do anyway. It's not in my nature to voice criticism publicly like this. I know that doing so will make things harder, and close certain doors.

Regret

However, not long after the initial tweet by @grafikart_fr, Fabien Potencier (lead maintainer of Symfony) tweeted:

And also:

This made me feel really bad about myself. I don't want to play a part in someone's sad day. Of course.

As you know I’ve always been a big fan of Symfony and Fabien's work on it. I have learned a tremendous amount of things from using it, documenting it and creating open source tools for it. So: dear Fabien, I'm sorry for my act of public shaming. It isn't good style.

Here's the lesson I'm trying hard to learn in my life: voicing criticism isn't equal to saying that everything you are, do or create sucks. In fact, my girlfriend warns me about this when she gives me feedback and I "cringe": "I'm not saying that your personality sucks, I love you, don't forget that." I think that this somehow applies in this situation too. Fabien, please don't feel rejected.

Earning money with open source

An interesting perspective on this topic was brought up Jordi Boggiano:

It was interesting because I didn't consider it to be relevant at first. It has never occurred to me that Fabien didn't earn enough money to justify all his open source work. Well, maybe he has, but that doesn't matter. Working on open source and earning money with that is something that's rather hard to accomplish. Examples of these are Jordi's own experiences with maintaining Composer. And I can very much relate to this too. Many people, including rich people, and many companies, including very successful ones, assume they can demand a lot of free work from open source maintainers. Starting to ask money for essentially the same thing, is often met by outrage (or silent neglect), so it's really hard to run an "open source" business.

For the past few months I've been trying to pick up what you could call my "master plan". I hope to organize workshops and do freelance programming jobs, thereby financing all the unpaid "open source" work that I love to do, like blogging, speaking at meetups and conference, and open source programming. I've learned two things already:

  1. You can't take it for granted that people will pay you for work you've always done for free (like book writing, or organizing workshops).
  2. You need to sell yourself and your products. Marketing is very important.

Marketing is also very annoying, and personally I'm just way too modest to keep throwing commercial messages at my audience. It's quite a personal victory to even show a banner for my new book on my blog. I know though, that in order to do all this free work, I need to sell stuff too, so I hereby assume that you'll understand that I'll do a little bit more work in the future to properly market my products.

Conclusion

What I'm saying is that I totally get that being lead maintainer of an open source framework (or package manager, etc.), and trying to compensate somewhat for everything you've given away for free, can be very difficult. And I totally understand that the Symfony trademark needs to be protected too. The Symfony brand has been carefully cultivated. I just hope that Sensiolabs will be more careful about dismissing some - I think - proper uses of the Symfony trademark and logo. I assume they will, based on what Fabien already tweeted tonight:

PHP Symfony Symfony open source Comments