At this point, all of our classes and functions have been consolidated to a central location, and all related include statements have been removed. We would prefer to start writing tests for our classes, but it is very likely that we have a lot of global variables embedded in them. These can cause a lot of trouble via action at a distance where modifying a global in one place changes its value in another place. The next step, then, is to remove all uses of the global keyword from our classes, and inject the necessary dependencies instead.

To start with a naive example, let's say an Example class needs a database connection. Here we create the connection inside a class method:

We are creating the Db dependency inside the method that needs it. There are several problems with this. Some of them are:

After writing code like this, many developers discover the global keyword, and realize they can create the connection once in a setup file, then pull it in from the global scope:

Even though we are still pulling in the dependency, this technique solves the problem of multiple database connections using up limited resources, since the same database connection is reused across the codebase. The technique also makes it possible to change our connection parameters in a single location, the setup.php file, instead of several locations. However, one problem remains, and one is added:

The last point is a killer. If a method ever sets $db = 'busted'; then the $db value is now a string, and not a database connection object, throughout the entire codebase. Likewise, if the $db object is modified, then it is modified for the entire codebase. This can lead to very difficult debugging sessions.

Once we ascertain that the class works with the properties in place, we need to convert the global calls in the constructor to use passed parameters instead. Given our Example class above, the converted version might look like this:

All we have done here is remove the global call, and added a constructor parameter. We need to do this for every global in the constructor.

Since the global is for a particular class of object, we typehint the parameter to that class (in this case Db). If possible, we should typehint to an interface instead, so if the Db object implements a DbInterface, we should typehint to DbInterface. This will help with testing and later refactoring. We may also typehint to array or callable as appropriate. Not all global calls are for typed values, so not all parameters will need typehints (e.g., when the parameter is expected to be a string).

After converting global variables to constructor parameters, we will find that every instantiation of the class throughout the legacy application is now broken. This is because the constructor signature has changed. With that in mind, we now need to search the entire codebase (not just the classes) for instantiations of the class, and change the instantiations to the new signature.

To search for instantiations, we use our project-wide search facility to find uses of the new keyword with our class name using a regular expression:

The expression searches for the new keyword, followed by at least one character of whitespace, followed by a terminating non-word character (such as a parenthesis, space, or semicolon).

As we discover instantiations of the class in the codebase, we modify them to pass the parameters as needed. If, for example, a page script looks like this:

We need to add the parameter to the instantiation:

The new instantiations need to match the new constructor signature, so if the constructor takes more than one parameter, we need to pass all of the parameters.