I am currently modernizing a legacy PHP application for a client. (The codebase was written earlier this year, in fact; new code can be "legacy" from the outset.) The original developer pulled a dirty trick with global that I had not seen before, and I thought I had seen everything.

Legacy codebases often use global to import a variable into the local scope, usually a global function. For example, they might drag in a database connection:

<?php
// define $db in a config file somewhere
$db = new DatabaseConnection(...);

// this function uses the $db connection via global
function fetch_user_by_id($id)
{
    global $db;
    return $db->fetchAssoc("SELECT * FROM users WHERE id = ?", $id);
}
?>

I see that kind of thing all the time in legacy PHP. However, what I have not seen before is a function exporting a global.

Take a look at the following code. If the $bar variable is not already defined in the global scope, PHP will define it in the global scope for you automatically when you call foo().

<?php
error_reporting(E_ALL);

function foo()
{
    global $bar;
    $bar ++;
}

// $bar is not defined yet, so PHP will show an
// "undefined variable" notice
echo $bar. PHP_EOL;

// calling foo() defines $bar in the global scope,
// and increments it
foo();

// $bar is now available in the global scope, having
// been exported from function foo()
echo $bar. PHP_EOL;

The legacy developer did that because he wanted to keep the variable initialization outside of the global scope for some reason, even though he used the variable in the global scope elsewhere.

It is exceptionally difficult to track down where an exported global is coming from when refactoring a legacy application. If you must write legacy code using globals, initialize them in the global scope. Better yet, don't use globals at all: pass values as function arguments, or use dependency injection techniques.


Are you stuck with a legacy PHP application? You should buy my book because it gives you a step-by-step guide to improving you codebase, all while keeping it running the whole time.