Resolve several ways to use global variables in PHP

  • 2020-06-19 09:52:36
  • OfStack

Introduction to the
Even if you develop a new large PHP program, you will inevitably need to use global data, because some data will need to be used in different parts of your code. Some common global data are: program Settings classes, database connection classes, user information, and so on. There are many ways to make this data global, the most common of which is to use the "global" keyword, which we will discuss later in this article.
Use "global" keyword to statements of global data only one downside is that it is actually one kind of very bad way of programming, and often after lead to bigger problems appear in the program, as global data in the code you originally separate code is linked in 1, the consequence of this is if you change one part of the code 1, other parts are likely to lead to errors. So if you have a lot of global variables in your code, then your entire program must be hard to maintain.

This article shows how different techniques or design patterns can be used to prevent this global variable problem. First, of course, let's look at how the "global" keyword is used for global data and how it works.

Use global variables and the "global" keyword
By default, PHP defines 1 "super global (Superglobals)" variables that are automatically global and can be called anywhere in the program, such as $_GET and $_REQUEST. They usually come from data or other external data, and using these variables is usually not a problem because they are essentially unwritable.

But you can use your own global variables. Using the keyword "global" you can import global data into the local scope of a function. If you do not understand the scope of variables, please refer to the instructions in the PHP manual.
Here is an example of using the "global" keyword:


<?php
$my_var = 'Hello World';
test_global();
function test_global() {
    // Now in local scope
    // the $my_var variable doesn't exist
    // Produces error: "Undefined variable: my_var"
    echo $my_var;
    // Now let's important the variable
    global $my_var;
    // Works:
    echo $my_var;
}
?>

As you can see in the example above, the "global" keyword is used to import global variables. It seems to be working well and simple, so why worry about using the "global" keyword to define global data?
Here are three good reasons:

Code reuse is almost impossible.
If a function depends on a global variable, it is almost impossible to use the function in a different environment. Another problem is that you can't extract the function and use it in other code.

2. It is very difficult to debug and solve problems.
Tracking a global variable is much more difficult than tracking a non-global variable. A global variable might be redefined in some obscure include file, and even if you had a very good program editor (or IDE) to help you, it would take you hours to discover the problem.

3. Understanding the code will be very difficult.
It's hard to know where a global variable comes from and what it does. During development, you might know every global variable, but after a year or so, you might forget at least one of them and regret using so many globals.
So if we don't use global variables, what should we use? Let's take a look at some of the solutions.
Using function parameters
One way to stop using global variables is simply to pass them as arguments to a function, as shown below:

 
<?php
$var = 'Hello World';
test ($var);
function test($var) {
    echo $var;
}
?>

If you only need to pass one global variable, this is an excellent or even excellent solution, but what if you have to pass many values?
For example, suppose we want to use a database class, a program setup class, and a user class. In our code, these three classes are used in all components, so we have to pass them to each component. If we use the method of function arguments, we have to:
   
<?php
$db = new DBConnection;
$settings = new Settings_XML;
$user = new User;
test($db, $settings, $user);
function test(&$db, &$settings, &$user) {
    // Do something
}
?>

Obviously, it's not worth it, and once we have new objects to add, we have to add one more function argument per function. So we need to do it in a different way.

One way to solve the function parameter problem using singleton (Singletons) is to use singleton (Singletons) instead of function parameter. Singlets are a special class of objects that can only be instantiated once and contain a static method to return the object's interface. The following example demonstrates how to build a simple singleton:
 
<?php
// Get instance of DBConnection
$db =& DBConnection::getInstance();
// Set user property on object
$db->user = 'sa';
// Set second variable (which points to the same instance)
$second =& DBConnection::getInstance();
// Should print 'sa'
echo $second->user;
Class DBConnection {
    var $user;
    function &getInstance() {
        static $me;
        if (is_object($me) == true) {
            return $me;
        }
        $me = new DBConnection;
        return $me;
    }
    function connect() {
        // TODO
    }
    function query() {
        // TODO
    }
}
?>

The most important part of the above example is the function getInstance(). This function ensures that there are only 1 instance of the DBConnection class by returning an instance of the class using a static variable of $me.
The advantage of using singletons is that instead of explicitly passing an object, we simply use the getInstance() method to get it, as follows:

<?php
function test() {
    $db = DBConnection::getInstance();
    // Do something with the object
}
?>

However, there is also a series 1 disadvantage to using a single piece. First, how do we need to globalize multiple objects in one class? This is not possible (as its name is singleton 1) because we use singleton. The other problem is that singleton can't be tested using individual tests, and that's completely impossible unless you introduce all the stacks, which you obviously don't want to see. This is the main reason why singletons are not our ideal solution.

The registration model
The best way to make 1 object available to all components in our code is to have a central container object that contains all of our objects. This container object is often referred to as a registry. It's very flexible and it's very simple. A simple registry object looks like this:

<?php
Class Registry {
    var $_objects = array();
    function set($name, &$object) {
        $this->_objects[$name] =& $object;
    }
    function &get($name) {
        return $this->_objects[$name];
    }
}
?>

The first step in using the registry object is to register an object using method set() :

<?php
$db = new DBConnection;
$settings = new Settings_XML;
$user = new User;
// Register objects
$registry =& new Registry;
$registry->set ('db', $db);
$registry->set ('settings', $settings);
$registry->set ('user', $user);
?>

Now that our register object holds all of our objects, we mean that we need to pass the register object to one function (instead of passing three objects separately). Take a look at this example:

<?php
function test(&$registry) {
    $db =& $registry->get('db');
    $settings =& $registry->get('settings');
    $user =& $registry->get('user');
    // Do something with the objects
}
?>

Registry compared with other methods, it is a great improvement when we need in our code with new one object, we no longer need to change all the things (translator note: refers to all use the global object in the application code), we only need 1 new registered in registry objects, and then it (translator note: new registered object) can immediately call in all the components.

To make it easier to use the registry, we changed its calls to singleton mode. Because we only need to use one registry in our program, the singleton pattern makes it ideal for this type of task. Add a new method to the registry class as follows:

<?
function &getInstance() {
    static $me;
    if (is_object($me) == true) {
        return $me;
    }
    $me = new Registry;
    return $me;
}
?>

In this way, it can be used as a single piece, such as:

<?php
$db = new DBConnection;
$settings = new Settings_XML;
$user = new User;
// Register objects
$registry =& Registry::getInstance();
$registry->set ('db', $db);
$registry->set ('settings', $settings);
$registry->set ('user', $user);
function test() {
    $registry =& Registry::getInstance();
    $db =& $registry->get('db');
    $settings =& $registry->get('settings');
    $user =& $registry->get('user');
    // Do something with the objects
}
?>

As you can see, we don't need to pass everything private to a function, nor do we need to use the "global" keyword. So the registry pattern is an ideal solution to this problem, and it is very flexible.

Request wrapper
Although our registry has made the "global" keyword redundant, there is still one type of global variable in our code: super global variables, such as $_POST,$_GET. While these variables are fairly standard and you won't have any problems using them, in some cases you might also need to use a registry to encapsulate them.
A simple solution is to write a class that provides an interface to these variables. This is often referred to as a "request wrapper," and here is a simple example:
 
<?php
$var = 'Hello World';
test ($var);
function test($var) {
    echo $var;
}
?>
0
The above example is a simple demonstration, but there are many other things you can do in the request wrapper (request wrapper) (auto-filter data, provide default values, etc.).
The following code demonstrates how to invoke a request wrapper:

<?php
$request = new Request;
// Register object
$registry =& Registry::getInstance();
$registry->set ('request', &$request);
test();
function test() {
    $registry =& Registry::getInstance();
    $request =& $registry->get ('request');
    // Print the 'name' querystring, normally it'd be $_GET['name']
    echo htmlentities($request->get('name'));
}
?>

As you can see, we are no longer relying on any global variables, and we have completely removed these functions from global variables.

conclusion
In this article, we demonstrated how to fundamentally remove global variables from your code and replace them with appropriate functions and variables. Registration mode is one of my favorite design modes because it is very flexible, and it prevents your code from becoming a mess.
In addition, I recommend passing the registry object using function arguments rather than singleton mode. Although it is easier to use singleton, it may cause problems in the future, and it is easier to understand how to pass function arguments.


Related articles: