Almost any WordPress or PHP programmer, even in his early days, has some contact with the Singleton design pattern. For obvious reasons, this is maybe the first PHP pattern learnt from those who study for developing using this language.

Regardless any discussion on the “antipattern” aspect of the Singleton, the reality is that most applications still use it in some way. When it comes to WordPress plugins or themes, the singleton is a common place.

But there is a general problem with patterns: most people just use them as they come, and never try to imagine possible changes or parallel uses. This is particularly true with the Singleton. In another article, we talked about the Singleton and Multiton patterns, and even suggested a third variation of it.

Establishing conditions

If you just use the Singleton as it comes, that means you’ll be checking for requirements and conditions AFTER the class instance. In an ideal world, if a class relies on some other classes or even PHP extensions to perform its tasks, an instance without these constraints will definitely point to a flaw.

We’ll use, like in our previous article, the Singleton as a trait which can be used by any class. That means a class using this trait can be executed by a static method instance() just once. The basic trait looks like this.

trait Singleton {

	/**
     * @var reference to singleton instance
     */
    private static $instance;
    
    /**
     * Creates a new instance of a singleton class (via late static binding),
     * accepting a variable-length argument list.
     *
     * @return self
     */
    final public static function instance()
    {

        if(!self::$instance) {
            
            self::$instance = new self;  

        }
        
        return self::$instance;
    }

    /**
     * Prevents cloning the singleton instance.
     *
     * @return void
     */
    private function __clone() {}

    /**
     * Prevents unserializing the singleton instance.
     *
     * @return void
     */
    public function __wakeup() {}
}

Just for recap, let’s use the Singleton trait in a basic class named A, as you can see in the following example. The trait is used inside the class, and then it can be instantiated once by using the static method.

class Any {

	use Selecton;

	private function __construct() {
		echo 'A ';
	}
}

// And then...
$instance = Any::instance();

However, now we want to establish a couple of conditions before running the tasks inside that class. Let’s say the class methods depend on particular PHP extensions, or even other classes. So, this means we need to find a way of checking for those BEFORE the new class instance is loaded.

Creating an array of constraints

The easier and more comfortable way – an array of constraints. That will make possible to check for multiple classes and extensions at the same time. In our example, I want PHP extensions mbstring and curl to be loaded, and also the classes B and C to be declared.

$constraints = [
	'extensions' => [
		'curl',
		'mbstring'
	],
	'classes' => [
		'A',
		'B'
	]
];

And now, let’s put our Singleton to work on checking those, and turning it into a “Selecton”. The way we are doing that, instead of looking for each of the constraints individually, is comparing them in groups with arrays containing all extensions and classes. This can be easily reached by using two native PHP functions:

The method we want to tweak is the original method instance(), in the Singleton trait. In our case, we gave a new name “Selecton” to the trait, but you can just keep the original.

final public static function instance(array $constraints = null)
    {
        $extensions = get_loaded_extensions();
        $classes = get_declared_classes();
// ...

Brilliant! It gives us two arrays: one with all loaded extensions and another with all declared classes. As a new argument for instance(), we have the array of constraints. However, if we don’t pass any argument, the Singleton will be working just as it normally did.

Now I’ll use array_intersect() and array_diff() as a way of comparing our constraints with the pool of loaded extensions and declared classes. The first function grabs the common elements in our array and the pools. The second uses the first and catches the differences – namely those constraints NOT loaded or declared.

If the difference returns an empty array, the class instance can be executed. If not, we can opt for throwing an exception, or just printing a notice – if we are using this approach in an WordPress plugin, for example. That makes our complete instance() method to look like this.

    final public static function instance(array $constraints = null)
    {

        $extensions = get_loaded_extensions();
        $classes = get_declared_classes();

        if($constraints['extensions']) {

            $commonExt = array_intersect($extensions, $constraints['extensions']);
            $missingExt = array_diff($constraints['extensions'], $commonExt);

            if(!empty($missingExt)) {
                throw new \Exception('Missing extensions: ' );                
            }
        }

        if($constraints['classes']) {

            $loadedClasses = array_intersect($classes, $constraints['classes']);
            $missingClasses = array_diff($constraints['classes'], $loadedClasses);

            if(!empty($missingClasses)) {
                throw new \Exception('Missing classes: ');                
            }
        }

        if(!self::$instance) {
            
            self::$instance = new self;  
        }
        
        return self::$instance;
    }

Any further constraints to check? Well, just keep the logic and add your constraints, as new subarrays. This could be involving the PHP or WordPress versions, existent plugins, or even environment and server variables, like memory, MySQL versions and so on.

Leave a comment

Your email address will not be published. Required fields are marked *