A basic plugin for MultilingualPress

Plugins for MultilingualPress should wait until MultilingualPress is actually loaded. That means they should use a hook provided by the main plugin to be sure that MultilingualPress is running and ready to be used. There are three main entry hooks:

  1. inpsyde_mlp_init: equals to plugins_loaded, 0 plus MultilingualPress loaded. Happens before scripts and stylesheets are registered. The only parameter is an instance of the class Inpsyde_Property_List which holds useful plugin information.
  2. inpsyde_mlp_loaded: Almost the same, but after the scripts are registered.
  3. mlp_and_wp_loaded: equals to wp_loaded, 0 plus MultilingualPress loaded. The first parameter is again the same Inpsyde_Property_List. When in doubt use this action.
    wp_loaded happens after WordPress has checked if the current blog was marked as spam. Unless you want to run your code on spam blogs, wait for this action.

When we use type hints in our callback function, we should declare the dependency for the first parameter against the interface Inpsyde_Property_List_Interface, not against a concrete class.

First example

(All code examples here require at least PHP 5.3.)

add_action(
    'mlp_and_wp_loaded',
    function( Inpsyde_Property_List_Interface $mlp_data ) {
    // your code here
});

The class Inpsyde_Property_List

This class is very similar to a stdClass from the PHP core: just some key-value pairs. There are two important differences: an Inpsyde_Property_List can be made immutable, and it can have a parent class.

To make an instance of this class immutable, we call its method freeze(). Once that is done, there is no way to change any data of this class. Any attempt to do that will return an instance of WP_Error. We can test that with the method is_frozen() which returns true or false, depending on the current state.
The instance we get from MultilingualPress is always frozen. We can rely on its data, because no matter how many plugins access it, they cannot change it.

What should we do if we want an instance with almost the same data, but some of them with different values? We create a new instance and use the existing one as its parent. Now we can change the values for our internal use and use the other values as if they were properties of our new instance.

Usage example for Inpsyde_Property_List

add_action(
    'mlp_and_wp_loaded',
    function( Inpsyde_Property_List_Interface $mlp_data ) {

        $ours = new Inpsyde_Property_List;
        $ours->set_parent( $mlp_data );

        $ours->plugin_url = plugins_url( '/', __FILE__ );
        $ours->css_url    = "{$ours->plugin_url}css/";
        $ours->js_url     = "{$ours->plugin_url}js/";
        $ours->image_url  = "{$ours->plugin_url}images/";

        $ours->freeze();
});

This approach avoids common problems with classic inheritance, like the fragile base class and statically linked dependencies. We could call it delayed inheritance. The concept is explained in Steve Yegge’s article The Universal Design Pattern.

We will look at the various default properties from MultilingualPress in a separate article. For custom code, we need just one important property:

The autoloader

The property loader holds an instance of the class Inpsyde_Autoload, a very simple Collection Pipeline for instances of the Inpsyde_Autoload_Rule_Interface which handle the actual class loading. Sounds complicated, but it is dead simple.

Let’s say our plugin is organized like this:

- plugin.php // the main file
- css/ // stylesheets
- js/ // scripts
- php/ // PHP classes
    - Plugin_Name_Controller.php
    - Plugin_Name_Model.php
    - Plugin_Name_View.php

Now we want set up an autoloader to get our classes when we need them. There is one existing class for that, we can reuse it to create a new auto-load rule: Inpsyde_Directory_Load. It takes a path to a directory, and we pass its instance to the Inpsyde_Autoload instance provided by $mlp_data->loader.

Usage example for Inpsyde_Autoload

add_action(
    'mlp_and_wp_loaded',
    function( Inpsyde_Property_List_Interface $mlp_data ) {

        $load_rule = new Inpsyde_Directory_Load( __DIR__ . '/php' );
        $mlp_data->loader->add_rule( $load_rule );

        // We can just use our classes now, no 'require' needed.
        $model      = new Plugin_Name_Model( 'foo' );
        $view       = new Plugin_Name_View( $model );
        $controller = new Plugin_Name_Controller( $model, $view );
        $controller->setup();
});

All we have to take care of is that the class names match the file names. This works for interfaces and traits too. Inpsyde_Directory_Load works with flat directories only, it doesn’t search in child directories. It is very fast, because the complete directory is read just once. There is no separate file_exists() check for every class name.

And that’s all we need for a start: Hook into mlp_and_wp_loaded, register the directory for the auto-loader with the help of the property list – and write awesome code!