3
\$\begingroup\$

I'm developing a WordPress plugin using the singleton pattern with the following requirements:

  • There should ever exist one instance of the plugin
  • Another instance of the object should never be instantiated, created, cloned, copied etc in any way.
  • The singular plugin object and its public methods and vars should be easily accessible and usable in any scope, by anything including other plugins

The last one is a given with singleton pattern, but there is one concern I have: - __construct() should run only once, and should never run again. The code inside __construct() should never be reparsed and reprocessed however the singleton is instantiated.

I already tested the code and it seems to work, however the code in __construct() was being reprocessed every time i requested the instance of the object through get_instance() outside the plugin until I added self::$instance = $this; at the top.

I'm wondering whether I am violating the guidelines I listed above and also if it is possible for anyone to violate them through any kind of logic or method that they put into their plugins. Also whether this code will last at least a few PHP versions into the future.

    class particular_plugin {

        private static $instance = null;

        protected function __construct() 
        {
            self::$instance = $this;

            // Numerous initialization variables and actions here
            // including calls to functions inside the class

        }

        public static function get_instance()
        {
            if (null === static::$instance) {
                static::$instance = new static();
            }

            return static::$instance;
        }

        private function __clone()
        {

        }

        private function __wakeup()
        {

        }

    }
    // Initializing the plugin here
    $particular_plugin = particular_plugin::get_instance();

    function particular_plugin_get()
    {
        // This function allows any plugin to easily retrieve this plugin object and access its public methods and vars as they exist at any given point in time
        return particular_plugin::get_instance();

    }
\$\endgroup\$
  • \$\begingroup\$ can you show the content of $this->initialize_vars(); function ? \$\endgroup\$ – RomanPerekhrest Jan 28 '16 at 10:42
  • \$\begingroup\$ I'm not sure if that "UPDATE" section was added in response to the answer you received, so I'm not going to roll it back right now. Please see what you may and may not do after receiving answers, and avoid creating seams in your posts with big "UPDATE" banners - readers typically only care about the last revision, and everyone can see the revision history of your post. \$\endgroup\$ – Mathieu Guindon Jan 28 '16 at 18:39
  • \$\begingroup\$ Forgot to say - @RomanPerekhrest - i updated the answer with the contents of initialize_vars. \$\endgroup\$ – unity100 Jan 29 '16 at 0:54
  • \$\begingroup\$ I removed updates to comments. \$\endgroup\$ – unity100 Jan 29 '16 at 0:57
  • \$\begingroup\$ The reason Im thinking that __construct() is called more than once and creates copies of the object is: 1 - There is an action $this->initialize_vars(); called inside the construct, which initializes some variables. 2 - There is an action in another plugin hooked to this action just to test. It does nothing other than run itself and assign a value to an object var. 3 - When the plugin is activated, it dies out in a memory limit error. \$\endgroup\$ – unity100 Jan 29 '16 at 0:57
2
\$\begingroup\$

Forget everything but the instatiator, constructor and property, you don't call new static; you call new self(), then it jumps into the constructor, the way I do singleton is:

class Foo
{
    private static $init;

    private function __construct() {  }

    public static function init()
    {
        return static::$init = (
          null === static::$init ? new self() : static::$init  
        );
    }
}

$foo = Foo::init();

This makes it so when new self() is called, it jumps to the constructor and assigns self::$init to really be an instance of the class

\$\endgroup\$
  • \$\begingroup\$ Just tested Sam. If i comment out self::$instance = $this; in __construct(), i enter recursion during plugin activation. There is an action initialize_vars that run inside the constructor, and one simple action that is hooked to it - which does nothing but the setup causes recursion. Leading me to think that construct is called multiple times if i leave out the self::$instance = $this; \$\endgroup\$ – unity100 Jan 27 '16 at 19:55
  • \$\begingroup\$ @unity100, that's why you do your logic inside __construct and call new self() over new static \$\endgroup\$ – SomeRandomPerson Jan 28 '16 at 18:59
  • \$\begingroup\$ I have to call $this->initialize_vars() inside the construct. This allows the variable initialization to be hookable. \$\endgroup\$ – unity100 Jan 29 '16 at 15:58
  • \$\begingroup\$ @unity, please read the manual, calling new self() is like calling $this->__construct() inside the static member. basic OOP PHP. \$\endgroup\$ – SomeRandomPerson Jan 29 '16 at 23:07
  • \$\begingroup\$ Yeah but that's not my problem. I actually implemented your code and tested it. I still got the memory overflow problem without "self::$instance = $this;" at top of __construct(). \$\endgroup\$ – unity100 Jan 29 '16 at 23:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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