0

I have a lot of functions extending various plugins and providing logic for a site, functions which use different bits of info that I would like to keep in one place and access it, but feel uncomfortable with using a global variable, out of fear that due to a vulnerability in my code they might get copied, or worse, overwritten in the global scope and then passed down to a function. Then again I think this is somewhat nonsensical, because if my code has that kind of vulnerability, then it's busted anyway - but I do feel that showing up a global var instead of the intended one somewhere where I made a mistake is still more likely than being able to call a function.

Some of this information are resource names and paths that WP loads up, other info IDs and paths to redirect pages.

I was thinking that instead of using $settings = array('some'=>'settings','go'=>'here'); and then calling the var with global $settings; into the local scope.

To instead use: function my_settings(){ return array('some'=>'settings','go'=>'here'); } and call this function into $settings = my_settings() outside of the global scope, into whichever function or class I need it in, but never in the global scope.

Bottom line, does this idea have any merit to it, instead of using globals? As performance goes, there will be no decrease due to calling one more function. But is there an advantage from a security standpoint?

1

No overriding benefit, other than collision avoidance and safeguard against unintended modification.

Not sure if it makes you feel any better but WP core itself has many dependencies on global variables. I'm not saying that's a good thing; just a fact.

Also remember you have a database and functions to handle storing and retrieving options, for values you might want to use in different situations:

https://developer.wordpress.org/reference/functions/update_option/

https://developer.wordpress.org/reference/functions/get_option/

  • Thank you! I know about WP's globals, and it doesn't make me feel any better, haha, but that's not my concern since it's unavoidable. So I'll stick to setting the variables in a function to safeguard against unintended changes and to have all things defined in one place. I rather not go with the option features since it gives me the same functionality as above but with added db queries. – Alex Protopopescu Nov 11 '16 at 6:12
2

MAGIC METHODS

The easiest way to create a global variable without polluting the global space is to utilize the Singleton pattern. Essential a class that is only instantiated once and accessed via a single static function.

In this case you could make getters and setters to store and retrieve values but we can utilize PHP's magic methods. Now you have an ability to access this class from anywhere (as long as it's loaded) and save any property with any value.

The basic syntax is <class>::<singleton>()-><property>

Just load this class once before accessing it.

if ( ! class_exists( 'CustomSettingClass' ) ) {

    class CustomSettingClass {

        private        $s = array ();
        private static $_instance;

        public static function instance() {
            if ( ! static::$_instance ) {
                static::$_instance = new self();
            }

            return static::$_instance;
        }

        // setter
        function __set( $k, $c ) {
            $this->s[ $k ] = $c;
        }

        // getter
        function __get( $k ) {
            return isset( $this->s[ $k ] ) ? $this->s[ $k ] : null;
        }

        // callable
        function __call( $method, $args ) {
            if ( isset( $this->s[ $method ] ) && is_callable( $this->s[ $method ] ) ) {
                return call_user_func_array( $this->s[ $method ], $args );
            } else {
                echo "unknown method " . $method;

                return false;
            }
        }
    }
}

Then just access the properties of the singleton from a static accessor.

CustomSettingClass::instance()->foo = 'bar';

echo CustomSettingClass::instance()->foo; // bar

CustomSettingClass::instance()->complex = array(1,2,3);

print_r( CustomSettingClass::instance()->complex ); // 1,2,3

As for security, you could utilize the same approach but rather than magic methods use private methods for secure functions. Basic OOP really.

It's also worth noting that, by adding the __call magic method, we can resolve anonymous functions.

// create handler
CustomSettingClass::instance()->callback = function($value){ echo $value; };

// add listener
add_action( 'test', array(CustomSettingClass::instance(), 'callback'));

// do action
do_action ('test', 'some value'); // some value

Now you have a completely dynamic and customizable object that acts like a $global, can be adjusted on the fly, and can handle actions and filters.


SIMPLIFIED STATIC CLASS FUNCTIONS

Ok, so magic methods are over kill and not private. So how about a static class with private internals and an external facing settings getter. You only need to expose as much as you need in the public StaticSettings::settings() function.

if ( ! class_exists( 'StaticSettings' ) ) {

    class StaticSettings {

        private static $_location = 'here';

        public static function settings() {

            return array (
                    'some' => 'settings',
                    'go'   => static::$_location,
            );
        }
    }
}

$settings = StaticSettings::settings();

LOCALLY SCOPED STATIC VARIABLE

I almost want to say your best bet is to go the static variable in a function route. This instantiated the $settings object as null which lets you trigger an initialization. At that point they are basically cached. So every other call to the function returns an already constructed object. All the inner workings are private to the function itself.

function my_settings() {

    static $settings = null;

    if ( null === $settings ) {

        // initialize settings object once

        $settings = array (
                'some' => 'setting',
                'go'   => 'here',
        );
    }

    // return cached value

    return $settings;
}
  • Thank you, that's a good deal of info to keep in mind! However for my scenario this is way to complicated. The alternative remains to have a function that returns the variables so I can keep them in local scope, and that takes care of the issue I was more concerned about - unintended or malicious changes to the variables to the variables defined at the start. – Alex Protopopescu Nov 11 '16 at 6:02

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.