Stop using `global` in PHP
php
Solution
The point against `global` variables is that they couple code very tightly. Your entire codebase is dependent on a) the variable name `$config` and b) the existence of that variable. If you want to rename the variable (for whatever reason), you have to do so everywhere throughout your codebase. You can also not use any piece of code that depends on the variable independently of it anymore.
Example with `global` variable:
require 'SomeClass.php';
$class = new SomeClass;
$class->doSomething();
Anywhere in the above lines you may get an error because the class or some code in `SomeClass.php` implicitly depends on a global variable `$config`. There's no indication of this whatsoever though just looking at the class. To solve this, you have to do this:
$config = array(...);
require 'SomeClass.php';
$class = new SomeClass;
$class->doSomething();
This code may still fail somewhere if you do not set the correct keys inside `$config`. Since it's not obvious what parts of the config array `SomeClass` needs or doesn't need and when it needs them, it's hard to recreate the correct environment for it to run correctly. It also creates conflicts if you happened to already have a variable `$config` used for something else wherever you want to use `SomeClass`.
So instead of creating implicit, invisible dependencies, inject all dependencies:
require 'SomeClass.php';
$arbitraryConfigVariableName = array(...);
$class = new SomeClass($arbitraryConfigVariableName);
$class->doSomething();
By passing the config array explicitly as a parameter, all the above problems are solved. It's as simple as handing the required information around inside your app. It also makes the structure and flow of the application and what talks to what much clearer. To get to this state if your application is currently a big ball of mud may take some restructuring.
The bigger your codebase gets, the more you have to decouple the individual parts from each other. If every part is dependent on every other part in your codebase, you simply cannot test, use or reuse any part of it individually. That simply devolves into chaos. To separate parts from each other, code them as classes or functions which take all their required data as parameters. That creates clean seams (interfaces) between different parts of your code.
Trying to tie your question together into one example:
require_once 'Database.php';
require_once 'ConfigManager.php';
require_once 'Log.php';
require_once 'Foo.php';
// establishes a database connection
$db = new Database('localhost', 'user', 'pass');
// loads the configuration from the database,
// the dependency on the database is explicit without `global`
$configManager = new ConfigManager;
$config = $configManager->loadConfigurationFromDatabase($db);
// creates a new logger which logs to the database,
// note that it reuses the same $db as earlier
$log = new Log($db);
// creates a new Foo instance with explicit configuration passed,
// which was loaded from the database (or anywhere else) earlier
$foo = new Foo($config);
// executes the conversion function, which has access to the configuration
// passed at instantiation time, and also the logger which we created earlier
$foo->conversion('foo', array('bar', 'baz'), $log);
I'll leave to implementation of the individual classes up as an exercise for the reader. When you try to implement them, you'll notice that they're very easy and clear to implement and do not require a single `global`. Every function and class gets all its necessary data passed in the form of function arguments. It should also be obvious that the above components can be plugged together in any other combination or that dependencies can easily be substituted for others. For example, the configuration does not need to come from the database at all, or the logger can log to a file instead of the database without `Foo::conversion` having to know about any of this.
Example implementation for `ConfigManager`:
class ConfigManager {
public function loadConfigurationFromDatabase(Database $db) {
$result = $db->query('SELECT ...');
$config = array();
while ($row = $result->fetchRow()) {
$config[$row['name']] = $row['value'];
}
return $config;
}
}
It's a very simple piece of code that doesn't even do much. You may ask why you'd want this as object oriented code. The point is that this makes using this code extremely flexible, since it isolates it perfectly from everything else. You give one database connection in, you get one array with a certain syntax back. Input → Output. Clear seams, clear interfaces, minimal, well defined responsibilities. You can do the same with a simple function.
The extra advantage an object has is that it even further decouples the code that calls `loadConfigurationFromDatabase` from any particular implementation of that function. If you'd just use a global `function loadConfigurationFromDatabase()`, you basically have the same problem again: that function needs to be defined when you try to call it and there are naming conflicts if you want to replace it with something else. By using an object, the critical part of the code moves here:
$config = $configManager->loadConfigurationFromDatabase($db);
You can substitute `$configManager` here for any other object that also has a method `loadConfigurationFromDatabase`. That's "duck typing". You don't care what exactly `$configManager` is, as long as it has a method `loadConfigurationFromDatabase`. If it walks like a duck and quacks like a duck, it is a duck. Or rather, if it has a `loadConfigurationFromDatabase` method and gives back a valid config array, it's some sort of ConfigManager. You have decoupled your code from one particular variable `$config`, from one particular `loadConfigurationFromDatabase` function and even from one particular `ConfigManager`. All parts can be changed and swapped out and replaced and loaded dynamically from anywhere, because the code does not depend on any one particular other piece.
The `loadConfigurationFromDatabase` method itself also does not depend on any one particular database connection, as long as it can call `query` on it and fetch results. The `$db` object being passed into it could be entirely fake and read its data from an XML file or anywhere else instead, as long as its interface still behaves the same.
Problem
I have a `config.php` that is included to each page. In config I create an array that looks something like: ``` $config = array(); $config['site_name'] = 'Site Name'; $config['base_path'] = '/home/docs/public_html/'; $config['libraries_path'] = $config['base_path'] . '/libraries'; //etc... ``` Then I have `function.php`, that is also included to almost each page, where I have to use `global $config` to get access to it - and this is what I would like to get rid of! How do I access `$config` in the other parts of my code without using `global`? Could anyone explain, WHY I shouldn't use `global` in my example? Some say it's a bad tone, others say it's not secure? EDIT 1: Example of where and how I use it: ``` function conversion($Exec, $Param = array(), $Log = '') { global $config; $cmd = $config['phppath'] . ' ' . $config['base_path'] . '/' . $Exec; foreach ($Param as $s) { $cmd .= ' ' . $s; } } ``` EDIT 2: Putting all of this in the class, as suggested by Vilx, would be cool but in this case, how would I tie it with the following loop that is extracting config `key` and `value` from database. I oversimplified the idea of assigning `$config` array, here is an example: ``` $sql = "SELECT * from settings"; $rsc = $db->Execute($sql); if ( $rsc ) { while(!$rsc->EOF) { $field = $rsc->fields['setting_options']; $config[$field] = $rsc->fields['setting_values']; @$rsc->MoveNext(); } } ``` EDIT 3: Besides, I have to access other `vars` from functions that are set in config and it's few of them, e.g.:`$db`, `$language` and etc. If I put them in the class will it really solve anything? If I use `global` what does it really change? EDIT 4: I read PHP global in functions where Gordon explains in the very nice way why you shouldn't use `global`. I agree on everything but I don't use `global` in my case to reassign the variables, which will result in, like he said, `<-- WTF!!`, ;)) yeah agree, it's crazy. But if I just need to access database from a function just by using `global $db` where is the problem in this case? How do you do this otherwise, without using `global`? EDIT 5: In the same PHP global in functions deceze says: "The one big reason against global is that it means the function is dependent on another scope. This will get messy very quickly." But I'm talking here about basic 'INIT'. I basically set `define` but use `vars` - well that is wrong in technical way. But your function is not depended on anything - but the name of one var `$db` that you could keep in mind? It's really global need to use `$db`, where is the DEPENDENCY here and how to use it otherwise? P.S. I just had a thought, that we're facing the conflict here of two different minds, e.g.: mine (yet NOT well understanding object-oriented programming) and those who could be called gurus (from my current point of view) in OOP - what looks obvious for them for me arises new questions. I think that's why this question is being asked over and over again. Personally for me it's gotten more clear after all but still there are things to clarify.