0

Hello I am creating a plugin using WordPress, because the scale of this plugin is pretty big I am attempting to use OOP to implement what I need.

The way I am using it is that in the main plugin file I am including all the classes, then when plugins_loaded action is fired I make an instance of that function to fire up the __construct.

All of my classes are extending a helper class so I am adding parent::__construct in all my classes __construct function.

In the helper class I have defined bunch of init actions.

The code example:


// main plugin function code
require_once('include/helper.php');
require_once('includes/database.php');

add_action('plugins_loaded', array('helper', 'get_instance'));
add_action('plugins_loaded', array('database', 'get_instance'));

// end of main plugin function

// helper class code
class helper {
    function __construct() {
        add_action('init', 'function_name_1');
        add_action('init', 'function_name_2');
        add_action('init', 'function_name_3');
        add_action('init', 'function_name_4');
        add_action('init', 'function_name_5');
    )

    private $instance;
    static public function get_instance() {
        if (NULL === self::$instance) {
            self::$instance = new helper();
        }
        return $instance;
    }
}

// one sample class code
// I probably have like 8 or 10 of these classes

class database extends helper {
    function __construct() {
        parent::__construct();
        add_action('init', 'inner_function_name_1');
    }
    private $instance;
    static public function get_instance() {
        if (NULL === self::$instance) {
            self::$instance = new helper();
        }
        return $instance;
    }
}

My questions are:

  1. Is this way of coding efficient if not please suggest the best way.
  2. I have like 8 or 10 classes that does the exact same thing which causing the helper __construct function being loaded that many times so the init action will fire each time, Is that a problem and if that is how can I avoid it?

closed as too broad by fuxia Nov 24 '16 at 13:11

Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. Avoid asking multiple distinct questions at once. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • An object encapsulates a task, a combination of state and behavior or a data structure. Your many static dependencies on code outside of the object's scope are making the classes pretty much anti-OOP. – fuxia Nov 24 '16 at 12:39
  • When you use a singleton pattern on all of your classes it can't be OOP, but leaving theory aside, how is this a wordpress specific question? It looks more like a generic PHP or OOP question – Mark Kaplun Nov 24 '16 at 12:41
  • Because of the actions being loaded in __construct – MMT designer Nov 24 '16 at 12:43
  • Never register callbacks in a constructor. That makes your code almost impossible to test. – fuxia Nov 24 '16 at 12:47
  • 1
    I think this question is way too broad and not really WordPress specific. Try to learn the basics first, limit yourself to a very strict set of rules. Recommended reading: Object Calisthenics. If you hit a WordPress specific, isolated problem, your question is very welcome. :) – fuxia Nov 24 '16 at 13:10
1

A few things stand out.

  • Don't load all your classes and files up front.

Load admin class when admin_init hooks fire and front-end classes when front-end hooks fire. Just think about lazy loading as much as possible.

  • If database is an extension of helper then why initialize the base class?

That should be taken care of by database.

  • Why hook 'init' multiple times?

Create an 'init' function and add one hook:

add_action('init', array($this,'my_init');
  • Classes should be CamelCase

Don't make a class lowercase, ever.

In other languages it's not ideal to put actions in the initializer for the instance. Consider __invoke() as an alternatives. Unless you think someone might use the initializer later on...

self::$instance = new helper(); // __construct
self::$instance(); // __invoke

http://wppb.me/ does an interesting job of separating out admin loading from front-end loading.

And @toscho had a good link to https://github.com/object-calisthenics/phpcs-calisthenics-rules


Note: This is a rough outline of what I was thinking but I haven't tested it.

<?php

if ( ! class_exists( 'HelperClasses' ) ) {

    class HelperClasses {

        public static function initialize() {

            add_action( 'init', array ( 'HelperClasses', 'init' ) );
        }

        public static function init() {
            require_once( __DIR__ . '/inc/helper.php' );
            require_once( __DIR__ . '/inc/database.php' );
        }
    }

    add_action( 'plugins_loaded', array ( 'HelperClasses', 'initialize' ) );
}

/inc/helper.php

if ( ! class_exists( 'Helper' ) ) {

    /**
     * Class Helper
     *
     * Loaded via HelperClasses on `init`
     */
    class Helper {

        private static $_instance;

        public static function instance() {

            if ( ! isset( static::$_instance ) ) {
                $singleton = new Helper();
                static::$_instance = $singleton;
                $singleton();
            }

            return static::$_instance;
        }

        public function __invoke() {
            // TODO: Implement __invoke() method.
        }
    }
}

/inc/database.php

if ( ! class_exists( 'Database' ) ) {

    if ( ! class_exists( 'Helper' ) ) {
        wp_die('Helper class required');
    }

    /**
     * Class Database
     *
     * Loaded via HelperClasses on `init`
     */
    class Database extends Helper {

        private static $_instance;

        public static function instance() {

            if ( ! isset( static::$_instance ) ) {
                $singleton = new Database();
                static::$_instance = $singleton;
                $singleton();
            }

            return static::$_instance;
        }

        public function __invoke() {
            // TODO: Implement __invoke() method.
        }
    }
}
  • Great answer very helpful Thanks, I have some questions 1- where would I use this add_action('init', array($this,'my_init'); in class if in __construct still would be the same thing thing write. 2- How can I use the __invoke could you give a complete example. – MMT designer Nov 24 '16 at 12:58
  • obviously the order of these things may need to adjust based on the hooks you're working with – jgraup Nov 24 '16 at 13:17
  • The very first advice should be to use an autoloader instead of manually load classes on different contexts. – David Nov 25 '16 at 9:49
  • @David - and the reason for that? – jgraup Nov 25 '16 at 13:00
  • Dynamic autoloading ensures that classes are loaded on actual demand. Writing classes with focus on a specific environmental context (e.g. «is admin») leads to inflexible code. However, autoloading reduces own code and thus increases maintainability and makes developers life much easier. – David Nov 28 '16 at 10:15

Not the answer you're looking for? Browse other questions tagged or ask your own question.