Singletons And Shared Instances

Every once in a while, when discussing software design with fellow developers, the topic of Singletons comes up, especially in the context of WordPress development. Often times, I try to explain why these should be avoided, even though they are recognized as being a “standard pattern”.

In this article, I try to go over the reasoning of why the Singleton should never be used in your code, and what alternative approaches you can use instead to solve the same problems.

What is a Singleton?

The Singleton is a software design pattern that has been described in the book Design Patterns: Elements of Reusable Object-Oriented Software by “the Gang of Four“, the reference that effectively started the talk about Design Patterns as a software engineering tool.

The basic principle is that you want to ensure there can only ever be a single instance of a given class, and you provide a global point of access for it.

This is really easy to explain and understand, and for most people, the Singleton is the entry drug into the world of Design Patterns, making it the single most popular pattern.

So, given its popularity, and given the fact it is part of the reference book and was amongst the first patterns to be described and standardized, how comes that some developers decry it as an “anti-pattern”? Can it really be that bad?

Yes.

Yes, it can.

But Singletons are useful and necessary!

I notice that a lot of people confuse two related concepts, and when they say they need a Singleton, they actually mean to say they need to share a same instance of an object across different instantiations. In general, when you instantiate a class, you create a new instance of that class. But for some objects, you want to always use the same, shared instance of that object, no matter where it is being used.

But the Singleton is not the proper solution for this.

The confusion is caused by the fact that a Singleton actually combines two responsibilities in one object. Imagine having a Singleton that is being used to establish a connection to the database. Let’s call it (very creatively) DatabaseConnection. The Singleton now has the following two main responsibilities:

  1. Manage the database connection.
  2. Manage the instantiations of the DatabaseConnection.

Responsibility 2. is what makes people pick the Singleton, but this task should actually be the responsibility of a different object.

The problem with a Singleton is not that it is shared, but that it enforces its own instantiation. Click To Tweet

There’s nothing wrong with using a shared instance. But the object you want to share is not the place to put that restriction into.

I’ll show some alternative approaches of how to handle this later in the article. But first, I want to discuss what types of problems the Singleton will cause.

Problems with the Singleton Pattern

Singleton vs SOLID

First of all, and this might seem to be more of a theoretical and puristic issue than a real problem at first, the Singleton design pattern breaks most of the SOLID principles.

  • stands for “Single Responsibility Principle“. It should be obvious that the Singleton pattern goes against this principle, as already described above.
  • O stands for “Open/Closed Principle“, meaning that objects should be open for extension but closed for modification. The Singleton breaks this principle because it controls the point of access, and will only ever return itself, not an extension.
  • L stands for “Liskov Substitution Principle“, meaning that objects should be replaceable by instances of their subtypes without needing changes to the consuming code. This obviously does not work out for a Singleton, as the simple fact of having multiple differing versions of an object causes it to not be a Singleton anymore.
  • I stands for “Interface Segregation Principle”, meaning that you should prefer many client-specific interfaces over one general-purpose one. This is the only principle that the Singleton does not directly break, but only because the Singleton does not allow for an interface to begin with.
  • D stands for “Dependency Inversion Principle“, stating that you should depend on abstractions, not concretions. Once again, the Singleton breaks this principle, because you can only ever depend on the very concrete Singleton instance.

The Singleton pattern breaks four out of five SOLID principles! It would probably even want to break the fifth as well, if only it was able to have interfaces in the first place…

The Singleton pattern breaks four out of five SOLID principles! Click To Tweet

It is easy to say that your code does not work or break because of some random theoretical principles. And although my personal experience has shown time and time again that these principles are amongst the most valuable and enduring guidelines you can adhere to in terms of software design, I acknowledge that just stating this as a “fact” will probably not convince a lot of people. We should look at the impact the Singleton pattern has for your immediate practical reality.

Singleton in practical use

Here are the more practical drawbacks and issues that a Singleton brings about:

  • You cannot have arguments passed to/injected into the constructor. As the constructor is only actually executed by the first call that hits the Singleton, and you cannot know in advance, which of the consuming code will be the first one to hit the Singleton, all consuming code would need to have the exact same set of arguments to pass to the constructor, which is both not practically feasible in most cases, and rather pointless to begin with. As a result, the Singleton renders the main mechanism of instantiation in OOP languages useless.
  • You cannot mock the Singleton away when testing other components that make use of it. This makes proper unit tests practically impossible, because you never have a clear isolation of the code you want to test. This problem is not even caused by the actual logic you want to test, but rather by the arbitrary instantiation restriction you wrapped it into.
  • As the Singleton is a globally accessible construct that is always shared across your entire codebase, it defeats any efforts for encapsulation, causing the exact same problems as global variables do. This means that whatever you try to do to get the Singleton isolated in an encapsulated part of your code, any other external code can cause side effects and produce bugs through this Singleton. Without proper encapsulation, the very principles of object-oriented programming are diluted.
  • If you ever have your site/app grow to the point where your DatabaseConnection Singleton suddenly needs to allow for a second, different database connection nevertheless, you’re in a world of trouble. Your very architecture needs to be revised, and will probably cause large portions of your code to need a complete rewrite.
  • All of the tests you write that make use of your Singleton in a direct or indirect way cannot be properly reset from one test to the next. They always carry over state through the Singleton, which can produce weird behavior where your tests depend on the order they are run in or obfuscate actual bugs because of left-over state.
  • They only enforce their singular instantation for the current process space that is valid for the static scope. This means that you’ll run into issues with multi-threading, with multiple processes or with distributed execution. This should hint to the possibility that they are just a faulty concept that simply breaks down as soon as you work in a distributed system.

Alternatives to using a Singleton

I don’t want to be the person that decries everything as bad but then fails to provide viable alternatives or preferable approaches. Although I do think that you need to consider your overall architectural design to decide how best to avoid Singletons in the first place, I can think of a few common use cases in WordPress where the Singleton can easily be replaced with a mechanism that meets all requirements while avoiding most or all of the drawbacks. But before describing these, I’d like to point out why all of them are only a compromise.

There is an actual “ideal structure” for dealing with the construction of an app. The theoretical best case is one single instantiation call in the bootstrap code that builds the entire dependency tree of the complete app from the top down. This would work along the lines of this example:

  1. Instantiate App (needs ConfigDatabase, Controller).
  2. Instantiate Config to inject into App.
  3. Instantiate Database to inject into App.
  4. Instantiate Controller to inject into App (needs Router, Views).
  5. Instantiate Router to inject into Controller (needs HTTPMiddleware).

With a single call, the entire application stack would be built from the top down, injecting the dependencies as needed. The goals with such an approach:

  • Every object has an exact list of dependencies it needs, and should only make use of these dependencies. When something breaks, you can easily isolate the responsible code.
  • No tight coupling between any of the objects, all objects rely on interfaces only while using whatever implementations will be injected.
  • No global state. Every individual subtree in the above hierarchy would be properly isolated from the rest, without the developers being able to introduce bugs into module B while making changes to module A.
The ideal app construction is a single instantiation call that builds the entire dependency tree. Click To Tweet

However, as good as this might sound, it is just not possible with WordPress code. WordPress does not provide any centralized container or injection mechanism, and each of the plugins/themes are loaded in an isolated way.

Keep this in mind while we discuss the possible approaches below. The ideal solution, having the entire WordPress stack be instantiated through one centralized injector, is currently out of reach as it would need to be supported by the WordPress Core. All of the approaches below will share some of the same drawbacks in differing degrees, like the fact that they hide dependencies by accessing them directly from within the logic instead of having them be injected.

Singleton code

Here is the example code for the Singleton approach that we will use as a base to compare the other approaches to:

// Singleton.
final class DatabaseConnection {

   private static $instance;

   private function __construct() {}

   // Call to get a hold of the one true instance.
   public static function get_instance() {
      if ( ! isset( self::$instance ) ) {
         self::$instance = new self();
      }

      return self::$instance;
   }

   // Actual logic code mixed in with the instantiation mechanism.
   public function query( ...$args ) {
      // Execute query and return results.
   }
}

// Consuming code.
$database = DatabaseConnection::get_instance();
$result   = $database->query( $query );

I didn’t include all of the implementation details that Singletons are often loaded with, as they don’t add anything to the theoretical discussion.

Using Factories

In most cases, the easiest way to defuse your problems with Singletons is to use the Factory pattern instead. A Factory is an object that has the instantiation of objects as its single responsibility. Instead of having a DatabaseConnectionManager that takes care of its own instantiation via a get_instance() method, you’ll have a DatabaseConnectionFactory that can hand out instances of a DatabaseConnection object. In general, the Factory would always hand out new instances of a requested object. But it can decide, based on the requested object and the context, whether to hand out new instances or to always share one instance only.

Regarding the naming, you might think that this looks more like Java code than PHP code, so feel free to stray away from a too strict (and thus lazy) naming convention and name the Factory in a more straightforward and creative way.

Example code for the Factory approach:

// Factory.
final class Database {

   public function get_connection(): DatabaseConnection {
      static $connection = null;

      if ( null === $connection ) {
         // You can have arbitrary logic in here to decide what
         // implementation to use.
         $connection = new MySQLDatabaseConnection();
      }

      return $connection;
   }
}

// Interface, so that we can deal with multiple implementations and properly
// mock for testing.
interface DatabaseConnection {

   public function query( ...$args );
}

// The implementation we're currently using.
final class MySQLDatabaseConnection implements DatabaseConnection {

   public function query( ...$args ) {
      // Execute query and return results.
   }
}

// Consuming code.
$database = ( new Database )->get_connection();
$result   = $database->query( $query );

You can see that the consuming code is not more difficult or verbose to write, there’s only a slight nuance to it. We’ve opted to call the Factory Database instead of DatabaseConnectionFactory, as this is part of the API we’re providing, and we always should strive to hit the right balance between logical precision and elegant consiceness.

The Factory version above gets rid of pretty much all of the drawbacks we’ve been identifying before, with a small exception:

  • Although we’ve removed the tight coupling to the DatabaseConnection object, we’ve introduced a tight coupling to the new Factory instead. This is not problematic, as the Factory is a pure abstraction, and there’s a very low probability that we need to move away from the actual concept of “instantiation” at one point. If that should happen, the entire paradigm of OOP probably needs to be revised.

You’ll probably start wondering now about the fact that we are not able to enforce a single instantiation anymore. Although we always hand out a shared instance of a DatabaseConnection implementation, anyone can still just do a new MySQLDatabaseConnection() and thus get access to an additional instance. Yes, that is correct, and that is actually one of the goals of getting away from the Singleton pattern. It is good to have conventions about always sharing an instance of an object. But enforcing something like this does not provide additional real-world benefits, while making basic requirements like unit testing impossible.

Using Static Proxies

A Static Proxy is another design pattern that can be used to get rid of Singletons. It introduces more tight coupling than the Factory does, but this coupling is still only tied to an abstraction, not a concrete implementation. The basic principle is that you have a static mapping of the interface, and these static calls are being transparently forwarded to a concrete implementation behind the scenes. As such, there’s no direct coupling to the actual implementation, and the Static Proxy is free to decide in what ways to pick the actual implementation to use.

// Static Proxy.
final class Database {

   public static function get_connection(): DatabaseConnection {
      static $connection = null;

      if ( null === $connection ) {
         // You can have arbitrary logic in here to decide what
         // implementation to use.
         $connection = new MySQLDatabaseConnection();
      }

      return $connection;
   }

   public static function query( ...$args ) {
      // Forward call to actual implementation.
      self::get_connection()->query( ...$args );
   }
}

// Interface, so that we can deal with multiple implementations and properly
// mock for testing.
interface DatabaseConnection {

   public function query( ...$args );
}

// The implementation we're currently using.
final class MySQLDatabaseConnection implements DatabaseConnection {

   public function query( ...$args ) {
      // Execute query and return results.
   }
}

// Consuming code.
$result = Database::query( $query );

As we can see in the consuming code example, the Static Proxy creates a very short and clear API, with the drawback being that it tightly couples the code to its class signature. If used in the right places, this should not be a real problem, as the tight coupling is still to an abstraction you can directly control, and not to a concrete implementation. You are still able to swap out the specific database code with a different one as you see fit, and our actual implementation is still a completely normal object that can be easily tested.

Using the WordPress Plugin API

The WordPress Plugin API can also be used to get rid of Singletons, when they are being used for their capability of providing global access across plugins. This is a mostly clean solution within the constraints of WordPress, with the added caveat that it ties the entire infrastructure and architecture of your code to the WordPress Plugin API. Don’t use this if you intend to reuse your code with different frameworks.

// Interface, so that we can deal with multiple implementations and properly
// mock for testing.
interface DatabaseConnection {

   const FILTER = 'get_database_connection';

   public function query( ...$args );
}

// The implementation we're currently using.
class MySQLDatabaseConnection implements DatabaseConnection {

   public function query( ...$args ) {
      // Execute query and return results.
   }
}

// Initialization code.
$database = new MySQLDatabaseConnection();
add_filter( DatabaseConnection::FILTER, function () use ( $database ) {
   return $database;
} );

// Consuming code.
$database = apply_filters( DatabaseConnection::FILTER );
$result   = $database->query( $query );

One of the main compromises with this code is that your architecture is directly tied to the WordPress Plugin API. If you ever plan to provide your plugin functionality for Drupal sites as well, you’re facing a complete rewrite.

Another potential issue is that you’re now relying on the timing of the WordPress hooks. This can introduce timing-related bugs, which are often hard to replicate and debug.

Using a Service Locator

A Service Locator is one form of an Inversion of Control Container. You’ll find several references online outing it as an anti-pattern. This is true to some degree, but as we had already discussed above, all of the recommendations here have to be considered as compromises only.

The Service Locator is a container that gives you access to Services that were implemented elsewhere. The container itself is mostly just a collection of instances mapped to identifiers. More elaborate Service Locator implementations might add features like lazy instantiation or proxy generation to the mix.

// Container interface so that we can swap out the Service Locator
// implementation.
interface Container {

   public function has( string $key ): bool;

   public function get( string $key );
}

// Basic implementation of a Service Locator.
class ServiceLocator implements Container {

   protected $services = [];

   public function has( string $key ): bool {
      return array_key_exists( $key, $this->services );
   }

   public function get( string $key ) {
      $service = $this->services[ $key ];
      if ( is_callable( $service ) ) {
         $service = $service();
      }

      return $service;
   }

   public function add( string $key, callable $service ) {
      $this->services[ $key ] = $service;
   }
}

// Interface, so that we can deal with multiple implementations and properly
// mock for testing.
interface DatabaseConnection {

   public function query( ...$args );
}

// The implementation we're currently using.
class MySQLDatabaseConnection implements DatabaseConnection {

   public function query( ...$args ) {
      // Execute query and return results.
   }
}

// Initialization code.
$services = new ServiceLocator();
$services->add( 'Database', function () {
   return new MySQLDatabaseConnection();
} );

// Consuming code.
$result = $services->get( 'Database' )->query( $query );

As you might have guessed in the above example, there’s still the problem of getting a hold of the reference to the $services instance. This can be solved by combining it with any of the previous three methods.

  • Using a Factory:
    $result = ( new ServiceLocator() )->get( 'Database' )->query( $query );
    
  • Using a Static Proxy:
    $result = Services::get( 'Database' )->query( $query );
    
  • Using the WordPress Plugin API:
    $services = apply_filters( 'get_service_locator' );
    $result   = $services->get( 'Database' )->query( $query );
    

This leaves us now with the question of whether we should really use the “anti-pattern” Service Locator to replace the “anti-pattern” Singleton… The problem with the Service Locator is that it “hides” dependencies. To explain what this means, you need to think about a code base that actually uses proper constructor injection throughout. In such a codebase, a quick look at the constructor of a given object immediately lets you know which other objects it depends on. If your object has access to a reference to the Service Locator, you can get around this explicit dependency resolution and fetch a reference (and thus depend on) any other object from within the actual logic. This is what people refer to when they state that the Service Locator “hides” dependencies.

However, within the WordPress context, we need to accept that we’re already blocked from an ideal situation from the start. There is no technical possibility of having proper dependency injection throughout the entire codebase. This means that we have to put some compromise into the code either way. The Service Locator, although not a perfect solution, is a good fit within the such a legacy context, and at least allows you to have these “compromises” be located in one central place, instead of having your entire codebase be riddled with them.

Using Dependency Injection

If you are only working from within your own plugin and don’t need to provide access to your objects to other plugins, you’re in luck: you can use true dependency injection to get rid of the need to have global access to your dependencies.

// Interface, so that we can deal with multiple implementations and properly
// mock for testing.
interface DatabaseConnection {

   public function query( ...$args );
}

// The implementation we're currently using.
class MySQLDatabaseConnection implements DatabaseConnection {

   public function query( ...$args ) {
      // Execute query and return results.
   }
}

// We'll have to model the entire plugin to demonstrate this concept.
class Plugin {

   private $database;

   public function __construct( DatabaseConnection $database ) {
      $this->database = $database;
   }

   public function run() {
      $consumer = new Consumer( $this->database );
      return $consumer->do_query();
   }
}

// Consuming code.
// Also modeled as an entire class to demonstrate constructor injection.
class Consumer {

   private $database;

   public function __construct( DatabaseConnection $database ) {
      $this->database = $database;
   }

   public function do_query() {
      // Here's the actual consuming code.
      // At this point, it had an arbitrary database connection injected.
      return $this->database->query( $query );
   }
}

// Inject dependencies from the bootstrap code through the entire code tree.
$database = new MySQLDatabaseConnection();
$plugin   = new Plugin( $database );
$result   = $plugin->run();

This looks a bit more involved now, but keep in mind that we had to build a basic version of the entire plugin to demonstrate the dependency injection.

As we can’t have full dependency injection across the entire app, we can at least have it within the constraints of our own plugin.

This is an example of doing the actual wiring in a manual way, by explicitly instantiating all dependencies ourselves. In a more complex codebase, you’d want to look into using an auto-wiring Dependency Injector (a specialized container) that accepts upfront configuration information and that can then recursively instantiate your entire tree in one call.

Here’s an example of how this wiring would be done with such a Dependency Injector (given the same classes/interfaces as in the last example):

// Let the injector know what implementation to use for resolving the
// DatabaseConnection interface.
$injector->alias( DatabaseConnection::class, MySQLDatabaseConnection::class );

// Let the injector know it should always return the same shared instance
// for DatabaseConnection requests.
$injector->share( DatabaseConnection::class );

// Let the injector instantiate the Plugin class, which will cause it to
// recursively traverse all constructors and instantiate objects to
// resolve dependencies.
$plugin = $injector->make( Plugin::class );

Combinations

For more complex needs that are distributed across several custom and third-party plugins, you should look into combining the above approaches in ways that are adapted to your specific requirements.

As an example, I’ve been using the following approach with more complex client projects to get closer to the ideal approach I described further above:

  • Every plugin is instantiated through a centralized auto-wiring Dependency Injector.
  • Every plugin is a service provider that can register services with a centralized Service Locator.
  • Intra-plugin dependencies -> dependency injection.
  • Inter-plugin dependencies -> service location.
  • Third-party dependencies -> virtual services that wrap third-party functionality.

You can read more about this approach and see a recording of a case study talk on the Bright Nucleus Architecture page.

Conclusion

As you can see, there are several viable approaches to getting rid of your Singletons. While none of them are perfect in the WordPress context, they are all preferable to an actual Singleton, without exception.

Remember: the problem with Singletons is not that they are shared, but that they enforce their own instantiation.

If you think you know of a specific use case where the Singleton pattern is the only viable solution, please let me know, as I’d love to discuss!

7 Comments

  1. Thorsten Frommen on July 8, 2017 at 7:18 pm

    Hey Alain,

    nice write-up that is both very complete and useful, especially for people new to OOP and/or people who use some sort of singleton-infected boilerplate for WordPress plugins—and maybe simply don’t know better. The task is “just” to make them aware of this post. ;)

    At the risk of sounding like I was justifying the use of singletons, I would like to make one amendment. When thinking about unit testability of a class, a singleton is a better choice than a static proxy. Now why is this?

    Although (almost ;) ) all of what you mentioned about singletons is correct, there is a way to make them easily testable in terms of real unit testing. What makes a singleton your enemy in unit testing is none of the two responsibilities that you mentioned, but the fact that it cannot be undone. Consequently, to allow for proper unit testing, all you would have to do is to add some method that either resets the internal state, or actually unsets it altogether. An example for the latter could look something like this:

    final class HalfBadSingleton {
    
        private static $instance;
    
        public static function instance() {
    
            if ( ! isset( self::$instance ) ) {
                self::$instance = new self();
            }
    
            return self::$instance;
        }   
    
        public function reset() {
    
            self::$instance = null;
        }
    
        // Other code...
    }
    

    When we have a look at the static proxy, we can see that there is no way to re- or unset the static part—because it is encapsulated in the static method of the class.

    Again, I do not want to be seen as a singleton evangelist :D at all. It’s just that, at least, one of your replacements might be even worse, in a specific context.

    Thanks for the post,
    ThorstenReport

    • Alain Schlesser on July 8, 2017 at 7:35 pm

      Hey Thorsten,

      Thanks for the feedback!

      I had thought about talking about “resetting” a Singleton, but didn’t bother, as it does not have anything to do with the pattern per se, but rather is a random hack to be able to control the behaviour directly from within your unit tests. So this in essence causes your interface to be messed up for the purposes of testing. This “reset” hack can be added just as well to a Static Proxy, by the way.

      As I had written in the comment for the get_connection() method in the Static Proxy example, you can use any instantiation logic you might need. And that’s where the Static Proxy is so convenient. Behind the scenes, it can just use a real Dependency Injector or Service Locator to get the actual instance to use. The consuming code does not need to care what mechanism is being used.

      In your unit tests where you test code that has a Static Proxy as a dependency, you can then reconfigure whatever mechanism you use to pass mocks instead of real objects instead. This means, your Static Proxy can transparently use a MockDatabaseConnection from within these unit tests.

      What’s more, for testing the actual object that the Static Proxy represents, you can just omit the proxy and test the normally instantiable object itself, which is not possible with Singletons. The proxy is just a 1:1 mapping from static calls to instance calls, so only testing the actual object it represents is good enough.Report

      • Thorsten Frommen on July 8, 2017 at 10:48 pm

        Hey again,

        let me follow up.

        I had thought about talking about “resetting” a Singleton, but didn’t bother, as it does not have anything to do with the pattern per se…

        True.

        …[it] is a random hack to be able to control the behaviour directly from within your unit tests.

        I wouldn’t put it like that. This is, in my opinion, in no way a hack, but simply a means to allow for easy (unit) testing. Just like several other things that one might do in that regard, for example, delegating die or exit calls, wrapping around static (third-party) classes, wrapping around superglobals etc. I know, I know—not all of these are just because of easier testing. But I guess you know what I mean.

        As I had written in the comment for the `get_connection()` method in the Static Proxy example, you can use any instantiation logic you might need.

        Yes, I know. I better should have said “your static proxy”, and not talk about static proxies in general.

        In your unit tests where you test code that has a Static Proxy as a dependency, you can then reconfigure whatever mechanism you use to pass mocks instead of real objects instead.

        I guess it all boils down to whether or not people know how to properly work with a static proxy. I didn’t mean to say that you cannot unit test a static proxy (at all). First, though, you have to make sure you can reset its internal state. And second, if your SUT (subject under test) interacts with a static proxy, you would either have to set up and use a stub, or use a mocking framework that is able to mock static classes (e.g., Mockery)—or you’re not doing real unit tests.

        Cheers,
        ThorstenReport

  2. Evan on July 8, 2017 at 9:44 pm

    Nice article Alain, glad to see this topic broken down so thoroughly with many examples as well! It’s hard to find a plugin in the wild these days that doesn’t use this pattern. You mentioned tests several times throughout the article. I think quite possibly the larger issue might be that many aren’t writing tests. For those who aren’t as far along the path and learning from the surrounding ecosystem of code in the WP sphere, reaching for a singleton may seem like a ubiquitous best practice.

    I would add that one of the pitfalls for someone new to a non-singleton architecture is that if your objects are registering their methods as callbacks to WP’s plugin API, then you need to exercise a bit more care in making sure those don’t get registered more than once in some cases. A lot of singleton classes register their actions and filters in the constructor where that can never happen!

    One small correction as well: in your Plugin API example, do_action doesn’t return a value; maybe you were referring to using apply_filters?

    In regards to dependencies, are you using 3rd party packages via Composer for things like your container or including that kind of thing in your own namespace as to not conflict with other plugins? Would love to hear your take on dependency hell in WordPress.Report

    • Alain Schlesser on July 9, 2017 at 9:51 am

      Hi Evan,

      Thanks for the feedback. And yes, I agree, most developers have probably never hit any of the issues the Singleton creates because they are not following any of the other related best practices either. Thorsten Frommen has a nice article for people to get started: An Introduction to Unit Testing

      Regarding the point about the need to make sure people only ever register something once: If the code is properly structured and documented, I don’t see why should go out of your way to instantiate something that is not meant to be instantiated. The explicit API you communicate should make usage obvious, then this is a non-issue. Yes, you will always have the one developer that breaks whatever you hand them to, but the majority of developers will be smart enough to get it right.

      Also, thanks for pointing out the issue with do_action(), I corrected the article. You are right, that was meant to be apply_filters(), but I had structured the example the other way around in the beginning, where it was using actions.

      Right now, to be honest, I just avoid creating publically supported plugins, because WordPress does not allow me to develop them without me being stuck in “support hell” as a result. It is just not possible to deal with third-party dependencies without the developer paying the price for WordPress’ technical debt. For my client work, on the other hand, I use Composer for pretty much everything. This works just fine, when you control the entire stack and know what the technical limits of PHP are.

      Thanks for taken the time to comment!Report

  3. Lewis Cowles on August 1, 2017 at 9:12 pm

    I was trying to solve “how to pass DB into application?” just the other day. I felt useless I couldn’t come up with a clean solution, so I went out for a walk, I was babbling to my poor wife about feeling like my brain was melting, reaching for metaphors, when I settled on a cake. Of course I started when you make a cake you take your ingredients. And then it hit me. You don’t modify the cake ingredients at an atomic level, you simply accept them as they are. You don’t inject the DB into the application. You don’t even inject the DB config into the application!

    You make a repository Interface for each model, for each thing that might need to be passed. You can then implement it given the database. The real beauty is that under the hood you can default instantiate a class to read from ENV vars, from CONFIG, you can use define("DB_URL") like heroku does, or take the piecemeal approach WP does. Your repository becomes an adaptor to whatever database library you want to use hard-coded, so long as it fulfils the interface. If you have a JOIN, or something that uses multiple tables, you define an interface and then code a repository to it. If your application needs to share queries or logic, you need to break those out into an interface with implementations you can share (I know it gets crazy, but it’s clean).

    Your Models are divorced from your Repository. Your Repository Interface is Divorced from your application. Where necessary you can define abstract classes, where differences in configuration are required, and you can even create Model Interfaces.

    DI, and Singleton don’t need to enforce single-ness. If it is required, lazy-instantiation of dependencies like php-di provides allows you to pass your controllers into the application config, and their dependencies directly into them. It’s very clean. A lot of text, and setup, but very clean and without Singleton or magic for passing such specific details. Where a controller needs to use several objects, shared functionality must be broken into separate classes that can be shared, a new interface created, and the controller would use that one interface (The overall implementation might end-up as a facade to not violate SOLID).

    @startuml
    (Plugins) –> (App)
    (Base classes) –> (App)
    @enduml

    I think it may be a fundamental problem with WP that it has filters the extensions pull in, rather than pulling in from interfaces that extensions provide. The only way to handle the number of types of extension then becomes an exercise in effort. You’d have to not just have a plugins folder, but standardised ways to access those plugins filters, actions etc (sadly a lot like Magento).Report

    • Alain Schlesser on August 5, 2017 at 11:19 am

      Hey Lewis,

      In general, you’d think of your application in layers, like the Domain layer, the Application layer, the Infrastructure layer, etc… Everywhere you need to cross this layer, you’ll want to provide an interface and use the Dependency Inversion Principle. This means that both parties depend on the interface, instead of one depending on the other.

      So, in the example you’re citing of a Repository, the Repository should be the interface, and then you’ll have both your Application layer depend on that interface (by having any implementation of Repository be injected) as well as the Infrastructure layer depend on that interface (by implementing the Repository interface in the form of a WpdbRepository, for example).

      This makes it so that you can change both sides of that relationship independently, as long as you stick to the contract (= interface) that they both agreed upon.

      With a proper DI, you can then have a shared WpdbRepository be injected into the class in the Application layer that needs a Repository implementation. No need for a singleton or any other global state hacks.

      I think it may be a fundamental problem with WP that it has filters the extensions pull in, rather than pulling in from interfaces that extensions provide.

      Yes, this is indeed problematic, as this whole actions & filters stuff completely destroys any boundaries and makes it nearly impossible to do high-level optimisations, as you never know when some piece of code might be needed.

      We face this problem a lot with WP-CLI development, for example. To return a list of post titles, you cannot just read from the database and return that list. You need to load the entire WordPress stack and all of the plugins, because any of these plugins (or the theme) can potentially filter the post title. If you omit loading the plugins, you risk returning the wrong data.

      Thanks for the thoughtful comment!Report

Leave a Comment