The settings loader is a library that loads settings from several sources (currently only XML is supported).
This loader loads the settings from an XML file with a given structure:
<!-- settings.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<s:abstract xmlns="http://github.com/superruzafa/settings-loader">
<country>Japan</country>
<s:settings>
<company>Nintendo</company>
</s:settings>
<s:settings>
<company>Sony</company>
</s:settings>
</s:abstract>
<?php
use \DomDocument;
use Superruzafa\Settings\Loader\XmlLoader;
$doc = new DomDocument();
$doc->load(__DIR__ . '/settings.xml');
$loader = new XmlLoader($doc);
$loader->load();
$settings = $loader->getSettings();
// $settings = array(
// array(
// 'country' => 'Japan',
// 'company' => 'Nintendo',
// ),
// array(
// 'country' => 'Japan',
// 'company' => 'Sony',
// )
// )
In essence you can create your own XML settings file following these steps:
- Define in your XML a namespace pointing to
http://github.com/superruzafa/settings-loader
- Use the two reserved tags in that namespace for define settings entries:
<abstract>
and<settings>
- The namespaceless tags will be used as key-value pairs and will build the settings entries.
Both <abstract>
and <settings>
tags define a context (or change the previous one).
However, the <settings>
takes the current context and creates a settings entry in the global settings list.
Summarizing, you should use <abstract>
when you want to define a global context that would be overrided by concrete context using <settings>
.
Both <abstract>
and <settings>
nodes inherit the values defined by its ancestors and could be combined for create large collections of settings easily.
These tags could be nested:
<s:settings>
<s:abstract>
<s:settings>
<s:settings>
...
</s:settings>
</s:settings>
<s:settings>
<s:abstract>
...
</s:abstract>
</s:settings>
<s:abstract>
<s:settings>
...
</s:settings>
</s:settings>
An <abstract>
or a <setting>
node are allowed to define their context using both elements and attributes. These two examples would create the same settings:
<s:settings>
<language>PHP</language>
<purpose>Web and more</purpose>
</s:settings>
<s:settings language="PHP" purpose="Web and more" />
<s:settings language="PHP">
<language>PHP</language>
</s:settings>
You can use the method that better fits your needs.
When a key appears twice or more within the same context then the values for that key are interpreted as an array, instead of preserving the last defined value:
<s:settings>
<colors>red</colors>
<colors>green</colors>
<colors>blue</colors>
</s:settings>
// array(
// array('colors' => array('red', 'green', 'blue'))
// )
When inheriting, the child settings overrides its parent:
Reason: otherwise settings nodes having keys that already are defined by its parent would always append its value to the one from its parent, creating an array.
<s:settings>
<colors>black</colors>
<colors>white</colors>
<s:settings>
<colors>red</colors>
<colors>green</colors>
<colors>blue</colors>
</s:settings>
<s:settings>
<colors>transparent</colors>
</s:settings>
<s:settings>
// array(
// array('colors' => array('black', 'white')),
// array('colors' => array('red', 'green', 'blue'))
// array('colors' => 'transparent')
// )
String values could be considered as templates.
When an string contains something like {{ username }}
the parser looks in the current context the value associated to the key "username" and makes a replacement.
<s:settings>
<language>PHP</language>
<string>I like {{ language }}</string>
</s:settings>
// array(
// 'language' => 'PHP',
// 'string' => 'I like PHP',
// )
You can chain even more complicated interpolations and hierarchies:
<s:abstract>
<s:settings who="I">
<language>PHP</language>
<string>{{ who }} {{ preference }} {{ language }} {{ how-many }}</string>
<preference>like</preference>
</s:settings>
<how-many>so much!<how-many>
<preference>love</preference>
</s:abstract>
// array(
// 'who' => 'I',
// 'language' => 'PHP',
// 'string' => 'I like PHP so much!',
// 'preference' => 'like',
// )
Non-existing keys are replaced by an empty string, generating a warning.
<s:settings>
<string>My name is {{ name }}</key1>
</s:setting>
// array(
// array (
// 'string' => 'My name is ',
// )
// )
Cyclic recursive resolution will end with an empty string, generating a warning:
<s:settings>
<key1>Need {{ key2 }}</key1>
<key2>Need {{ key3 }}</key2>
<key3>Need {{ key1 }}</key3>
</s:setting>
// array(
// array (
// 'key1' => 'Need Need Need ',
// 'key2' => 'Need Need ',
// 'key3' => 'Need ',
// )
// )
Array interpolations are replaced by "<array>"
and a warning is generated:
<s:settings>
<seasons>Spring</seasons>
<seasons>Summer</seasons>
<seasons>Autumn</seasons>
<seasons>Winter</seasons>
<year>A year is composed by {{ seasons }}</year>
</s:setting>
// array(
// array (
// 'seasons' => array('Spring','Summer','Autumn','Winter'),
// 'year' => 'A year is composed by <array>',
// )
// )