Home > Others > How To Use Autoloading And A Plugin Container In WordPress Plugins

How To Use Autoloading And A Plugin Container In WordPress Plugins

Settings Page In Action

Building and maintaining1 a WordPress plugin can be a daunting task. The bigger the codebase, the harder it is to keep track of all the working parts and their relationship to one another. And you can add to that the limitations imposed by working in an antiquated version of PHP, 5.2.

In this article we will explore an alternative way of developing WordPress plugins, using the lessons learned from the greater PHP community, the world outside WordPress. We will walk through the steps of creating a plugin and investigate the use of autoloading and a plugin container.

Let’s Begin

The first thing you need to do when creating a plugin is to give it a unique name. The name is important as it will be the basis for all our unique identifiers (function prefix, class prefix, textdomain, option prefix, etc.). The name should also be unique across the wordpress.org space. It won’t hurt if we make the name catchy. For our sample plugin I chose the name Simplarity, a play on the words “simple” and “clarity”.

We’ll assume you have a working WordPress installation already.

Folder Structure

First, create a directory named simplarity inside wp-content/plugins. Inside it create the following structure:

  • simplarity.php: our main plugin file
  • css/: directory containing our styles
  • js/: directory containing JavaScript files
  • languages/: directory that will contain translation files
  • src/: directory containing our classes
  • views/: directory that will contain our plugin view files

The Main Plugin File

Open the main plugin file, simplarity.php, and add the plugin information header:

<?php
/*
Plugin Name: Simplarity
Description: A plugin for smashingmagazine.com
Version: 1.0.0
License: GPL-2.0+
*/

This information is enough for now. The plugin name, description, and version will show up in the plugins area of WordPress admin. The license details are important to let your users know that this is an open source plugin. A full list of header information can found in the WordPress codex2.

Autoloading

Autoloading allows you to automatically load classes using an autoloader so you don’t have to manually include the files containing the class definitions. For example, whenever you need to use a class, you need to do the following:

require_once '/path/to/classes/class-container.php';
require_once '/path/to/classes/class-view.php';
require_once '/path/to/classes/class-settings-page.php';

$plugin = new Container();
$view = new View();
$settings_page = new SettingsPage();

With autoloading, you can use an autoloader instead of multiple require_once3 statements. It also eliminates the need to update these require statements whenever you add, rename, or change the location of your classes. That’s a big plus for maintainability.

Adopting The PEAR Naming Convention For Class Names

Before we create our autoloader we need to create a convention for our class names and their location in the file system. This will aid the autoloader in mapping out the class to its source file.

For our class names we will adopt the PEAR naming convention4. The gist is that class names are alphabetic characters in StudlyCaps. Each level of the hierarchy is separated with a single underscore. Class names will directly map to the directories in which they are stored.

It’s easier to illustrate it using examples:

  • A class named Simplarity_Plugin would be defined in the file src/Simplarity/Plugin.php.
  • A class named Simplarity_SettingsPage would be defined in src/Simplarity/SettingsPage.php.

As you can see with this convention, the autoloader will just replace the underscores with directory separators to locate the class definition.

What About The WordPress Coding Standards For Class Names?

As you might be aware, WordPress has its own naming convention5 for class names. It states:

Class names should use capitalized words separated by underscores. Any acronyms should be all upper case. […] Class file names should be based on the class name with class- prepended and the underscores in the class name replaced with hyphens, for example WP_Error becomes class-wp-error.php

I know that we should follow the standards of the platform that we are developing on. However, we suggest using the PEAR naming convention because:

  • WP coding standards do not cover autoloading.
  • WP does not follow its own coding standards. Examples: class.wp-scripts.php and SimplePie. This is understandable since WordPress grew organically.
  • Interoperability allows you to easily use third-party libraries that follow the PEAR naming convention, like Twig. And conversely, you can easily port your code to other libraries sharing the same convention.
  • It’s important your autoloader is future-ready. When WordPress decides to up the ante and finally move to PHP 5.3 as its minimum requirement, you can easily update the code to be PSR-0 or PSR-4-compatible and take advantage of the built-in namespaces instead of using prefixes. This is a big plus for interoperability.

Note that we are only using this naming convention for classes. The rest of our code will still follow the WordPress coding standards. It’s important to follow and respect the standards of the platform that we are developing on.

Now that we have fully covered the naming convention, we can finally build our autoloader.

Building Our Autoloader

Open our main plugin file and add the following code below the plugin information header:

spl_autoload_register( 'simplarity_autoloader' );
function simplarity_autoloader( $class_name ) {
  if ( false !== strpos( $class_name, 'Simplarity' ) ) {
    $classes_dir = realpath( plugin_dir_path( __FILE__ ) ) . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR;
    $class_file = str_replace( '_', DIRECTORY_SEPARATOR, $class_name ) . '.php';
    require_once $classes_dir . $class_file;
  }
}

At the heart of our autoloading mechanism is PHP’s built in spl_autoload_register6 function. All it does is register a function to be called automatically when your code references a class that hasn’t been loaded yet.

The first line tells spl_autoload_register to register our function named simplarity_autoloader:

spl_autoload_register( 'simplarity_autoloader' );

Next we define the simplarity_autoloader function:

function simplarity_autoloader( $class_name ) {
  …
}

Notice that it accepts a $class_name parameter. This parameter holds the class name. For example when you instantiate a class using $plugin = new Simplarity_Plugin(), $class_name will contain the string “Simplarity_Plugin”. Since we are adding this function in the global space, it’s important that we have it prefixed with simplarity_.

The next line checks if $classname contains the string “Simplarity” which is our top level namespace:

if ( false !== strpos( $class_name, 'Simplarity' ) ) {

This will ensure that the autoloader will only run on our classes. Without this check, our autoloader will run every time an unloaded class is referenced, even if the class is not ours, which is not ideal.

The next line constructs the path to the directory where our classes reside:

$classes_dir = realpath( plugin_dir_path( __FILE__ ) ) . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR;

It uses WP’s plugin_dir_path7 to get the plugin root directory. __FILE__ is a magic constant8 that contains the full path and filename of the current file. DIRECTORY_SEPARATOR is a predefined constant that contains either a forward slash or backslash depending on the OS your web server is on. We also use realpath9 to normalize the file path.

This line resolves the path to the class definition file:

$class_file = str_replace( '_', DIRECTORY_SEPARATOR, $class_name ) . '.php';

It replaces the underscore (_) in $class_name with the directory separator and appends .php.

Finally, this line builds the file path to the definition and includes the file using require_once:

require_once $classes_dir . $class_file;

That’s it! You now have an autoloader. Say goodbye to long lines of require_once statements.

Plugin Container

A plugin container is a special class that holds together our plugin code. It simplifies the interaction between the many working parts of your code by providing a centralized location to manage the configuration and objects.

Uses Of Our Plugin Container

Here are the things we can expect from the plugin container:

  • Store global parameters in a single location

    Often you’ll find this code in plugins:

    define( 'SIMPLARITY_VERSION', '1.0.0' );
    define( 'SIMPLARITY_PATH', realpath( plugin_dir_path( __FILE__ ) ) . DIRECTORY_SEPARATOR );
    define( 'SIMPLARITY_URL', plugin_dir_url( __FILE__ ) );
    

    Instead of doing that, we could do this instead:

    $plugin = new Simplarity_Plugin();
    $plugin['version] = '1.0.0';
    $plugin['path'] = realpath( plugin_dir_path( __FILE__ ) ) . DIRECTORY_SEPARATOR;
    $plugin['url'] = plugin_dir_url( __FILE__ );
    

    This has the added benefit of not polluting the global namespace with our plugin’s constants, which in most cases aren’t needed by other plugins.

  • Store objects in a single location

    Instead of scattering our class instantiations everywhere in our codebase we can just do this in a single location:

    $plugin = new Simplarity_Plugin();
    /…/
    $plugin['scripts'] = new Simplarity_Scripts(); // A class that loads javascript files
    
  • Service definitions

    This is the most powerful feature of the container. A service is an object that does something as part of a larger system. Services are defined by functions that return an instance of an object. Almost any global object can be a service.

    $plugin['settings_page'] = function ( $plugin ) {
      return new SettingsPage( $plugin['settings_page_properties'] );
    };
    

    Services result in lazy initialization whereby objects are only instantiated and initialized when needed.

    It also allows us to easily implement a self-resolving dependency injection design. An example:

    $plugin = new Plugin();
    $plugin['door_width'] = 100;
    $plugin['door_height'] = 500;
    $plugin['door_size'] = function ( $plugin ) {
      return new DoorSize( $plugin['door_width'], $plugin['door_height'] );
    };
    $plugin['door'] = function ( $plugin ) {
      return new Door( $plugin['door_size'] );
    };
    $plugin['window'] = function ( $plugin ) {
      return new Window();
    };
    $plugin['house'] = function ( $plugin ) {
      return new House( $plugin['door'], $plugin['window'] );
    };
    $house = $plugin['house'];

    This is roughly equivalent to:

    $door_width = 100;
    $door_height = 500;
    $door_size = new DoorSize( $door_width, $door_height );
    $door = new Door( $door_size );
    $window = new Window();
    $house = new House( $door, $window );

    Whenever we get an object, as in $house = $plugin['house']; , the object is created (lazy initialization) and dependencies are resolved automatically.

Building The Plugin Container

Let’s start by creating the plugin container class. We will name it “Simplarity_Plugin”. As our naming convention dictates, we should create a corresponding file: src/Simplarity/Plugin.php.

Open Plugin.php and add the following code:

<?php
class Simplarity_Plugin implements ArrayAccess {
  protected $contents;
    
  public function __construct() {
    $this->contents = array();
  }
  
  public function offsetSet( $offset, $value ) {
    $this->contents[$offset] = $value;
  }

  public function offsetExists($offset) {
    return isset( $this->contents[$offset] );
  }

  public function offsetUnset($offset) {
    unset( $this->contents[$offset] );
  }

  public function offsetGet($offset) {
    if( is_callable($this->contents[$offset]) ){
      return call_user_func( $this->contents[$offset], $this );
    }
    return isset( $this->contents[$offset] ) ? $this->contents[$offset] : null;
  }
  
  public function run(){ 
    foreach( $this->contents as $key => $content ){ // Loop on contents
      if( is_callable($content) ){
        $content = $this[$key];
      }
      if( is_object( $content ) ){
        $reflection = new ReflectionClass( $content );
        if( $reflection->hasMethod( 'run' ) ){
          $content->run(); // Call run method on object
        }
      }
    }
  }
}

The class implements the ArrayAccess interface:

class Simplarity_Plugin implements ArrayAccess {

This allows us to use it like PHP’s array:

$plugin = new Simplarity_Plugin();
$plugin['version'] = '1.0.0'; // Simplicity is beauty

The functions offsetSet, offsetExists, offsetUnset and offsetGet are required by ArrayAccess to be implemented. The run function will loop through the contents of the container and run the runnable objects.

To better illustrate our plugin container, let’s start by building a sample plugin.

Example Plugin: A Settings Page

This plugin will add a settings page named “Simplarity” under WordPress Admin ? Settings.

Let’s go back to the main plugin file. Open up simplarity.php and add the following code. Add this below the autoloader code:

add_action( 'plugins_loaded', 'simplarity_init' ); // Hook initialization function
function simplarity_init() {
  $plugin = new Simplarity_Plugin(); // Create container
  $plugin['path'] = realpath( plugin_dir_path( __FILE__ ) ) . DIRECTORY_SEPARATOR;
  $plugin['url'] = plugin_dir_url( __FILE__ );
  $plugin['version'] = '1.0.0';
  $plugin['settings_page_properties'] = array( 
    'parent_slug' => 'options-general.php',
    'page_title' =>  'Simplarity',
    'menu_title' =>  'Simplarity',
    'capability' => 'manage_options',
    'menu_slug' => 'simplarity-settings',
    'option_group' => 'simplarity_option_group',
    'option_name' => 'simplarity_option_name'
  );
  $plugin['settings_page'] = new Simplarity_SettingsPage( $plugin['settings_page_properties'] );
  $plugin->run();
}

Here we use WP’s add_action to hook our function simplarity_init into plugins_loaded:

add_action( 'plugins_loaded', 'simplarity_init' );

This is important as this will make our plugin overridable by using remove_action. An example use case would be a premium plugin overriding the free version.

Function simplarity_init contains our plugin’s initialization code. At the start, we simply instantiate our plugin container:

$plugin = new Simplarity_Plugin();

These lines assign global configuration data:

$plugin['path'] = realpath( plugin_dir_path( __FILE__ ) ) . DIRECTORY_SEPARATOR;
$plugin['url'] = plugin_dir_url( __FILE__ );
$plugin['version'] = '1.0.0';

The plugin path contains the full path to our plugin, the url contains the URL to our plugin directory. They will come in handy whenever we need to include files and assets. version contains the current version of the plugin that should match the one in the header info. Useful whenever you need to use the version in code.

This next code assigns various configuration data to settings_page_properties:

$plugin['settings_page_properties'] = array(
  'parent_slug' => 'options-general.php',
  'page_title' =>  'Simplarity',
  'menu_title' =>  'Simplarity',
  'capability' => 'manage_options',
  'menu_slug' => 'simplarity-settings',
  'option_group' => 'simplarity_option_group',
  'option_name' => 'simplarity_option_name'
);

These configuration data are related to WP settings API10.

This next code instantiates the settings page, passing along settings_page_properties:

$plugin['settings_page'] = new Simplarity_SettingsPage( $plugin['settings_page_properties'] );

The run method is where the fun starts:

$plugin->run();

It will call Simplarity_SettingsPage‘s own run method.

The Simplarity_SettingsPage Class

Now we need to create the Simplarity_SettingsPage class. It’s a class that groups together the settings API functions.

Create a file named SettingsPage.php in src/Simplarity/. Open it and add the following code:

<?php
class Simplarity_SettingsPage {
  protected $settings_page_properties;

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

  public function run() {
    add_action( 'admin_menu', array( $this, 'add_menu_and_page' ) );
    add_action( 'admin_init', array( $this, 'register_settings' ) );
  }

  public function add_menu_and_page() { 

    add_submenu_page(
      $this->settings_page_properties['parent_slug'],
      $this->settings_page_properties['page_title'],
      $this->settings_page_properties['menu_title'], 
      $this->settings_page_properties['capability'],
      $this->settings_page_properties['menu_slug'],
      array( $this, 'render_settings_page' )
    );
  }
    
  public function register_settings() { 
    
    register_setting(
      $this->settings_page_properties['option_group'],
      $this->settings_page_properties['option_name']
    );   
  }
   
  public function get_settings_data(){
    return get_option( $this->settings_page_properties['option_name'], $this->get_default_settings_data() );
  }
    
  public function render_settings_page() {
    $option_name = $this->settings_page_properties['option_name'];
    $option_group = $this->settings_page_properties['option_group'];
    $settings_data = $this->get_settings_data();
    ?>
    <div class="wrap">
      <h2>Simplarity</h2>
      <p>This plugin is using the settings API.</p>
      <form method="post" action="options.php">
        <?php
        settings_fields( $this->plugin['settings_page_properties']['option_group']);
        ?>
        <table class="form-table">
          <tr>
              <th><label for="textbox">Textbox:</label></th>
              <td>
                <input type="text" id="textbox"
                  name="<?php echo esc_attr( $option_name."[textbox]" ); ?>"
                  value="<?php echo esc_attr( $settings_data['textbox'] ); ?>" />
              </td>
          </tr>
        </table>
        <input type="submit" name="submit" id="submit" class="button button-primary" value="Save Options">
      </form>
    </div>
    <?php
  }
   
  public function get_default_settings_data() {
    $defaults = array();
    $defaults['textbox'] = '';
      
    return $defaults;
  }
}

The class property $settings_page_properties stores the settings related to WP settings API:

<?php
class Simplarity_SettingsPage {
  protected $settings_page_properties;

The constructor function accepts the settings_page_properties and stores it:

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

The values are passed from this line in the main plugin file:

$plugin['settings_page'] = new Simplarity_SettingsPage( $plugin['settings_page_properties'] );

The run function is use to run startup code:

public function run() {
  add_action( 'admin_menu', array( $this, 'add_menu_and_page' ) );
  add_action( 'admin_init', array( $this, 'register_settings' ) );
}

The most likely candidate for startup code are filters11 and action hooks12. Here we add the action hooks related to our settings page.
Do not confuse this run method with the run method of the plugin container. This run method belongs to the settings page class.

This line hooks the add_menu_and_page function on to the admin_menu action:

add_action( 'admin_menu', array( $this, 'add_menu_and_page' ) );

Function add_submenu_page in turn calls WP’s add_submenu_page13 function to add a link under the WP Admin ? Settings:

public function add_menu_and_page() { 

  add_submenu_page(
    $this->settings_page_properties['parent_slug'],
    $this->settings_page_properties['page_title'],
    $this->settings_page_properties['menu_title'], 
    $this->settings_page_properties['capability'],
    $this->settings_page_properties['menu_slug'],
    array( $this, 'render_settings_page' )
  );

}

As you can see, we are pulling the info from our class property $settings_page_properties which we specified in the main plugin file.

The parameters for add_submenu_page are:

  • parent_slug: slug name for the parent menu
  • page_title: text to be displayed in the </code> element of the page when the menu is selected</li> <li><code>menu_title</code>: text to be used for the menu</li> <li><code>capability</code>: the capability required for this menu to be displayed to the user</li> <li><code>menu_slug</code>: slug name to refer to this menu by (should be unique for this menu)</li> <li><code>function</code>: function to be called to output the content for this page</li> </ul> <p>This line hooks the <code>register_settings</code> function on to the <code>admin_init</code> action:</p> <pre><code>add_action( 'admin_init', array( $this, 'register_settings' ) );</code></pre> <p><code>array( $this, 'register_settings' )</code> means to call <code>register_settings</code> on <code>$this</code>, which points to our <code>SettingsPage</code> instance.</p> <p>The <code>register_settings</code> then calls WP’s <code>register_setting</code> to register a setting:</p> <pre><code>public function register_settings() { register_setting( $this->settings_page_properties['option_group'], $this->settings_page_properties['option_name'] ); }</code></pre> <p>Function <code>render_settings_page</code> is responsible for rendering the page:</p> <pre><code>public function render_settings_page() { $option_name = $this->settings_page_properties['option_name']; $option_group = $this->settings_page_properties['option_group']; $settings_data = $this->get_settings_data(); ?> <div class="wrap"> <h2>Simplarity</h2> <p>This plugin is using the settings API.</p> <form method="post" action="options.php"> <?php settings_fields( $option_group ); ?> <table class="form-table"> <tr> <th><label for="textbox">Textbox:</label></th> <td> <input type="text" id="textbox" name="<?php echo esc_attr( $option_name."[textbox]" ); ?>" value="<?php echo esc_attr( $settings_data['textbox'] ); ?>" /> </td> </tr> </table> <input type="submit" name="submit" id="submit" class="button button-primary" value="Save Options"> </form> </div> <?php }</code></pre> <p>We hooked <code>render_settings_page</code> earlier using <code>add_submenu_page</code>.</p> <p>Function <code>get_settings_data</code> is a wrapper function for <code>get_option</code>:</p> <pre><code>public function get_settings_data(){ return get_option( $this->plugin['settings_page_properties']['option_name'] ); }</code></pre> <p>This is to easily get the settings data with a single function call.</p> <p>Function <code>get_default_settings_data</code> is use to supply us with our own default values:</p> <pre><code>public function get_default_settings_data() { $defaults = array(); $defaults['textbox'] = ''; return $defaults; }</code></pre> <h3>Abstracting Our Settings Page Class</h3> <p>Right now our settings page class cannot be reused if you want to create another subpage. Let’s move the reusable code for the settings page to another class.</p> <p>Let’s call this class <code>Simplarity_WpSubPage</code>. Go ahead and create the file <i>src/Simplarity/WpSubPage.php</i>.</p> <p>Now add the code below:</p> <pre><code><?php abstract class Simplarity_WpSubPage { protected $settings_page_properties; public function __construct( $settings_page_properties ){ $this->settings_page_properties = $settings_page_properties; } public function run() { add_action( 'admin_menu', array( $this, 'add_menu_and_page' ) ); add_action( 'admin_init', array( $this, 'register_settings' ) ); } public function add_menu_and_page() { add_submenu_page( $this->settings_page_properties['parent_slug'], $this->settings_page_properties['page_title'], $this->settings_page_properties['menu_title'], $this->settings_page_properties['capability'], $this->settings_page_properties['menu_slug'], array( $this, 'render_settings_page' ) ); } public function register_settings() { register_setting( $this->settings_page_properties['option_group'], $this->settings_page_properties['option_name'] ); } public function get_settings_data(){ return get_option( $this->settings_page_properties['option_name'], $this->get_default_settings_data() ); } public function render_settings_page(){ } public function get_default_settings_data() { $defaults = array(); return $defaults; } } </code></pre> <p>Notice that it is an abstract class. This will prevent intantiating this class directly. To use it you need to extend it first with another class, which in our case is <code>Simplarity_SettingsPage</code>:</p> <pre><code><?php class Simplarity_SettingsPage extends Simplarity_WpSubPage { public function render_settings_page() { $option_name = $this->settings_page_properties['option_name']; $option_group = $this->settings_page_properties['option_group']; $settings_data = $this->get_settings_data(); ?> <div class="wrap"> <h2>Simplarity</h2> <p>This plugin is using the settings API.</p> <form method="post" action="options.php"> <?php settings_fields( $option_group ); ?> <table class="form-table"> <tr> <th><label for="textbox">Textbox:</label></th> <td> <input type="text" id="textbox" name="<?php echo esc_attr( $option_name."[textbox]" ); ?>" value="<?php echo esc_attr( $settings_data['textbox'] ); ?>" /> </td> </tr> </table> <input type="submit" name="submit" id="submit" class="button button-primary" value="Save Options"> </form> </div> <?php } public function get_default_settings_data() { $defaults = array(); defaults['textbox'] = ''; return $defaults; } } </code></pre> <p>The only functions we have implemented are <code>render_settings_page</code> and <code>get_default_settings_data</code>, which are customized to this settings page.</p> <p>To create another WP settings page you’ll just need to create a class and extend the <code>Simplarity_WpSubPage</code>. And implement your own <code>render_settings_page</code> and <code>get_default_settings_data</code>.</p> <h3>Defining A Service</h3> <p>The power of the plugin container is in defining services. A service is a function that contains instantiation and initialization code that will return an object. Whenever we pull a service from our container, the service function is called and will create the object for you. The object is only created when needed. This is called lazy initialization.</p> <p>To better illustrate this, let’s define a service for our settings page.</p> <p>Open <i>simplarity.php</i> and add this function below the Simplarity code:</p> <pre><code>function simplarity_service_settings( $plugin ){ $object = new Simplarity_SettingsPage( $plugin['settings_page_properties'] ); return $object; }</code></pre> <p>Notice that our service function has a <code>$plugin</code> parameter which contains our plugin container. This allows us to access all configuration, objects, and services that have been stored in our plugin container. We can see that the <code>Simplarity_SettingsPage</code> has a dependency on <code>$plugin['settings_page_properties']</code>. We inject this dependency to <code>Simplarity_SettingsPage</code> here. This is an example of dependency injection. Dependency injection is a practice where objects are designed in a manner where they receive instances of the objects from other pieces of code, instead of constructing them internally. This improves decoupling of code.</p> <p>Now let’s replace this line in <code>simplarity_init</code>:</p> <pre><code>$plugin['settings_page'] = new Simplarity_SettingsPage( $plugin['settings_page_properties'] );</code></pre> <p>with a service definition assignment:</p> <pre><code>$plugin['settings_page'] = 'simplarity_service_settings'</code></pre> <p>So instead of assigning our object instance directly, we assign the name of our function as string. Our container handles the rest. </p> <h3>Defining A Shared Service</h3> <p>Right now, every time we get <code>$plugin['settings_page']</code>, a new instance of <code>Simplarity_SettingsPage</code> is returned. Ideally, <code>Simplarity_SettingsPage</code> should only be instantiated once as we are using WP hooks, which in turn should only be registered once.</p> <p>To solve this we use a shared service. A shared service will return a new instance of an object on first call, on succeeding calls it will return the same instance.</p> <p>Let’s create a shared service using a static variable:</p> <pre><code>function simplarity_service_settings( $plugin ){ static $object; if (null !== $object) { return $object; } $object = new Simplarity_SettingsPage( $plugin['settings_page_properties'] ); return $object; }</code></pre> <p>On first call, <code>$object</code> is null, and on succeeding calls it will contain the instance of the object created on first call. Notice that we are using a static variable. A static variable exists only in a local function scope, but it does not lose its value when program execution leaves this scope.</p> <p>That’s it.</p> <p>Now if you activate the plugin, an admin menu will appear in Admin ? Settings named “Simplarity”. Click on it and you will be taken to the settings page we have created.</p> <figure><sup><a class="colorbox" href="http://www.smashingmagazine.com/#14">14</a></sup><br /><figcaption>Settings Page In Action</figcaption></figure> <h3>The Future: PHP 5.3+</h3> <p>Earlier we mentioned that our class naming convention was future-ready. In this section we will discuss how our codebase will work in PHP version 5.3 and up. Two of the best features that have graced the PHP world are namespaces and anonymous functions.</p> <h4>Namespaces</h4> <p>PHP does not allow two classes or functions to share the same name. When this happens, a name collision occurs and causes a nasty error.</p> <p>With namespaces you can have the same class names as long as they live in their own namespace. A good analogy for namespaces are the folders you have in your OS. You cannot have files with the same name in one folder. However, you can have the same filenames in different folders.</p> <p>With namespaces, class and function names won’t need unique prefixes anymore.</p> <h4>Anonymous Functions</h4> <p>Anonymous functions, also known as <strong>closures</strong>, allow the creation of functions which have no specified name. They are most useful as the value of callback parameters, but they have many other uses. You can also store closures in variables.</p> <p>Here’s an example of closure:</p> <pre><code><?php $greet = function($name) { printf("Hello %srn", $name); }; $greet('World'); $greet('PHP');</code></pre> <h3>Using Namespaces In Classes</h3> <p>Let’s go ahead and use namespaces in our class definitions. Open up the following files in <i>src/Simplarity:</i></p> <ul> <li><i>Plugin.php</i></li> <li><i>SettingsPage.php</i></li> <li><i>WpSubPage.php</i></li> </ul> <p>In each of these files, add a namespace declaration on top and remove the “Simplarity_” prefix on class names:</p> <pre><code>// Plugin.php namespace Simplarity; class Plugin { ... // SettingsPage.php namespace Simplarity; class SettingsPage extends WpSubPage { ... // WpSubPage.php namespace Simplarity; abstract class WpSubPage { ... </code></pre> <p>Since we have updated our class names we also need to update our class instantiations in <i>simplarity.php</i>. We do this by deleting the prefixes:</p> <pre><code>function simplarity_init() { $plugin = new Plugin(); ... } ... function simplarity_service_settings( $plugin ){ ... $object = new SettingsPage( $plugin['settings_page_properties'] ); return $object; } </code></pre> <p>By default, PHP will try to load the class from the root namespace so we need to tell it about our namespaced classes. We add this to the top of <i>simplarity.php</i> just above the autoloader code:</p> <pre><code>use SimplarityPlugin; use SimplaritySettingsPage; </code></pre> <p>This is called importing/aliasing with the <a class="colorbox" href="http://php.net/manual/en/language.namespaces.importing.php">use operator</a><sup><a class="colorbox" href="http://www.smashingmagazine.com/#15">15</a></sup>.</p> <h3>Updating The Autoloader</h3> <p>Open up <i>simplarity.php</i> and change this line in the autoloader from:</p> <pre><code>$class_file = str_replace( '_', DIRECTORY_SEPARATOR, $class_name ) . '.php';</code></pre> <p>to:</p> <pre><code>$class_file = str_replace( '', DIRECTORY_SEPARATOR, $class_name ) . '.php';</code></pre> <p>Remember that in 5.2 code we are using underscores as hierarchy separators. For 5.3+ we are using namespaces which use backslash “” as hierarchy separators. Thus we simply swap “_” for “”. We use another backslash to escape the original one: “”.</p> <h3>Updating Our Service Definitions To Use Anonymous Functions</h3> <p>We can now replace the global functions we created for our service definitions with anonymous functions. So instead of doing this:</p> <pre><code>function simplarity_init() { ... $plugin['settings_page'] = 'simplarity_service_settings'; ... } ... function simplarity_service_settings( $plugin ){ static $object; if (null !== $object) { return $object; } $object = new Simplarity_SettingsPage( $plugin['settings_page_properties'] ); return $object; }</code></pre> <p>we can just replace this with an inline anonymous function:</p> <pre><code>function simplarity_init() { $plugin = new Plugin(); ... $plugin['settings_page'] = function ( $plugin ) { static $object; if (null !== $object) { return $object; } return new SettingsPage( $plugin['settings_page_properties'] ); }; ... } </code></pre> <h3>Using Pimple As A Plugin Container</h3> <p>Pimple is a small dependency injection (DI) container for PHP 5.3+. Pimple has the same syntax as our simple plugin container. In fact our plugin container was inspired by Pimple. In this part, we will extend Pimple and use it.</p> <p>Download <a class="colorbox" href="https://raw.githubusercontent.com/silexphp/Pimple/master/src/Pimple/Container.php">Pimple container from GitHub</a><sup><a class="colorbox" href="http://www.smashingmagazine.com/#16">16</a></sup> and save it in <i>src/Simplarity/Pimple.php</i>.</p> <p>Open up <i>Pimple.php</i> and replace the namespace and the classname to:</p> <pre><code>... namespace Simplarity; /** * Container main class. * * @author Fabien Potencier */ class Pimple implements ArrayAccess ... </code></pre> <p>Open up <i>Plugin.php</i> and replace all the code with:</p> <pre><code><?php namespace Simplarity; class Plugin extends Pimple { public function run(){ foreach( $this->values as $key => $content ){ // Loop on contents $content = $this[$key]; if( is_object( $content ) ){ $reflection = new ReflectionClass( $content ); if( $reflection->hasMethod( 'run' ) ){ $content->run(); // Call run method on object } } } } } </code></pre> <p>Now let’s change the service definition in <i>simplarity.php</i> to:</p> <pre><code>$plugin['settings_page'] = function ( $plugin ) { return new SettingsPage( $plugin['settings_page_properties'] ); }; </code></pre> <p>By default, each time you get a service, Pimple returns the same instance of it. If you want a different instance to be returned for all calls, wrap your anonymous function with the <code>factory()</code> method:</p> <pre><code>$plugin['image_resizer'] = $plugin->factory(function ( $plugin ) { return new ImageResizer( $plugin['image_dir'] ); }); </code></pre> <h3>Conclusion</h3> <p>The PHP community is big. A lot of best practices have been learned over the years. It’s good to always look beyond the walled garden of WordPress to look for answers. With autoloading and a plugin container we are one step closer to better code.</p> <h4>Code Samples</h4> <ul> <li><a class="colorbox" href="https://github.com/kosinix/simplarity">Simplarity: settings page</a><sup><a class="colorbox" href="http://www.smashingmagazine.com/#17">17</a></sup></li> <li><a class="colorbox" href="https://github.com/kosinix/simplarity-php53">Simplarity: settings page (PHP5.3+)</a><sup><a class="colorbox" href="http://www.smashingmagazine.com/#18">18</a></sup></li> </ul> <h4>Resources</h4> <ul> <li><a class="colorbox" href="http://pear.php.net/manual/en/standards.naming.php">PEAR naming standards</a><sup><a class="colorbox" href="http://www.smashingmagazine.com/#19">19</a></sup></li> <li><a class="colorbox" href="http://daylerees.com/php-namespaces-explained">Namespaces explanation</a><sup><a class="colorbox" href="http://www.smashingmagazine.com/#20">20</a></sup></li> <li><a class="colorbox" href="https://github.com/silexphp/Pimple">Pimple (a small DI container)</a><sup><a class="colorbox" href="http://www.smashingmagazine.com/#21">21</a></sup></li> </ul> <p><em>(dp, og, il)</em></p> <h4>Footnotes</h4> <ol> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-1">1 https://shop.smashingmagazine.com/products/wordpress-maintenance-keeping-your-website-safe-and-efficient</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-2">2 http://codex.wordpress.org/Writing_a_Plugin</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-3">3 http://php.net/manual/en/function.require-once.php</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-4">4 http://pear.php.net/manual/en/standards.naming.php</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-5">5 https://make.wordpress.org/core/handbook/coding-standards/php/#naming-conventions</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-6">6 http://php.net/manual/en/function.spl-autoload-register.php</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-7">7 http://codex.wordpress.org/Function_Reference/plugin_dir_path</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-8">8 http://php.net/manual/en/language.constants.predefined.php</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-9">9 http://php.net/manual/en/function.realpath.php</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-10">10 http://codex.wordpress.org/Settings_API</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-11">11 http://codex.wordpress.org/Plugin_API/Filter_Reference</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-12">12 http://codex.wordpress.org/Plugin_API/Hooks</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-13">13 http://codex.wordpress.org/add_submenu_page</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-14">14 http://www.kosinix.com/wp-content/uploads/2015/02/simplarity1.png</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-15">15 http://php.net/manual/en/language.namespaces.importing.php</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-16">16 https://raw.githubusercontent.com/silexphp/Pimple/master/src/Pimple/Container.php</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-17">17 https://github.com/kosinix/simplarity</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-18">18 https://github.com/kosinix/simplarity-php53</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-19">19 http://pear.php.net/manual/en/standards.naming.php</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-20">20 http://daylerees.com/php-namespaces-explained</a></li> <li><a class="colorbox" href="http://www.smashingmagazine.com/#note-21">21 https://github.com/silexphp/Pimple</a></li> </ol> <p>The post <a class="colorbox" rel="nofollow" href="http://www.smashingmagazine.com/2015/05/29/how-to-use-autoloading-and-a-plugin-container-in-wordpress-plugins/">How To Use Autoloading And A Plugin Container In WordPress Plugins</a> appeared first on <a class="colorbox" rel="nofollow" href="http://www.smashingmagazine.com/">Smashing Magazine</a>.</p> <div class="fixed"></div> </div> <div class="under"> <span class="categories">Categories: </span><span><a href="http://www.webmastersgallery.com/category/uncategorized/" rel="category tag">Others</a></span> <span class="tags">Tags: </span><span></span> </div> </div> <!-- related posts START --> <!-- related posts END --> <script type="text/javascript" src="http://www.webmastersgallery.com/wp-content/themes/inove/js/comment.js"></script> <div id="comments"> <div id="cmtswitcher"> <a id="commenttab" class="curtab" href="javascript:void(0);" onclick="MGJS.switchTab('thecomments,commentnavi', 'thetrackbacks', 'commenttab', 'curtab', 'trackbacktab', 'tab');">Comments (0)</a> <a id="trackbacktab" class="tab" href="javascript:void(0);" onclick="MGJS.switchTab('thetrackbacks', 'thecomments,commentnavi', 'trackbacktab', 'curtab', 'commenttab', 'tab');">Trackbacks (0)</a> <span class="addcomment"><a href="#respond">Leave a comment</a></span> <span class="addtrackback"><a href="http://www.webmastersgallery.com/2015/05/29/how-to-use-autoloading-and-a-plugin-container-in-wordpress-plugins/trackback/">Trackback</a></span> <div class="fixed"></div> </div> <div id="commentlist"> <!-- comments START --> <ol id="thecomments"> <li class="messagebox"> No comments yet. </li> </ol> <!-- comments END --> <!-- trackbacks START --> <ol id="thetrackbacks"> <li class="messagebox"> No trackbacks yet. </li> </ol> <div class="fixed"></div> <!-- trackbacks END --> </div> </div> <div id="comment_login" class="messagebox"> You must be <a href="http://www.webmastersgallery.com/wp-login.php">logged in</a> to post a comment. </div> <div id="postnavi"> <span class="prev"><a href="http://www.webmastersgallery.com/2015/05/30/lining-js-controlling-text-lines-per-css-selector/" rel="next">Lining.js: Controlling Text Lines per CSS Selector</a></span> <span class="next"><a href="http://www.webmastersgallery.com/2015/05/29/how-does-googles-new-ranking-factor-mobile-friendly-affect-your-website/" rel="prev">How Does Google’s New Ranking Factor Mobile-Friendly Affect Your Website?</a></span> <div class="fixed"></div> </div> </div> <!-- main END --> <!-- sidebar START --> <div id="sidebar"> <!-- sidebar north START --> <div id="northsidebar" class="sidebar"> <!-- feeds --> <div class="widget widget_feeds"> <div class="content"> <div id="subscribe"> <a rel="external nofollow" id="feedrss" title="Subscribe to this blog..." href="http://www.webmastersgallery.com/feed/"><abbr title="Really Simple Syndication">RSS</abbr></a> </div> <div class="fixed"></div> </div> </div> <!-- showcase --> <div id="text-389627311" class="widget widget_text"> <div class="textwidget"><a href="http://feeds2.feedburner.com/webmastersgallery" title="Subscribe to my feed" rel="alternate" type="application/rss+xml"><img src="http://www.feedburner.com/fb/images/pub/feed-icon32x32.png" alt="" style="border:0"/></a><a href="http://feeds2.feedburner.com/webmastersgallery" title="Subscribe to my feed" rel="alternate" type="application/rss+xml">Subscribe for latest Updates</a></div> </div><div id="text-389629461" class="widget widget_text"> <div class="textwidget"><form style="border:1px solid #ccc;padding:3px;text-align:center;" action="http://feedburner.google.com/fb/a/mailverify" method="post" target="popupwindow" onsubmit="window.open('http://feedburner.google.com/fb/a/mailverify?uri=webmastersgallery', 'popupwindow', 'scrollbars=yes,width=550,height=520');return true"><p>Enter your email address:</p><p><input type="text" style="width:140px" name="email"/></p><input type="hidden" value="webmastersgallery" name="uri"/><input type="hidden" name="loc" value="en_US"/><input type="submit" value="Subscribe" /><p>Delivered by <a href="http://feedburner.google.com" target="_blank" rel="noopener">FeedBurner</a></p></form></div> </div></div> <!-- sidebar north END --> <div id="centersidebar"> <!-- sidebar east START --> <div id="eastsidebar" class="sidebar"> <!-- categories --> <div class="widget widget_categories"> <h3>Categories</h3> <ul> <li class="cat-item cat-item-518"><a href="http://www.webmastersgallery.com/category/affiliate-programs/">Affiliate Programs</a> </li> <li class="cat-item cat-item-147"><a href="http://www.webmastersgallery.com/category/design/">Designing</a> </li> <li class="cat-item cat-item-519"><a href="http://www.webmastersgallery.com/category/domain-names/">Domain Names</a> </li> <li class="cat-item cat-item-37"><a href="http://www.webmastersgallery.com/category/e-commerce/">E-commerce</a> </li> <li class="cat-item cat-item-509"><a href="http://www.webmastersgallery.com/category/internet-directories/">Internet Directories</a> </li> <li class="cat-item cat-item-510"><a href="http://www.webmastersgallery.com/category/message-boards/">Message Boards</a> </li> <li class="cat-item cat-item-1"><a href="http://www.webmastersgallery.com/category/uncategorized/">Others</a> </li> <li class="cat-item cat-item-506"><a href="http://www.webmastersgallery.com/category/programming/">Programming</a> </li> <li class="cat-item cat-item-511"><a href="http://www.webmastersgallery.com/category/promotion-and-marketing/">Promotion and Marketing</a> </li> <li class="cat-item cat-item-534"><a href="http://www.webmastersgallery.com/category/scripts-and-programming/">Scripts and Programming</a> </li> <li class="cat-item cat-item-513"><a href="http://www.webmastersgallery.com/category/search-engines/">Search Engines</a> </li> <li class="cat-item cat-item-135"><a href="http://www.webmastersgallery.com/category/social-media/">Social Media</a> </li> <li class="cat-item cat-item-514"><a href="http://www.webmastersgallery.com/category/softwares/">Softwares</a> </li> <li class="cat-item cat-item-515"><a href="http://www.webmastersgallery.com/category/tips-and-tutorials/">Tips and Tutorials</a> </li> <li class="cat-item cat-item-338"><a href="http://www.webmastersgallery.com/category/web-hosting/">Web Hosting</a> </li> <li class="cat-item cat-item-516"><a href="http://www.webmastersgallery.com/category/webmaster-tools/">Webmaster Tools</a> </li> <li class="cat-item cat-item-501"><a href="http://www.webmastersgallery.com/category/webmaster-resources/">Webmasters Resources</a> </li> <li class="cat-item cat-item-3"><a href="http://www.webmastersgallery.com/category/web-design/">Website Design</a> </li> </ul> </div> </div> <!-- sidebar east END --> <!-- sidebar west START --> <div id="westsidebar" class="sidebar"> <!-- blogroll --> <div class="widget widget_links"> <h3>Blogroll</h3> <ul> <li><a href="http://wordpress.org/development/">Development Blog</a></li> <li><a href="http://codex.wordpress.org/">Documentation</a></li> <li><a href="http://wordpress.org/extend/plugins/">Plugins</a></li> <li><a href="http://wordpress.org/extend/ideas/">Suggest Ideas</a></li> <li><a href="http://wordpress.org/support/">Support Forum</a></li> <li><a href="http://wordpress.org/extend/themes/">Themes</a></li> <li><a href="http://planet.wordpress.org/">WordPress Planet</a></li> </ul> </div> </div> <!-- sidebar west END --> <div class="fixed"></div> </div> <!-- sidebar south START --> <div id="southsidebar" class="sidebar"> <!-- archives --> <div class="widget"> <h3>Archives</h3> <ul> <li><a href='http://www.webmastersgallery.com/2024/11/'>November 2024</a></li> <li><a href='http://www.webmastersgallery.com/2024/10/'>October 2024</a></li> <li><a href='http://www.webmastersgallery.com/2024/09/'>September 2024</a></li> <li><a href='http://www.webmastersgallery.com/2024/08/'>August 2024</a></li> <li><a href='http://www.webmastersgallery.com/2024/07/'>July 2024</a></li> <li><a href='http://www.webmastersgallery.com/2024/06/'>June 2024</a></li> <li><a href='http://www.webmastersgallery.com/2024/05/'>May 2024</a></li> <li><a href='http://www.webmastersgallery.com/2024/04/'>April 2024</a></li> <li><a href='http://www.webmastersgallery.com/2024/03/'>March 2024</a></li> <li><a href='http://www.webmastersgallery.com/2024/02/'>February 2024</a></li> <li><a href='http://www.webmastersgallery.com/2024/01/'>January 2024</a></li> <li><a href='http://www.webmastersgallery.com/2023/12/'>December 2023</a></li> <li><a href='http://www.webmastersgallery.com/2023/11/'>November 2023</a></li> <li><a href='http://www.webmastersgallery.com/2023/10/'>October 2023</a></li> <li><a href='http://www.webmastersgallery.com/2023/09/'>September 2023</a></li> <li><a href='http://www.webmastersgallery.com/2023/08/'>August 2023</a></li> <li><a href='http://www.webmastersgallery.com/2023/07/'>July 2023</a></li> <li><a href='http://www.webmastersgallery.com/2023/06/'>June 2023</a></li> <li><a href='http://www.webmastersgallery.com/2023/05/'>May 2023</a></li> <li><a href='http://www.webmastersgallery.com/2023/04/'>April 2023</a></li> <li><a href='http://www.webmastersgallery.com/2023/03/'>March 2023</a></li> <li><a href='http://www.webmastersgallery.com/2023/02/'>February 2023</a></li> <li><a href='http://www.webmastersgallery.com/2023/01/'>January 2023</a></li> <li><a href='http://www.webmastersgallery.com/2022/12/'>December 2022</a></li> <li><a href='http://www.webmastersgallery.com/2022/11/'>November 2022</a></li> <li><a href='http://www.webmastersgallery.com/2022/10/'>October 2022</a></li> <li><a href='http://www.webmastersgallery.com/2022/09/'>September 2022</a></li> <li><a href='http://www.webmastersgallery.com/2022/08/'>August 2022</a></li> <li><a href='http://www.webmastersgallery.com/2022/07/'>July 2022</a></li> <li><a href='http://www.webmastersgallery.com/2022/06/'>June 2022</a></li> <li><a href='http://www.webmastersgallery.com/2022/05/'>May 2022</a></li> <li><a href='http://www.webmastersgallery.com/2022/04/'>April 2022</a></li> <li><a href='http://www.webmastersgallery.com/2022/03/'>March 2022</a></li> <li><a href='http://www.webmastersgallery.com/2022/02/'>February 2022</a></li> <li><a href='http://www.webmastersgallery.com/2022/01/'>January 2022</a></li> <li><a href='http://www.webmastersgallery.com/2021/12/'>December 2021</a></li> <li><a href='http://www.webmastersgallery.com/2021/11/'>November 2021</a></li> <li><a href='http://www.webmastersgallery.com/2021/10/'>October 2021</a></li> <li><a href='http://www.webmastersgallery.com/2021/09/'>September 2021</a></li> <li><a href='http://www.webmastersgallery.com/2021/08/'>August 2021</a></li> <li><a href='http://www.webmastersgallery.com/2021/07/'>July 2021</a></li> <li><a href='http://www.webmastersgallery.com/2021/06/'>June 2021</a></li> <li><a href='http://www.webmastersgallery.com/2021/05/'>May 2021</a></li> <li><a href='http://www.webmastersgallery.com/2021/04/'>April 2021</a></li> <li><a href='http://www.webmastersgallery.com/2021/03/'>March 2021</a></li> <li><a href='http://www.webmastersgallery.com/2021/02/'>February 2021</a></li> <li><a href='http://www.webmastersgallery.com/2021/01/'>January 2021</a></li> <li><a href='http://www.webmastersgallery.com/2020/12/'>December 2020</a></li> <li><a href='http://www.webmastersgallery.com/2020/11/'>November 2020</a></li> <li><a href='http://www.webmastersgallery.com/2020/10/'>October 2020</a></li> <li><a href='http://www.webmastersgallery.com/2020/09/'>September 2020</a></li> <li><a href='http://www.webmastersgallery.com/2020/08/'>August 2020</a></li> <li><a href='http://www.webmastersgallery.com/2020/07/'>July 2020</a></li> <li><a href='http://www.webmastersgallery.com/2020/06/'>June 2020</a></li> <li><a href='http://www.webmastersgallery.com/2020/05/'>May 2020</a></li> <li><a href='http://www.webmastersgallery.com/2020/04/'>April 2020</a></li> <li><a href='http://www.webmastersgallery.com/2020/03/'>March 2020</a></li> <li><a href='http://www.webmastersgallery.com/2020/02/'>February 2020</a></li> <li><a href='http://www.webmastersgallery.com/2020/01/'>January 2020</a></li> <li><a href='http://www.webmastersgallery.com/2019/12/'>December 2019</a></li> <li><a href='http://www.webmastersgallery.com/2019/11/'>November 2019</a></li> <li><a href='http://www.webmastersgallery.com/2019/10/'>October 2019</a></li> <li><a href='http://www.webmastersgallery.com/2019/09/'>September 2019</a></li> <li><a href='http://www.webmastersgallery.com/2019/08/'>August 2019</a></li> <li><a href='http://www.webmastersgallery.com/2019/07/'>July 2019</a></li> <li><a href='http://www.webmastersgallery.com/2019/06/'>June 2019</a></li> <li><a href='http://www.webmastersgallery.com/2019/05/'>May 2019</a></li> <li><a href='http://www.webmastersgallery.com/2019/04/'>April 2019</a></li> <li><a href='http://www.webmastersgallery.com/2019/03/'>March 2019</a></li> <li><a href='http://www.webmastersgallery.com/2019/02/'>February 2019</a></li> <li><a href='http://www.webmastersgallery.com/2019/01/'>January 2019</a></li> <li><a href='http://www.webmastersgallery.com/2018/12/'>December 2018</a></li> <li><a href='http://www.webmastersgallery.com/2018/11/'>November 2018</a></li> <li><a href='http://www.webmastersgallery.com/2018/10/'>October 2018</a></li> <li><a href='http://www.webmastersgallery.com/2018/09/'>September 2018</a></li> <li><a href='http://www.webmastersgallery.com/2018/08/'>August 2018</a></li> <li><a href='http://www.webmastersgallery.com/2018/07/'>July 2018</a></li> <li><a href='http://www.webmastersgallery.com/2018/04/'>April 2018</a></li> <li><a href='http://www.webmastersgallery.com/2018/01/'>January 2018</a></li> <li><a href='http://www.webmastersgallery.com/2017/12/'>December 2017</a></li> <li><a href='http://www.webmastersgallery.com/2017/11/'>November 2017</a></li> <li><a href='http://www.webmastersgallery.com/2017/09/'>September 2017</a></li> <li><a href='http://www.webmastersgallery.com/2017/08/'>August 2017</a></li> <li><a href='http://www.webmastersgallery.com/2017/07/'>July 2017</a></li> <li><a href='http://www.webmastersgallery.com/2017/06/'>June 2017</a></li> <li><a href='http://www.webmastersgallery.com/2017/05/'>May 2017</a></li> <li><a href='http://www.webmastersgallery.com/2017/04/'>April 2017</a></li> <li><a href='http://www.webmastersgallery.com/2017/03/'>March 2017</a></li> <li><a href='http://www.webmastersgallery.com/2017/02/'>February 2017</a></li> <li><a href='http://www.webmastersgallery.com/2017/01/'>January 2017</a></li> <li><a href='http://www.webmastersgallery.com/2016/12/'>December 2016</a></li> <li><a href='http://www.webmastersgallery.com/2016/11/'>November 2016</a></li> <li><a href='http://www.webmastersgallery.com/2016/10/'>October 2016</a></li> <li><a href='http://www.webmastersgallery.com/2016/09/'>September 2016</a></li> <li><a href='http://www.webmastersgallery.com/2016/08/'>August 2016</a></li> <li><a href='http://www.webmastersgallery.com/2016/07/'>July 2016</a></li> <li><a href='http://www.webmastersgallery.com/2016/06/'>June 2016</a></li> <li><a href='http://www.webmastersgallery.com/2016/05/'>May 2016</a></li> <li><a href='http://www.webmastersgallery.com/2016/04/'>April 2016</a></li> <li><a href='http://www.webmastersgallery.com/2016/03/'>March 2016</a></li> <li><a href='http://www.webmastersgallery.com/2016/02/'>February 2016</a></li> <li><a href='http://www.webmastersgallery.com/2016/01/'>January 2016</a></li> <li><a href='http://www.webmastersgallery.com/2015/12/'>December 2015</a></li> <li><a href='http://www.webmastersgallery.com/2015/11/'>November 2015</a></li> <li><a href='http://www.webmastersgallery.com/2015/10/'>October 2015</a></li> <li><a href='http://www.webmastersgallery.com/2015/09/'>September 2015</a></li> <li><a href='http://www.webmastersgallery.com/2015/08/'>August 2015</a></li> <li><a href='http://www.webmastersgallery.com/2015/07/'>July 2015</a></li> <li><a href='http://www.webmastersgallery.com/2015/06/'>June 2015</a></li> <li><a href='http://www.webmastersgallery.com/2015/05/'>May 2015</a></li> <li><a href='http://www.webmastersgallery.com/2015/04/'>April 2015</a></li> <li><a href='http://www.webmastersgallery.com/2015/03/'>March 2015</a></li> <li><a href='http://www.webmastersgallery.com/2015/02/'>February 2015</a></li> <li><a href='http://www.webmastersgallery.com/2015/01/'>January 2015</a></li> <li><a href='http://www.webmastersgallery.com/2014/12/'>December 2014</a></li> <li><a href='http://www.webmastersgallery.com/2014/11/'>November 2014</a></li> <li><a href='http://www.webmastersgallery.com/2014/10/'>October 2014</a></li> <li><a href='http://www.webmastersgallery.com/2014/09/'>September 2014</a></li> <li><a href='http://www.webmastersgallery.com/2014/08/'>August 2014</a></li> <li><a href='http://www.webmastersgallery.com/2014/07/'>July 2014</a></li> <li><a href='http://www.webmastersgallery.com/2014/06/'>June 2014</a></li> <li><a href='http://www.webmastersgallery.com/2013/07/'>July 2013</a></li> <li><a href='http://www.webmastersgallery.com/2013/01/'>January 2013</a></li> <li><a href='http://www.webmastersgallery.com/2012/12/'>December 2012</a></li> <li><a href='http://www.webmastersgallery.com/2012/08/'>August 2012</a></li> <li><a href='http://www.webmastersgallery.com/2012/07/'>July 2012</a></li> <li><a href='http://www.webmastersgallery.com/2012/06/'>June 2012</a></li> <li><a href='http://www.webmastersgallery.com/2012/05/'>May 2012</a></li> <li><a href='http://www.webmastersgallery.com/2012/04/'>April 2012</a></li> <li><a href='http://www.webmastersgallery.com/2012/01/'>January 2012</a></li> <li><a href='http://www.webmastersgallery.com/2011/11/'>November 2011</a></li> <li><a href='http://www.webmastersgallery.com/2011/06/'>June 2011</a></li> <li><a href='http://www.webmastersgallery.com/2011/03/'>March 2011</a></li> <li><a href='http://www.webmastersgallery.com/2011/02/'>February 2011</a></li> <li><a href='http://www.webmastersgallery.com/2011/01/'>January 2011</a></li> <li><a href='http://www.webmastersgallery.com/2010/12/'>December 2010</a></li> <li><a href='http://www.webmastersgallery.com/2010/11/'>November 2010</a></li> <li><a href='http://www.webmastersgallery.com/2010/09/'>September 2010</a></li> <li><a href='http://www.webmastersgallery.com/2010/07/'>July 2010</a></li> <li><a href='http://www.webmastersgallery.com/2010/06/'>June 2010</a></li> <li><a href='http://www.webmastersgallery.com/2010/05/'>May 2010</a></li> <li><a href='http://www.webmastersgallery.com/2010/02/'>February 2010</a></li> <li><a href='http://www.webmastersgallery.com/2009/12/'>December 2009</a></li> <li><a href='http://www.webmastersgallery.com/2009/08/'>August 2009</a></li> <li><a href='http://www.webmastersgallery.com/2009/07/'>July 2009</a></li> <li><a href='http://www.webmastersgallery.com/2009/06/'>June 2009</a></li> <li><a href='http://www.webmastersgallery.com/2009/05/'>May 2009</a></li> <li><a href='http://www.webmastersgallery.com/2009/04/'>April 2009</a></li> <li><a href='http://www.webmastersgallery.com/2009/03/'>March 2009</a></li> </ul> </div> <!-- meta --> <div class="widget"> <h3>Meta</h3> <ul> <li><a href="http://www.webmastersgallery.com/wp-login.php">Log in</a></li> </ul> </div> </div> <!-- sidebar south END --> </div> <!-- sidebar END --> <div class="fixed"></div> </div> <!-- content END --> <!-- footer START --> <div id="footer"> <a id="gotop" href="#" onclick="MGJS.goTop();return false;">Top</a> <a id="powered" href="http://wordpress.org/">WordPress</a> <div id="copyright"> Copyright © 2009-2024 Webmasters Gallery </div> <div id="themeinfo"> Theme by <a href="http://www.neoease.com/">NeoEase</a>. Valid <a href="http://validator.w3.org/check?uri=referer">XHTML 1.1</a> and <a href="http://jigsaw.w3.org/css-validator/check/referer?profile=css3">CSS 3</a>. </div> </div> <!-- footer END --> </div> <!-- container END --> </div> <!-- wrap END --> </body> </html>