Update a Plugin while maintaining back compatibility: general

How can I make updates a public plugin of mine without breaking the plugin for all existing users.

Firstly, I’d like to change the main plugin’s class/namespace, because I’d like it to be similar to other classes in the plugin that it extends.

Read More

So I’d like to change

class some_class(){}

to

class Some_Class(){}

Secondly, the whole class is instantiated and saved in a global variable.

global $some_class;
$some_class = new some_class();

Somewhere on here I saw a good example of how to instantiate a class without a global (can’t find it now of course, #doh). The global can be used by users to add/remove a couple of templates. Is there a way to get rid of the global without completely breaking the themes of people who might be using it to manipulate the templates?

Thirdly, related to above, my main plugin file has gotten really big and I’d like to split it up into pieces for my own sanity. However, if the some_templates() method is in the some_class() is it possible to move it to the front_end_class() without breaking things for users?

For example

To unhook an action in my plugin

function remove_method(){
  global $some_class;
  remove_action('wp_head', array($someclass, 'some_templates'));
}
add_action('init','remove_method');

Can the class structure and global variable setup be adjusted without breaking this. How can I alert people that this has changed? _doing_it_wrong() ?

Finally, I’ve been saving some post meta as “yes” versus “no” in lieu of a boolean. Does it make sense to change this?

Related posts

Leave a Reply

1 comment

  1. Class names are case insensitive in PHP (with one exception), so that should not be a problem.

    Accessing your main plugin instance for a class is possible with a static method get_instance() (example). You don’t have to build a Singleton for that. And it should not break backwards-compatibility.

    If you had public methods in your old code and these methods have been used by third party code, you have to keep the exact signature. So either keep the method and pass the call through to the instance of the new class, or – if there are many such methods – use __call() to catch all these methods in one.

    (Update) Given your example of remove_action() … this is tricky. If you add this callback from another class now, there is no safe way to stay backwards compatible, because you cannot “watch” remove_action() calls.
    You could register the old callback and implement an Observer to be noticed if it has been removed.

    One idea I have been playing with for future projects: separate the public API from the inner logic.

    Example: Create a class Plugin_API and pass all objects from your working code into that class when they are set up. When a method from that API is called, pass that call to the responsible object internally, without exposing your plugin structure to the public.

    Sample code

    add_action( 'wp_loaded', array ( Plugin_Controller::get_instance(), 'plugin_setup' ) );
    
    class Plugin_Controller
    {
        protected static $instance = NULL;
    
        public static function get_instance()
        {
            NULL === self::$instance and self::$instance = new self;
    
            return self::$instance;
        }
    
        public function plugin_setup()
        {
            $api = Plugin_API::get_instance();
            $api->add_object( 'post_type',    new Plugin_Custom_Post_Type );
            $api->add_object( 'taxonomy',     new Plugin_Custom_Taxonomy );
            $api->add_object( 'options_page', new Plugin_Options_Page );
    
            add_action( 'wp_head', array ( $api, 'wp_head' ) );
        }
    }
    
    class Plugin_Custom_Post_Type {}
    class Plugin_Custom_Taxonomy {}
    class Plugin_Options_Page {}
    
    class Plugin_API
    {
        protected static $instance = NULL;
    
        protected $objects;
    
        public static function get_instance()
        {
            NULL === self::$instance and self::$instance = new self;
    
            return self::$instance;
        }
    
        public function add_object( $name, $object )
        {
            $this->objects[ $name ] = $object;
        }
    
        public function wp_head()
        {
            $this->objects['post_type']->wp_head_callback();
            $this->objects['taxonomy']->wp_head_callback();
        }
    }
    

    Now another plugin can remove that action with …

    remove_action( 'wp_head', array ( Plugin_API::get_instance(), 'wp_head' ) );
    

    … and you are still free to change your plugin structure whenever you want.

    This is just an idea, I haven’t used it. But it could be worth a try for a complex plugin with lots of classes. Not sure how it will work with filters, but these are easier to move anyway.

    Updating an unknown number of post meta fields can be expensive. I would not touch that.