Share via:

Avoid Direct Use of PHP Superglobals

This article explains why- and how to avoid the use of PHP superglobals such as $_POST and $_GET inside your PHP classes.

102 views

Edited: 2019-11-22 01:12

PHP tutorial

In OOP PHP, it is a good idea to avoid using the superglobals directly in your classes. One of the main reasons for this is that the state of your objects could be rendered unpredictable when used in foreign code environments.

Even if you are the only person working on the code and you will not be sharing the code with anyone else, there might still be benefits to avoiding direct use of superglobals in your code.

For example, if you rely on other peoples code (dependencies), it is hard to tell if a dependency is modifying the content of a superglobal. One way around this is to save a localized copy of the PHP superglobals ($_POST, $_GET, and $_SERVER) before loading dependencies. The wrapper can then be passed on where needed via dependency injection.

Rather than just saving the superglobals to global variables, you could instead create a class with getters and setters to handle the superglobals.

Multiple objects

On rare occasion, you might decide to change the value of a superglobal directly like this:

$_GET['requested'] = 'custom-404';

The above example tells the CMS "back end" that a custom 404 error page should be delivered, probably as a result to a file_exists returning false.

While changing the value of a superglobal is usually not recommended, the above is actually a rare example of a situation where it would be considered fine. In most cases, we should of course not be changing superglobals.

However, when changing a superglobal is done, the state of other objects that happens to depend on the variable suddenly becomes unpredictable. You will likely not be the one to cause the catastrophic failure, but once you start introducing code from the outside, proper encapsulation would help avoid such errors.

Of course, such failures are probably extremely rare, since developers know to not write to superglobals. But, why run the risk when it can be easily avoided?

Wrapping the superglobals in a class

Instead, we might choose to have a globals class containing local copies of the superglobals in the state they were in at run-time. This should then be passed on to classes that needs it via Dependency Injection.

Personally I now got two "globals" classes. One containing the PHP superglobals, and another containing stuff for the application I am working on. The latter contains things specific to my application, such as "requested path" and "base path".

A simple implementation of such a class is included below:

namespace doorkeeper\lib\superglobals;

class superglobals
{
    private $_SERVER;
    private $_POST;
    private $_GET;
    private $_SESSION;

    public function __construct()
    {
        $this->define_superglobals();
    }
    /**
     * Returns a key from the superglobal,
     * as it was at the time of instantiation.
     *
     * @param $key
     * @return mixed
     */
    public function get_SERVER($key = null)
    {
        if (null !== $key) {
            return (isset($this->_SERVER["$key"])) ? $this->_SERVER["$key"] : null;
        } else {
            return $this->_SERVER;
        }
    }
    /**
     * Returns a key from the superglobal,
     * as it was at the time of instantiation.
     *
     * @param $key
     * @return mixed
     */
    public function get_POST($key = null)
    {
        if (null !== $key) {
            return (isset($this->_POST["$key"])) ? $this->_POST["$key"] : null;
        } else {
            return $this->_POST;
        }
    }
    /**
     * Returns a key from the superglobal,
     * as it was at the time of instantiation.
     *
     * @param $key
     * @return mixed
     */
    public function get_GET($key = null)
    {
        if (null !== $key) {
            return (isset($this->_GET["$key"])) ? $this->_GET["$key"] : null;
        } else {
            return $this->_GET;
        }
    }
    /**
     * Returns a key from the superglobal,
     * as it was at the time of instantiation.
     *
     * @param $key
     * @return mixed
     */
    public function get_SESSION($key = null)
    {
        if (null !== $key) {
            return (isset($this->_SESSION["$key"])) ? $this->_SESSION["$key"] : null;
        } else {
            return $this->_SESSION;
        }
    }
    /**
     * Function to define superglobals for use locally.
     * We do not automatically unset the superglobals after
     * defining them, since they might be used by other code.
     *
     * @return mixed
     */
    private function define_superglobals()
    {

        // Store a local copy of the PHP superglobals
        // This should avoid dealing with the global scope directly
        // $this->_SERVER = $_SERVER;
        $this->_SERVER = (isset($_SERVER)) ? $_SERVER : null;
        $this->_POST = (isset($_POST)) ? $_POST : null;
        $this->_GET = (isset($_GET)) ? $_GET : null;
        $this->_SESSION = (isset($_SESSION)) ? $_SESSION : null;

    }
    /**
     * You may call this function from your compositioning root,
     * if you are sure superglobals will not be needed by
     * dependencies or outside of your own code.
     *
     * @return void
     */
    public function unset_superglobals()
    {
        unset($_SERVER);
        unset($_POST);
        unset($_GET);
        unset($_SESSION);
    }

}

Comments