Brief introduction
Even if you are developing a new, large PHP program, you will inevitably have to use global data, because some data needs to be used in different parts of your code. Some common global data include: Program settings class, database connection class, user information and so on. There are many ways to make this data global, and the most common is to use the "global" keyword declaration, later in the article we will specifically explain.
The only downside to declaring global data with the "global" keyword is that it is in fact a very poor way of programming, and often leads to bigger problems in the program, since global data links the separate snippets of your code. The consequence is that if you change one part of the code, it can cause other parts to go wrong. So if you have a lot of global variables in your code, your entire program is inherently difficult to maintain.
This article will show you how to prevent this global variable problem with different techniques or design patterns. Of course, let's first look at how to use the "global" keyword for global data and how it works.
Using global variables and the "global" keyword
PHP defaults to defining "Super Global (Superglobals)" variables that are automatically global and can be invoked anywhere in the program, such as $_get and $_request, and so on. They usually come from data or other external data, and using these variables is usually not a problem because they are basically not writable.
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 don't understand the scope of variable usage, please refer to the instructions in the PHP manual.
The following is a demo example using the "global" keyword:
Copy Code code as follows:
<?php
$my _var = ' Hello world ';
Test_global ();
function Test_global () {
Now in the 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 above example, the "global" keyword is used to import global variables. It seems to work well and it's simple, so why worry about defining global data with the "global" keyword?
Here are three good reasons:
1, code reuse is almost impossible.
If a function relies on global variables, it is almost impossible to use this function in a different environment. Another problem is that you can't extract this function and use it in other code.
2, debugging and solve the problem is very difficult.
It is more difficult to track a global variable than to track a non-global variable. A global variable may be redefined in some of the less obvious include files, even if you have a very Good program editor (or IDE) to help you, it will take you several hours to discover the problem.
3, understanding the code will be very difficult things.
It's hard to figure out where a global variable comes from and what it does. In the process of development, you may know that every global variable is known, but after about a year, you may forget at least the general global variable, and you will regret the use of so many global variables.
So if we don't use global variables, what should we use? Let's look at some of the solutions below.
Using function arguments
One way to stop using a global variable is simply to pass the variable as a function parameter, as shown in the following:
Copy Code code as follows:
<?php
$var = ' Hello world ';
Test ($var);
function test ($var) {
Echo $var;
}
?>
If you only need to pass a global variable, then this is a very good or even outstanding solution, but what if you have to pass a lot of values?
For example, if we want to use a database class, a program settings class and a user class. In our code, these three classes are used in all components, so they must be passed to each component. If we use the method of function parameters, we have to do this:
Copy Code code as follows:
<?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 join, we have to add one more function parameter to each function. So we need to use a different approach to solve.
using a single piece (singletons)One way to solve the problem of function parameter is to use single piece (singletons) instead of function parameter. A single piece is a special kind of object that can only be instantiated once and contains a static method to return an object's interface. The following example demonstrates the
How to build a simple single piece:
Copy Code code as follows:
<?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 returns an instance of this class by using a static variable $me to ensure that there is only one instance of the DbConnection class.
The advantage of using a single piece is that we do not need to explicitly pass an object, but simply use the getinstance () method to get to the object, as follows:
Copy Code code as follows:
<?php
function Test () {
$db = Dbconnection::getinstance ();
Do something with the object
}
?>
However, there are a series of deficiencies in the use of single pieces. First, what if we need to globally target multiple objects in a class? Because we use a single piece, so this is impossible (just as its name is a single piece). Another problem is that a single piece cannot be tested with an individual test, and it is completely impossible unless you introduce all the stacks, which you obviously don't want to see. This is why single pieces are not the main reason for our ideal solution.
Registration Mode
The best way for some objects to be used by all the components in our code is to use a central container object that contains all of our objects. Usually this container object is called a register. It's very flexible and very simple. A simple register object looks like this:
Copy Code code as follows:
<?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 Registrar object is to register an object using the method set ():
Copy Code code as follows:
<?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 refer to the need to pass this register object to a function (instead of passing three objects separately). Look at the following example:
Copy Code code as follows:
<?php
Function test (& $registry) {
$db =& $registry->get (' db ');
$settings =& $registry->get (' Settings ');
$user =& $registry->get (' user ');
Do something with the objects
}
?>
The registrar compared to other methods, a big improvement is that when we need to add a new object to our code, we no longer need to change everything (translator: The code that uses the global object in the program), we just need to register a new object in the register. It can then be invoked immediately in all components. "(Translator Note: The newly registered object).
To make it easier to use the Registrar, we change its invocation to a single mode (translator Note: Do not use the aforementioned function to pass). Because we need only one registrar in our program, the single piece mode makes it very suitable for this task. Add a new method to the Registrar class, as follows:
Copy Code code as follows:
?
function &getinstance () {
Static $me;
if (Is_object ($me) = = True) {
return $me;
}
$me = new Registry;
return $me;
}
?>
So it can be used as a single piece, such as:
Copy Code code as follows:
<?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 the proprietary stuff to a function, nor do we need to use the "global" keyword. So the Registrar model is the ideal solution for this problem, and it is very flexible.
Request Wrapper
Although our registrar has made the "global" keyword completely redundant, there is still one type of global variable in our code: Super Global variables, such as variable $_post,$_get. Although these variables are very standard, and in your use will not be a problem, but in some cases, you may also need to use the Registrar to encapsulate them.
A simple workaround is to write a class that provides the interface to get these variables. This is often referred to as a "request wrapper," And here's a simple example:
Copy Code code as follows:
<?php
Class Request {
var $_request = array ();
function Request () {
GET Request variables
$this->_request = $_request;
}
function Get ($name) {
return $this->_request[$name];
}
}
?>
The example above is a simple demo, and of course you can do a lot of other things in the request wrapper (wrapper) (such as filtering data automatically, providing default values, etc.).
The following code shows how to invoke a request wrapper:
Copy Code code as follows:
<?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, now we are no longer relying on any global variables, and we completely let these functions away from global variables.
Conclusion
In this article, we demonstrate 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 patterns because it is very flexible and it can prevent your code from becoming a mess.
In addition, I recommend that you pass the Registrar object using function arguments instead of a single piece mode. While it's easier to use a single piece, it can be problematic later, and it's easier to understand using function arguments.