Php : what is better for configuration variable ? variable or parameter? - php

i have an application done in php and all configuration variables are loaded in a big $conf variable at the beginning of the script.
What is the better way to communicate this configuration variable to all other functions ?
make it a parameter of every function ? or use it with "global $conf;" statement in every function ?
is there a better way to do ?
Thanks

Use PHP constants.
For ponies sake, avoid using global variables at all costs :)
EDIT
Some explanations about "avoiding global variables at all costs" and possible alternatives:
https://stackoverflow.com/questions/357187/when-are-global-variables-acceptable/357361#357361
http://my.opera.com/zomg/blog/2007/08/30/globals-are-evil
https://stackoverflow.com/questions/1285700/what-are-some-good-tips-for-a-new-php-developer (especially section Scope of the accepted answer)

Make a configuration class that stores the options. Make it a singleton PHP Manual describes that here. This is just an alternative to global variables. It would allow you to define a method to load options from a file or a php array and store them in the class. Other classes can use the configuration object by getting the single instance and accessing the data.
I think this is better than a global variable as the other answer also says. But it still lets you define options as arrays, or even nested arrays if you want (and set up your class accordingly)

Your use of a single global-scoped variable $conf is perfectly fine. Many PHP applications do that. But there are drawbacks to combat.
In particular it's often more effort to write global $conf in each function where you want to access them. In that case I would recommend a simple global wrapper function instead:
function conf($key, $sub="") {
global $conf;
if (defined($key))
{ return constant($key); }
elseif ($sub)
{ return $conf[$key][$sub]; }
else
{ return $conf[$key]; }
}
This allows you to write conf("setting1") or conf("main", "opt3") whereever you need it. Still you can access the global $conf where that is more suitable. As extra bonus you can make this wrapper function more intelligent, by allowing it to query alternative settings etc. Also see how easy it is to also sneak in conf("CONSTANT") support.
Keeping this adds some flexibility in defining your configuration settings. Personally I use a similar approach, albeit with defining the array step-wise rather than at once:
$app_config["title"] = ...;
$app_config["editor.btns"] = ...;
define("RESTRICTED_MODE", true);
I'm preferring the array() approach, but transitioning to an ini-file for storage at a later point is not a problem. Also you can still make your config array read-only if the need arises. For that just define an:
class Read_Only_Array extends ArrayObject { function offsetSet() {} }
$conf = new ReadOnlyArray($conf);
So it's still accessible as array, but you easily established what others use cumbersome registries or syntactic workarounds for.
The "globals are evil" meme is completely baloney. It's parrotted on SO by cargo cult programmers with a desire for oversimplification and newcomers who glance over bold headlines without understanding the language semantics.
In your case, you just use a single $conf variable, and do not pollute the shared scope. When it is coherently accessed from the whole application, then it's not an issue. You should however strictly avoid to modify contents at runtime (use Read_Only_Array if need be). Create a secondary $app_var[] aray for that, and keep your config settings static.

Related

PHP class for all site "globals" instead of define / global

I'm trying to improve my website engine. So I can stop setting global $vars inside functions
So now I'm setting all my global site vars with this instead:
define('ROOT_prefix', 'mysitename_');
define('ROOT_support', 'support#mysite.com');
I can access them anywhere. But it does not feel as good (or smart) practice..
I know very little about classes.. but couldn't/should't I use a class for this instead?
This works:
class ROOT {
public static $prefix = 'mysitename_';
public static $support = 'support#mysite.com';
}
And then anywhere on my site I can use this (even inside functions):
echo '<h1>Please contact support at: '.ROOT::$support.' </h1>';
Is this a good way, or is there a better way?
If the value of these "globals" will not be changed for the entire run-time of the script, then you absolutely should use constants, as this is exactly what they are for.
You should keep them all centralized in a common include file for readability.
(Edit based on comments follows)
Since it looks like you're using constants for some kind of localization of content, it might be prudent to use a class for this. As I have said: using constants for non-changing values in a procedurally oriented script isn't bad practice in itself, but in the context of localization, there are better ways.
One such would be to create a class with some static methods to translate a string based on the passed ini file, this would be in line with the dependency injection mentioned in other comments and answers here.
An example of such a class would look something like this:
class Localizer {
public static function localize($langFile, $string) {
if (!file_exists($langFile)) {
throw new Exception($langFile . 'not found!');
}
$lang = parse_ini_file($langFile);
return (!empty($lang[$string])) ? $lang[$string] : false;
}
}
You can use it like this:
echo Localizer::localize('./english.ini', 'hello') . "\n";
echo Localizer::localize('./english.ini', 'email') . "\n";
This assumes an ini file that looks like this:
; english.ini
hello = 'Hello!'
email = 'test#test.com'
Realistically, this is probably a more "proper" way than declating a boat load of constants for each language your application runs in, but it is going to open the file every time you need to localize a string, which wouldn't be optimal for a very high volume application on a large system. But, as with a constant, you will be able to access the static methods of a class in the scope of any function in your application so long as the class was included beforehand. No need to use constants or declare globals.
The most proper and efficent way to do it would be to instantiate a class instead of using static methods, which would load the files into memory once and keep them there, eliminating the need to open the file for every string translated. But this would require that you are able to pass the variable containing the instantiation of this class to every function in your code that requires it, or declare it as global, which was exactly what you were trying to avoid in the first place.
So in order to do this, you would probably need to re-structure your code to allow for dependency injection throughout.
To continue with your current code and structure, you can continue using generated constants, which will be much messier, less "proper", and not expandable, but the advantage is that you will only read the ini files once, and keep them in memory.
Or you can use a static method, which is more "proper" but needs to read a file every time you localize a string, meaning that on large systems, it could cause some inefficiency. Realistically though, if your application in low volume, you will likely never see problems arise from this.
The main advantages of this method are expandability, and clean code. While declaring constants might be more efficient in terms of file opening and memory usage in the very short term, in most cases, it's not as expandable, because you can have an unlimited number of strings and language files, which means you could end up in a situation in the future where your loading thousands and thousands of constants every time your application loads.
If you use a class, and only load the files/strings that are needed by that specific user at run time, you can avoid this, no matter how many languages and strings you support.
Static class variables aren't any better than constants. They're still globally accessible values. There's no real change.
If you want to be improving your style, you should be using dependency injection. This simply means that you pass all variables that a function or class needs into the function/class as parameters. It's that simple, really. If you want to decouple your code, you need to create borders between different pieces. That means one piece does not "reach out" and get a global variable; instead you define that piece as accepting a parameter and write another piece that passes it that parameter.
Please read How Not To Kill Your Testability Using Statics for an in-depth explanation of this topic.
You may set variable to global when you need it. Just use global ${$variablename};.
where $variablename contain name of needs variable. For example it may be array keys or values.
Declaring your properties as public allows for their modification.
If you want them to be constants, as they were when created with define, you'll have to declare them as protected and use methods to access them :
class ROOT {
protected $prefix = 'mysitename_';
protected $support = 'support#mysite.com';
public static getPrefix(){
return $this->prefix;
}
public static getSupport(){
return $this->getSupport;
}
}
This way is actually quite better than using define() actually.
It's a step forward to singleton patterns (http://en.wikipedia.org/wiki/Singleton_pattern).
Next step is the building of an Application class (ROOT name sounds fine) which would contain these constants, and perhaps load them from a configuration file.
In this Application singleton, you can build some main function, like an init() for a bootstrap, inclusion of other classes, database configuration, logging system, templating system, and so on...

Global Variables vs. Config Variables

I have custom config items in application/config/config.php.
Samples of my custom config items:
$config['website_title'] = 'ABC Website'; //Assume website title is fixed
.
.
.
etc
Now i can call $this->config->item('website_title') any where in my application. However, i don't find it efficient enough because i might have multiple $this->config->item('website_title') within the project. I came up with the following solution:
1.Create a function, within a helper, that return the config item as the following:
public function website_title() {
return $this->config->item('website_title');
}
2.Now i can call website_title() as many as i want.
Is this a good solution? Do you see any downsides?
Note: I try to avoid using global variables because i tried it and i
faced many unnecessary problems such as undefined variables,
surprised!
What do you mean by "efficient"? Runtime efficiency? Coding efficiency? Clarity?
In terms of runtime efficiency Truth's suggestion of using is perhaps the simplest and best. However, I prefer to code using a strict class/object implementation, and in reality defines are just global constants.
If you profile the vast majority of scripts, you will find that however you code your configuration referencing makes an immaterial impact on runtime, so I would suggest going for simplicity and clarity of coding every time.
One approach is to use a singleton class (there are lots of tutorials on doing this) and use the magic method __get() to allow you to dynamically overload parameter access. This is one case where I feel that you have to use a single class as these property methods only work with object (non-static) parameter references. Hence you can simply use:
$cfg = Configuration::get();
...
... $cfg->someConfigParam ... // to refer to a config parameter
...
... /* or even */ ... Configuration::get()->someOtherParameter ...
Note that $cfg in the above example essentially stores an object handle, so there is no material runtime cost in doing this, and you can put this statement at the top of each function or class constructor that references a config item if you don't want to litter your code with Configuration::get()->someOtherParameter type calls.
The Configuration::__get() access function , plus the class constructor can handle all the complexities of caching and access of the individual parameters. This also means that you con also encapsulate the source of the configuration: some application-specific D/B config table; one or more config files, ...; even cookies or URI parameters (so long as you include appropriate validation).
I personally don't recommend over loading with the __set() magic method as, IMO, overriding or setting a config parameter should be an explicit action, e.g. $cfg->setConfigItem( 'someValue', TRUE );
Here is a link to the documentation on my config class if you want some ideas.
I asked this sort of question on programmers once. I got a very good answer, simply use constants.
I.e. WEBSITE_TITLE

Do globals interfere with required files in PHP?

I need to edit a variable (array) that is defined outside of the function, so I can use it in another function further in. The easiest way I can think of is to define it as global inside the function, but I have many required files involved as well.
The documentation of global variables says that it can be used "anywhere in the program." Does that imply throughout all files (is it global in a sense of across all files) or is it just the file it's in (locally global, if that makes sense).
I did find a question about globals on this site that suggests passing it by reference, but I have this function implemented extensively in other files and requiring them to have an additional variable in their calls would be obnoxious to say the least.
If you define your variable global within the function, you will be referring to the globally scoped variable, and changes to that variable made within your function will be visible to other functions that use that global variable, whatever files they're in, so long as the inclusion / execution order is correct.
If the file you declare the global in is in memory, then that variable is available for you to use. But, if you don't include or require the file the global is declared in on a certain page, it will not be available to you.
Order is also important. If you try to call the global variable before the include or require of the file you set it in, it will be unavailable.
Globals are shared among all files. By the way, instead of declaring them with global $variable;, you should use $GLOBALS['variable'] to make explicit that you're accessing a global variable.
If a lot of functions grouped in a file require access to some common state, chances are you need to turn them into a class. That's pretty much the definition of a class.
Or you could turn the array into a class and have functions call methods on it.
Perhaps a singleton or a registry (2) could help.
Note that most OOP implementations pass a reference to the object as a method's first parameter, hidden (C++, PHP) or not (C, Python).

PHP best practices: repass variables from config file when calling functions or use global?

I have a program that I use on several sites. It uses require('config.php'); to set any site dependant variables like mysql connect info, paths, etc.
Let's say that I use one of these site-dependant variables in a function, like $backup_path.
This variable was initially declared in config.php, and does not appear in the main program file.
I need to access this variable in function makebackup($table_name); (also in a separate functions.php file).
Is it better to say
makebackup('my_table');
and then use "global $backup_path" inside the function, or is it better to call the function using
makebackup('my_table',$backup_path);
The argument for the first is that it keeps the main program flow simple and easy to understand, without clutter.
The argument for the second is that it might not be obvious that the variable $backup_path exists after some time has passed, and debugging or reworking could be difficult.
Is one or the other of these techniques "standard" among professional programmers? Or should I be using $_SESSION to declare these global variables?
The second alternative,
makebackup('my_table', $backup_path);
is a reusable function and therefore generally preferable. The extra argument is not a big price for reusability.
If you are entirely sure that you'll ever use that function in that particular application only, and for $backup_path only, then maybe consider the global alternative. Even then it's good to check that the global variable actually exists. And be aware that it's extremely difficult to get rid of globals once you start using them.
Remember that you can set a default value for your function:
function makebackup($table, $dir = CONFIG_BACKUP_PATH)
That way you won't have to supply the variable in the default case, you can simply assume that the configured backup path is the default.
This assumes that you are using constants, not global variables.
I'm think you must use Singleton of Factory class config for this purposes.
function makebackup($table)
{
$backup_path = ConfigFactory().getConfig($some_site_specific_data).getBackupPath()
mysqldump($table, $backup_path)
}
Passing references around is far easier to test (you can give mock configuration objects). Globals less so. You can assert that the reference is not null on the method. I would call testability best practice.
Label that global variable
Personally, I have also taken to marking global variables very clearly. If I must use them, I want to be clear about them.
So here, I'd rename $backup_path to $GLOBAL_backup_path. Every time I saw it, I'd know to be careful with it.
An alternative option is using php constants with define().
Your config.php will set constants for every parameter (mysql connection, css style, wathever). Then you will not need to pass variables to functions nor using global.
One downside is that you can define only booleans, floats, strings or integers, no complex data structures.
Not sure there's really a 'right' way to do it, but another option would be something like this:
function makebackup($table, $backup_path = '') {
if ( $backup_path == '' ) {
if ( isset($GLOBALS['backup_path']) ) {
$backup_path = $GLOBALS['backup_path'];
}
else {
die('No backup path provided');
}
}
}
That way you can either pass in the value (for testing and future use) or if you don't pass it in, then the function will look for a possible global variable.

"Namespaces", constants and multiple PHP includes

I have some PHP code similar to the following:
foreach ($settingsarray as $settingsfile)
{
include ($settingsfile);
// do stuff here
}
$settingsarray is an array of file names from a particular folder.
The problem is, that each $settingsfile defines constants (with the same names), which of course, can not be redefined.
What possible methods are there to prevent errors occurring in this situation?
Two options I can think of include, changing all the constants to variables and using PHP namespaces.
However, I'm not sure how I would go about using namespaces, it would require the declaration at the start of every $settingsfile? Is there a method of isolating constants, ariables and functions without using namespaces?
What I would really love, is to be able to do something as simple as:
foreach ($settingsarray as $settingsfile)
{
{//added braces to indicate where the isolation is
include ($settingsfile);
// do stuff here
}//what happens in here does not affect what happens outside of here
}
I should just note, this is part of a new feature, and is the only part of the code that loads all the $settingsfiles. The rest of the code only ever loads one file at a time. One reason I am using constants is so that I don't have to worry about defining variables "global" to be able to access them inside functions.
My answer is somewhat complex, but should work for you quite nicely. I'm assuming you have a ton of these settings files, since you're so averse to changing each one individually.
If you're able to use namespaces, I'll assume you've already upgraded to PHP 5.3RC2. Copy the following into a .php file, and change the namespace to your liking:
<?php
namespace myapp\config;
function define($key, $val) {
Config::set($key, $val);
}
class Config {
private $vars = array();
// This class should not be instantiated
private function __construct() {}
public function set($key, $val) {
self::$vars[$key] = $val;
}
public function get($key) {
return isset(self::$vars[$key]) ? self::$vars[$key] : NULL;
}
}
?>
Include that in your code, and now making everything work is a simple matter of changing the
<?php
in your settings files to
<?php namespace myapp\config;
If you have a ton of them, a quick 'sed' command in your terminal should take care of it quite nicely, all in one fell swoop.
If I lost you on how to use my code, here's an example:
<?php
require_once('the_php_file_i_just_gave_you.php');
use \myapp\config\Config;
foreach ($settingsarray as $settingsfile) {
include ($settingsfile);
$varname = 'key';
echo "In this file, '$varname' equals: " . Config::get($varname);
}
?>
Good luck!
Okay, so if I'm understanding you correctly, inside that loop you'd like to do things with the constants defined inside each $settingsfile and then basically get rid of them after each loop?
Unlike variables, a constant is a value that, once set, cannot be changed or unset during the execution of your script
src
Your only option is to change the define() declarations to variables.
not easy but you could read the php file with file_get_contents($settingsfile) then rewrite the constants, and eval the code.
$settingsstr = file_get_contents($settingsfile);
$settingsstr = preg_replace('/<\?\w*(.*)/', '\\1', $settingsstr);//get rid of the php open tag
$settingsstr = preg_replace('/define\("(\w+)"/', 'define("NAMESPACE_\\1"', $settingsstr);
eval($settingsstr);
You have a few options:
Use PHP Namespaces
Surprisingly, PHP actually does have support for namespaces. I was very surprised to find this since I have been working in PHP for years and have never heard of this feature. It is likely a newer feature and one that is not commonly used, so I would suggest not using this method
Use Class Constants
You could use classes and class constants to load your settings. Class constants are local to the defining class, so you will not need to worry about name collisions within the class. You would probably need to do more than include the file, but you could call a method on each class, such as defineConstants() or loadConfiguration() that would define the application constants, keeping the class constants for internal use.
Use Functions to Extend the Settings
Another idea is to create a settings array and separate functions to 'extend' those settings. That way you can over-write any configuration in subsequent functions without causing an error. Finally, if you want to put those in constants, you could loop over the array and use define() to define the constants.
There are many other ways to accomplish what you are trying to do, but these are just a few ideas.
A settings file should not contan PHP code.
It should be some kind of "standard" format.
http://www.php.net/parse_ini_file
In the first step (in your foreach) you read all files
Collect the variables into a temporary array
In another loop over this array, you can define them as constants.
If you routinely feel the need to "redefine constants" then something is amish in the application design.

Categories