TooBasic: Using Config Files
Is it necessary an explanation?
Well yes, you already know every site has its own configuration files and probably several logic blocks inside a site have their own configurations, so what's new about this?
TooBasic provides a mechanism to store and easily find two kind of configuration files:
- user defined configuration files
- and system configuration files.
User defined configuration files
To make it easier, we'll start with user defined configuration files. Let's suppose you have a site that sells three types of paperboard boxes, each type has a name, width, length and depth, and you want to put such configuration inside a file you can access from anywhere in your site. For this example we're going to create a JSON configuration file with the next content and store in ROOTDIR/site/configs/boxes_types.json:
{
"types": [{
"name": "small",
"width": 5,
"length": 6,
"depth": 10
},{
"name": "medium",
"width": 10,
"length": 12,
"depth": 20
},{
"name": "large",
"width": 20,
"length": 24,
"depth": 40
}]
}
Why JSON? Well JSON is an object specification very easy to use through
json_decode() in PHP and it's already a javascript object you can use directly
in your scripts.
If you want to keep things privately and access only from PHP files, you can
create a file called ROOTDIR/site/configs/boxes_types.php containing something
like this:
<?php
$boxesTypes = [
[
"name" => "small",
"width" => 5,
"length" => 6,
"depth" => 10
], [
"name" => "medium",
"width" => 10,
"length" => 12,
"depth" => 20
], [
"name" => "large",
"width" => 20,
"length" => 24,
"depth" => 40
]
];
Now, how do you access them?
Here is when the singleton Paths comes in handy, such singleton provides a
method called configPath() that helps with these files.
Take a look at this examples:
<?php
//
// This line will find and return the path 'ROOTDIR/site/configs/boxes_types.php'.
echo \TooBasic\Paths::Instance()->configPath("boxes_types")."\n";
//
// This line will find and return the path 'ROOTDIR/site/configs/boxes_types.json'.
echo \TooBasic\Paths::Instance()->configPath("boxes_types", \TooBasic\Paths::EXTENSION_JSON)."\n";
Where is the magic in this? The real magic comes when you install a module, if it has a file named boxes_types.json in its configs directory, it will be overriding our first file which is useful if such module improves your site giving it the ability to handle other types of boxes.
I want 'em all!
At the end of the previous section we used the word "overriding" and it may no be what you wanted to hear (... I mean, read), well there's a work around for that and it will look like this:
<?php
print_r(\TooBasic\Paths::Instance()->configPath("boxes_types", \TooBasic\Paths::EXTENSION_PHP, true));
print_r(\TooBasic\Paths::Instance()->configPath("boxes_types", \TooBasic\Paths::EXTENSION_JSON, true));
Now we are using a print_r because we are going to get an array with all found
paths.
Automatic config files
As we said, there's a second type of configuration file TooBasic uses. This files are automatically loaded (modules first and then site) and they are not overridden between them.
config.php
If you store a file at ROOTDIR/site/configs/ and/or ROOTDIR/modules/<modname>/configs and name it config.php, it will be loaded automatically. This allows you to give specific configurations for your site or your modules, respectively.
config_shell.php
This kind of file loads automatically before config.php but if only the current execution is been run from a shell command line.
config_http.php
Same than the previous one, but when it's not run from a shell command line.
Site config file
The last automatic file is always stored at ROOTDIR/site/config.php and allows you to give the final touches.
Summary
So, how is it again?
- First, from every module folder and then the folder ROOTDIR/site:
- it loads every config_http.php if it's not shell,
- then every config_shell.php if it is shell,
- then every config.php,
- and finally it loads ROOTDIR/site/config.php.
And if it doesn't find any of these files, it won't say a thing and do as if there were no problems.
Easier with JSON
If you decide to use JSON files for your configurations, there's an easier way to access then and use their properties.
Let's say we go back to that example of ROOTDIR/site/configs/boxes_types.json and we want to use it, you can write something like this:
. . .
print_r(\TooBasic\Managers\ConfigsManager::Instance()->boxes_types->types);
. . .
This example shows that accessing the singleton ConfigsManager you can load your
JSON configuration file and use its properties the same way you do with any basic
object. Also, if you're writing inside a controller or a model, you can write
something like this using magic properties:
. . .
protected function basicRun() {
print_r($this->config->boxes_types->types);
}
. . .
Multiple files
Let's say you created two or more of these files, all with the same name and scattered across your plugins, each one with different properties. Now let's say you want to load them all as if they where one configuration file, for that you can write something like this:
. . .
protected function basicRun() {
print_r($this->config->boxes_types(GC_CONFIG_MODE_MULTI)->types);
}
. . .
Remember that when all these files are loaded, properties specified in more than one JSON configuration file will override properties with the same name loaded in a previous file.
Merged files
A third option is to load multiple files with a basic merging policy writing something like this:
. . .
protected function basicRun() {
print_r($this->config->my_config(GC_CONFIG_MODE_MERGE)->types);
}
. . .
This mechanism makes these considerations:
- Every top level property that has a list as value (an array) appends new values.
- Every top level property that is an object absorbs new properties and replaces duplicates.
- Other top level properties get replaced.
For example, given this JSON:
{
"common": "something",
"list": [1, 2],
"settings": {
"prop1": 1,
"prop2": 2
}
}
And then this second JSON:
{
"common": "something else",
"list": [2, 3],
"settings": {
"prop2": 22,
"prop3": 33
}
}
The result of merging them will result in something like this:
{
"common": "something else",
"list": [1, 2, 2, 3],
"settings": {
"prop1": 1,
"prop2": 22,
"prop3": 33
}
}
Interpreters
Let's say that we need a way to get the widest box in our example and we want to OOP compliant. If we look at how our files are loaded will find that it a basic object and if we want to get the widest box, we need run some external function like:
function widest() {
$boxes = \TooBasic\MagicProp::Instance()->config->boxes_types;
$result = false;
foreach($boxes->types as $box) {
if(!$result || $box->width > $result->width) {
$result = $box;
}
}
return $result;
}
This would work, but nicest way would be to run $boxes->widest().
Here is where interpreters are useful.
Basically, when you load a configuration file, instead of returning a simple
object, TooBasic can return an specific object type containing you logic.
Following this example we'll create a folder ROOTDIR/site/includes at and a file
at ROOTDIR/site/includes/BoxesTypes.php with this content:
<?php
class BoxesTypesConfig extends TooBasic\Config {
public function widest() {
$result = false;
foreach($this->types as $box) {
if(!$result || $box->width > $result->width) {
$result = $box;
}
}
return $result;
}
}
Now we tell TooBasic how to load this file by adding the next line at the end
of ROOTDIR/site/config.php:
$SuperLoader['BoxesTypesConfig'] = ROOTDIR.'/site/includes/BoxesTypes.php';
And that's it, now you can use it in the same way you've been using it and you may
also use $boxes->widest().
Suggestions
If you want you may visit these documentation pages: