I’m determined to get a decent answer to this, because while I have my own approach (constants) I’m not convinced I’m doing it as efficiently as I could be.
So I’m editing the entire question, to add actual code for a more clear description, and am adding a bounty (as much as I can).
(Sorry Ixrec, your answer might not stand any more).
Basic overview of my (simple) framework
- htaccess sends all requests to Front Controller (index.php)
- Front Controller loads Bootstrap.php
- Bootstrap loads all other framework things – e.g. Error reporting
setup; Auto loading; PHP Version check; Router;
Problem
I’m stuck at deciding on the best way to store “simple data” that’s used throughout the framework. Some used 3-4 times, some only once (at the moment).
Simple data is things like: PHP Version Running; Minimum PHP Version framework requires to operate; Allowed characters in the URI; Framework data (name, support contact details);
While arrays and constants are simple and affective, they instantly pollute the global space, and constants do so indefinitely (end of stack).
Classes seems good approach in terms of data is only called where needed, and so global space is not just immediately polluted with all data.
However, a class to just store some small data seems over kill (i.e. “5.4.0”).
So I am conflicted with what option to choose, as all have pros and cons.
I can choose, but wanted some feedback from more seasoned devs.
Requirements
1)
Data to be re-usable.
Not all, but most data will likely be used in multiple classes/files/templates (or at least want it open for future possibilities).
2)
Avoid over polluting global data as much as possible (i.e. just including a file with 50 data items in an array or 50 constants).
While the data needs to be re-usable, and so global (i.e. constants) is the easiest option, I am trying to avoid just shoving it all into global instantly.
I also feel it’s best to keep all this data as one type, ie ALL in an array, or ALL in constants, and all in one place – making it easy to manage.
But if that is not the best approach do say – remember while it is different data for different components, so arguably some could be in classes, some array, some constants etc, they have a common ground in that they are ALL simple data. So whatever option is best it will be likely be best for all this data.
Examples/Options
I have a “PHP Version Check“, which is a class loaded early on in Bootstrap which checks if the running (server) PHP version is adequate for what the framework requires to operate.
So using the data required for that component as an example of the data problem I face:
NOTE: There are currently about 50 data items. The examples below only have one data item, which makes it appear as if the issues don’t matter, especially polluting global space (the total data items will likely also grow in the future).
OPTION 1 – ARRAY
$ConfigArray['phpVersionRunning'] = PHP_VERSION;
$ConfigArray['phpVersionRequired'] = '5.4.0';
PROS
Data will be in a single array, a simple approach.
CONS
Data is immediately pushed into global.
Data can be altered.
Array name and array index name being long enough to be descriptive means long code in other files where this data is needed.
e.g. if ( $ConfigArray['phpVersionRequired'] !== $ConfigArray['phpVersionRunning'] )
OPTION 2 – CONSTANTS
define ('PHP_VERSION_RUNNING', PHP_VERSION);
define ('PHP_VERSION_REQUIRED', '5.4.0');
PROS
This is the most simple and clean looking approach, where used in other files constants are clear in their purpose etc.
e.g. if ( PHP_VERSION_RUNNING !== PHP_VERSION_REQUIRED )
Instantly accessible wherever needed, including in classes.
Data cannot be altered.
CONS
They are all pushed into global instantly.
They remain in global right through Front End, controllers, templates, view, etc, and won’t be needed by them.
OPTION 3 – CLASS
class PhpVersionData
{
private $VersionRunning;
private $VersionRequired;
protected function __construct()
{
$this->VersionRunning = PHP_VERSION;
$this->VersionRequired = '5.4.0';
}
protected function getVersionRunning()
{
return $this->VersionRunning;
}
protected function getVersionRequired()
{
return $this->VersionRequired;
}
}
PROS
Data is only created at the first point where it’s needed.
CONS
Firstly, this just looks way OTT for just basic data.
Constructor and Methods can be protected, so class would need extending so not instantly polluting global space, but then am treading into singleton territory and also tight coupling (?).
If public methods/constructor instead, then am back to potential global pollution issue.
OPTION 4 – Separate data loading:
Another approach is to use constants, or array or variables, but stored in separate files. So each file could be included separately and only when needed.
This avoids everything being immediately in global space, and with array/constant I could unset them once they are no longer needed.
On paper, this sounds like a great idea, but in practice it seems very fiddly, and maintaining it would be harder as data is stored and called all over the place.
Summary
They all have pros and cons which conflict each other. While one option seems ideal, such as constants, it has a major con which pollutes global space indefinitely.
In honesty, I’m swayed to a config array, as although it is pushed into global from the start, I can clean it up at points in the stack where it’s no longer needed.
But there are pros and cons to all approaches, and would like to know what is the most robust approach juggling global space pollution, maintainability, ease of use/readability, etc.
6
I don’t particularly care for any of your proposed approaches simply because it’s tying your classes directly to your configuration. Which means that once you do decide on an approach then you are pretty much stuck with it.
Instead, you should consider adopting more of a dependency injection approach.
class Bootstrap
{
public function __construct($phpVersionRequired)
{
if (version_compare(PHP_VERSION, $phpVersionRequired) < 0) {
The point being is that your actual code neither knows nor cares where $phpVersionRequired comes from. Your question talks about global data and global accessibility but you don’t really want that.
Of course whomever creates the Bootstrap object needs to know where $phpVersionRequired comes from. There raises the question of what do you mean by “simple framework”. Like @Ixrec has suggested, you best bet might be to look at some existing solutions. If your simple framework includes a dependency injection container then most of the injection stuff can be abstracted away.
The Silex micro-framework (http://silex.sensiolabs.org/) might be a good starting point. The Symfony 2 framework (http://symfony.com/doc/current/reference/index.html) represents the other end of the spectrum.
What is relevant is that both use the same SymfonyConfig component (http://symfony.com/doc/current/components/config/introduction.html).
So maybe spend some time researching existing solutions and perhaps rethink this global approach to locating dependencies. Inject your dependencies instead of trying to pull them out of some global resource.
===========================================================
Update 06 Mar 2015
So the question appears to be about where to actually store the config options and how to access them. My answer is to use a dependency injection container. In PHP, Pimple is a simple and widely used container: http://pimple.sensiolabs.org/
use PimpleContainer;
$container = new Container();
// Here is a config parameter
$container['php_version_required'] = '5.5.5';
// Here is a service definition which injects the above parameter
$container['bootstrap'] = function ($c) {
return new Bootstrap($c['php_version_required']);
};
// Here we get our bootstrap object with it's dependency injected
$bootstrap = $container['bootstrap'];
$bootstrap->run();
Of course you will probably want to read the values of your config options from some sort of parameter file but the values themselves would be saved in your container. The more advanced Symfony 2 container (http://symfony.com/doc/current/components/dependency_injection/introduction.html) has additional support for reading config files as well as caching for improved performance.
6
If you have enough of these little configuration values that you feel the need to put them in another file, then they should go in a configuration file, not a source code file. There are countless ways of implementing config files, but I believe PHP has some standard approaches.
To answer your numbered questions more directly:
1) They’re never really “necessary”, but they’re definitely useful once you have a lot of data. But again, they should be actual data files, not just code files that contain object literals.
2) A class whose sole purpose is to provide access to these values is not so good. But a class whose sole purpose is to parse config files is perfectly fine (and you’ll typically get this from a library).
3) I believe the link I gave above is a good place to start.
4