In our last post "Enabling Action and Filter Hook Removal from Class-based WordPress Plugins" we offered the following as a suggested template for coding WordPress plugins:
-
<?php
-
/*
-
* Plugin Name: Your Plugin Name
-
*/
-
class Your_Plugin_Classname {
-
private static $_this;
-
function __construct() {
-
self::$_this = $this;
-
// Your constructor code goes here
-
}
-
static function this() {
-
return self::$_this;
-
}
-
// Your plugin methods go here
-
}
-
new Your_Plugin_Classname();
-
[/code]
-
-
<p>Unfortunately there is an issue with this approach which we will fix in this post. </p>
-
-
<h2>A <em>De facto</em> Singleton</h2>
-
-
<p>The structure proposed is a de facto <a href="http://en.wikipedia.org/wiki/Singleton_pattern">Singleton</a> where Singletons are defined by Wikipedia to be:</p>
-
-
<blockquote>
-
<p>A design pattern in software engineering that restricts the instantiation of a class to one object.</p>
-
</blockquote>
-
-
<p>In our template the issue lies in the <a href="http://en.wikipedia.org/wiki/De_facto_standard"><em>de facto</em></a> part. Our class template is designed for only one instance but does not stop a developer from using <code>new</code> to create additional instances of the class. A second instance would overwrite the first in <code>self::$_this</code> which would create havoc for any code that expects <code>self::$_this</code> to be the same for the life of the <em>"page load"</em>[^page-load].</p>
-
-
<h2>Disallowing a Second Instantiation</h2>
-
-
<p>It's really quite simple; since a class' <code>__construct()</code> method gets invoked whenever the <code>new</code> operator is used we need to test the <code>static</code> variable <code>self::$_this</code> to see if it has been set and if so call <code>wp_die()</code>. Note we used five lines below when we could have consolidated down to two but we wanted to ensure the code didn't wrap in this post:</p>
-
-
[code lang="php"]if ( isset( self::$_this ) ) {
-
$msg = '%s is a singleton class and you cannot create a second instance.';
-
$msg = __( $msg, 'your-plugin-translation-domain' );
-
wp_die( sprintf( $msg, get_class( $this ) ) );
-
}
<?php /* * Plugin Name: Your Plugin Name */ class Your_Plugin_Classname { private static $_this; function __construct() { self::$_this = $this; // Your constructor code goes here } static function this() { return self::$_this; } // Your plugin methods go here } new Your_Plugin_Classname(); [/code] <p>Unfortunately there is an issue with this approach which we will fix in this post. </p> <h2>A <em>De facto</em> Singleton</h2> <p>The structure proposed is a de facto <a href="http://en.wikipedia.org/wiki/Singleton_pattern">Singleton</a> where Singletons are defined by Wikipedia to be:</p> <blockquote> <p>A design pattern in software engineering that restricts the instantiation of a class to one object.</p> </blockquote> <p>In our template the issue lies in the <a href="http://en.wikipedia.org/wiki/De_facto_standard"><em>de facto</em></a> part. Our class template is designed for only one instance but does not stop a developer from using <code>new</code> to create additional instances of the class. A second instance would overwrite the first in <code>self::$_this</code> which would create havoc for any code that expects <code>self::$_this</code> to be the same for the life of the <em>"page load"</em>[^page-load].</p> <h2>Disallowing a Second Instantiation</h2> <p>It's really quite simple; since a class' <code>__construct()</code> method gets invoked whenever the <code>new</code> operator is used we need to test the <code>static</code> variable <code>self::$_this</code> to see if it has been set and if so call <code>wp_die()</code>. Note we used five lines below when we could have consolidated down to two but we wanted to ensure the code didn't wrap in this post:</p> [code lang="php"]if ( isset( self::$_this ) ) { $msg = '%s is a singleton class and you cannot create a second instance.'; $msg = __( $msg, 'your-plugin-translation-domain' ); wp_die( sprintf( $msg, get_class( $this ) ) ); }
The Real Singleton
Adding the code snippet above to our template completes our singleton:
A Complete Working Example
Grabbing our code from our last post and adding the singleton logic gives us this example; to test just drop[^install-plugin] into your plugins directory:
Summary
When using classes as code wrappers for WordPress plugins as we've been recommending you are effectively using a Singleton without enforcing the singleton constraint. To disallow multiple instantiation use the isset( self::$_this )
test. If you don't then site builders, theme coders and other plugin developers might misuse your class by instantiating it a second time and overwrite your singleton instance. And nobody wants that. 🙂
Update
Be sure to read our follow up to this post where we talk about ensuring that classes uses for Singletons actually are limited to a single instance.
Related Links
Recently Eric Mann has had a field day writing about Singletons for WordPress, three (3) posts in little over a week (or should that be a "field week?") You'll note Eric recommends a slight different structure for singletons than we do. We'll address that in our next post:
About a year ago RJ Zaworski wrote a post offering his take on a:
- WordPress Plugin Boilerplate template
RJ has some interesting techniques that were very clearly influences by Model-view-controller frameworks, approaches that are not common in the WordPress world. Nonetheless, his coding techniques are interesting.
[footnotes]
[^page-load]: When we refer to a "page load" we mean the entire lifecycle generated by an HTTP request where the PHP starting with /index.php
is executed on the server and the resultant HTML is returned to the HTTP client which is typically a site visitor's browser.
[^install-plugin]: To test our example simply save as a file you can name thanks-for-reading.php
in the WordPress plugins directory on your web server, typically /wp-content/plugins/
from the root of your website and then activate it like you would any other plugin.
[/footnotes]
These tutorials are getting better and better.
Excellent ending to singleton for classes. I’ve been using this pattern in all of my plugins since you first introduced it. Its making designing plugins a lot easier.
Just to clarify, Mike, does the singleton method still allow multiple unique instances of your plugin on a page?
Hi @Chris,
Thanks for commenting. Can you elaborate on what you mean by “multiple unique instances of your plugin on a page?” Are you referring to widgets, or something else?