Configuration

1. Overview

The configuration concept of the APF introduces an abstraction layer for configuration access. This enables you to address various physical sources (e.g. files or databases) as well as various formats (e.g. INI, XML).

Technically spoken, this abstraction is achieved by separation of storage formats and representation as well as the introduction of format-dependent access components. For this reason, a central instance (ConfigurationManager) deals with the management of the access layer components (e.g. IniConfigurationProvider). The access layer components - also referred to as configuration providers - can be registered with the ConfigurationManager for a dedicated scheme.

As mentioned, the configuration is represented by a common definition that is independent to the configuration scheme. For this reason, the Configuration interface must be implemented along with each provider to be able to load and save a configuration.

2. Technical concept

2.1. Configuration scheme

The technical concept of the APF defines, that configurations may depend on the following parameters:

  • Base folder The configuration providers shipped with the APF follow the recommendation to separate configuration and program code. For this reason, all configuration files are expected to be stored under the /config folder that resides in parallel to core, modules, etc.
  • Namespace: This section describes the physical (or virtual) storage location concerning the relative position to the base folder. By convention, this path is similar to the application's namespace to easily associate the configuration. As you know from including classes or components, the namespace parts are separated by "\" (as PHP namespace elements).
  • Context: The context impresses the usage of a certain software (e.g. module) within a surrounding application (e.g. website). Since the APF is designed to operate various installations and applications with one single code base, the context can be used to separate the configurations of modules regarding their installation.
  • Environment: The environment attribute is to distinguish between the different environments the application is installed to (e.g. test server, production servers). It enables you to install and configure a software package on different target environments without changing one single line of code or configuration.
  • Name: The name of the configuration defines the physical name of the configuration file for file-based providers or the virtual name of the configuration converning e.g. database-driven providers respectively.
  • Extension/type: The file extension is used as the configuration type. Each extension can be mapped to an appropriate provider.

The subsequent table describes the parts in detail:

Installation folder Base folder Namespace Context Environment Name Extension/Type
/APF /config widgets\calendar siteone DEV labels .xml

The example results in the following configuration path:

Code
/APF/config/widgets/calendar/siteone/DEV_labels.xml
The scheme presented here is the default scheme of the APF. Please note, that the scheme may vary by the ConfigurationProvider implementation that is currently used!

Please note, that the values for Context and Environment are usually configured within your bootstrap file (index.php). The context can be applied to the Page controller and Front controller using the setContext() method. The environment must be defined via Registry:

PHP code
use APF\core\frontcontroller\Frontcontroller; $fC = Singleton::getInstance(Frontcontroller::class); $fC->setContext('my\context'); // environment definition for both cases Registry::register('APF\core', 'Environment', 'TESTBOX');

All further parameters of the configuration resource are applied while loading explicitly or implicity.

2.2. Software components

The APF brings the listed interfaces and implementations to define the configuration software components:

  • ConfigurationManager: The ConfigurationManager component manages the ConfigurationProvider instances and is the central component for read and write access to configurations. This includes resolving the provider that is responsible for the current file extension and caching of configurations that have already been loaded within one request.
  • Configuration: The Configuration interface describes the object representation of a configuration or a configuration section respectively. Each provider has it's own implementation and the entire configuration representation is composed of these implementations. One section within a configuration is again represented by the provider-dependent configuration object implementation.
  • ConfigurationProvider: The ConfigurationProvider (interface) describes the provider scheme. It defines the methods the ConfigurationManager uses to delegate read and write access to.
  • ConfigurationException: This exception is thrown concerning the API definition in case of any error dealing with configurations. The developer is free to catch the exception using a try-catch statement.

To ease reading and writing configurations the getConfiguration() and saveConfiguration() convenience methods are provides within classes that derive from APFObject. Deleting configurations can be done using the deleteConfiguration() method.

Since your configuration files contain sensitive data the /config folder and your APF installation should not be accessible via HTTP. Thus, we recommend to store these files outside of your web root or to place a .htaccess file with the following content to your /config directory:
Code
<Location "/path/to/APF/installation/config"> Order Allow,Deny Deny from all </Location>

3. Existing providers

The release of the Adventure PHP Framework shipps four provider implementations that are described in detail within the following chapters.

Besides the access model described in this chapter section 6.3 introduces a simplified model based on path queries. Using this notation allows to shorten your source code and this gain clarity. It's your freedom of choice which model to go for as both are fully supported.

3.1. INI provider

The INI provider implements the default configuration format of the APF. It follows the ini file scheme.

Loading a file with the content

APF configuration
[showCaptcha] ActionClass = "APF\modules\captcha\biz\actions\ShowCaptchaImageAction"

(extract of a front controller configuration) can be done within a document controller as follows:

PHP code
class FooController extends BaseDocumentController { public function transformContent() { $config = $this->getConfiguration('APF\widgets\calendar', 'labels.ini'); ... } }

The $config now contains an instance of the IniConfiguration class - the format of the IniConfigurationProvider -, that handles .ini files in default configuration.

The object structure returned by the provider depends on the definition of the configuration file. Here are the "rules":

  • INI sections concerning the INI file format are returned as section objects following the Configuration interface. Though, the section presented above can be requested by
    PHP code
    $config->getSection('showCaptcha')
  • The keys within one section are provided as values within this section. Dedicated values can thus be queried using the getValue() method:
    PHP code
    $config->getSection('showCaptcha')->getValue('InputParams')
  • One specialty of the INI format is that keys containing dots (".") are resolved as sub sections. For the configuration above this means that the showCaptcha section contains a further section FC. Dedicated values of the latter section can be requested using
    PHP code
    $config->getSection('showCaptcha')->getSection('FC')->getValue('InputParams')
    The provider thereby supports unlimited sub section support. Each sub-section is again an instance of the IniConfiguration class.

Write access to INI configurations is also intuitive:

PHP code
class BarController extends BaseDocumentController { public function transformContent() { $config = new IniConfiguration(); $section = new IniConfiguration(); $section->setValue('year_label', 'Jahr'); $section->setValue('month_label', 'Monat'); $section->setValue('day_label', 'Tag'); $config->setSection('global-labels', $section); $this->saveConfiguration('APF\widgets\calendar', 'labels.ini', $config); } }

Furthermore, you can save configurations that have been loaded by the provider prior to manipulation.

3.2. XML provider

The XML provider supports the APF configuration scheme. For this reason, the following scheme was defined (extract from the data mapper configuration of the guestbook module (German)):

XML code
<?xml version="1.0" encoding="UTF-8"?> <configuration> <section name="GuestbookMapper"> <property name="servicetype">NORMAL</property> <property name="class">APF\modules\guestbook2009\data\GuestbookMapper</property> <section name="conf"> <section name="db"> <property name="method">setConnectionName</property> <property name="value">guestbook2009</property> </section> <section name="orm"> <property name="method">setORMInitType</property> <property name="value">NORMAL</property> </section> </section> </section> </configuration>

The scheme is as follows:

  • The entire configuration is surrounded by the configuration tag. This is the root element.
  • The configuration os composed by sections within the first level as you know from the INI configuration. These are represented by the section tag in any level. The name of the section is declared by the name attribute.
  • Within one section further sections and values can be defined. A value is represented by a property tag declaring the name of the key by the name attribute. The value is stored as the tag's content.

Access to XML configuration is nearly identical to INI configurations described in chapter 3.1:

PHP code
class FooService extends APFObject { public function doSomething() { $config = $this->getConfiguration('APF\modules\guestbook2009', 'serviceobjects.xml'); ... } }

The $config now contains an instance of the XmlConfiguration class - the format of the XmlConfigurationProvider -, that handles .xml files in default configuration.

The object structure returned by the provider depends on the definition of the configuration file. Here are the "rules":

  • XML sections at the first level are mapped to sections concerning the Configuration interface. Hence, you can query the GuestbookMapper using
    PHP code
    $config->getSection('GuestbookMapper')
  • The property tags of the sections are provided as values. Dedicated values can be retrieved by the getValue() method:
    PHP code
    $config->getSection('GuestbookMapper')->getValue('servicetype')
  • Every section tag within a section is provided as sub-section. In order to retrieve values from the db section use the following code snippet:
    PHP code
    $config ->getSection('GuestbookMapper') ->getSection('conf') ->getSection('db') ->getValue('method')
    The provider supports any number of sub sections. Thereby, each section is an instance of the XmlConfiguration class again.

Writing configurations is identical to the INI format (here: using a configuration that is directly loaded and manipulated before saving):

PHP code
class FooService extends APFObject { public function doSomethingElse() { $config = $this->getConfiguration('APF\modules\guestbook2009', 'serviceobjects.xml'); $config->getSection('GuestbookMapper')->setValue('servicetype', APFService::SERVICE_TYPE_SINGLETON); $this->saveConfiguration('APF\modules\guestbook2009', 'serviceobjects.xml', $config); } }

3.3. Db provider

The DbConfigurationProvider is a simple implementation of the APF configuration scheme to save configuration values within a database table. In contrast to the XML and INI provider implementations only a flat hierarchy is supported (configuration -> section -> value). But context, language, and environment dependent values are still possible.

Using the provider the following database table must be created:

SQL statement
CREATE TABLE IF NOT EXISTS `config_widgets_calendar` ( `context` varchar(50) NOT NULL, `language` varchar(5) NOT NULL, `environment` varchar(20) NOT NULL, `name` varchar(20) NOT NULL, `section` varchar(20) NOT NULL, `key` varchar(30) NOT NULL, `value` varchar(500) NOT NULL, `creationtimestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `modificationtimestamp` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', PRIMARY KEY (`context`,`language`,`environment`,`name`,`section`,`key`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Concerning the configuration scheme chapter, database configurations can be loaded as follows:

PHP code
class FooController extends BaseDocumentController { public function transformContent() { $config = $this->getConfiguration('APF\widgets\calendar', 'labels.db'); ... } }

The $config now contains an instance of the DbConfiguration class - the format of the DbConfigurationProvider -, that handles .db files in default configuration.

The object structure returned by the provider depends on the definition of the configuration file. Here are the "rules":

  • Sections (see column section) are created by grouping rows with the same context, language, environment, and name. These are the sections on the first level of the configuration. Each section can be queried by
    PHP code
    $config->getSection('main')
  • The values of the section can be retrieved by the getValue() method:
    PHP code
    $config->getSection('main')->getValue('icon')
Due to the lack of support of greater hierarchies the database configuration provider should only be used for simple configuration tasks. To gain performance, it may be used together with the MemcachedConfigurationProvider.

Write access is identical to the schemes described before:

PHP code
class BarController extends BaseDocumentController { public function transformContent() { $config = $this->getConfiguration('APF\widgets\calendar', 'labels.db'); $config->getSection('main')->setValue('icon', 'calendar_big.png'); $this->saveConfiguration('APF\widgets\calendar', 'labels.db', $config); } }

3.4. Memcached provider

The MemcachedConfigurationProvider has no own format but acts as a "mediator" provider that uses an existing provider for read and write access. It stores the configuration within a Memcached server to have it available request-independently.

In case the provider is registered for the file extension or type "mem" (see chapter Configuration of the providers) a memcached configuration can be loaded as follows:

PHP code
class FooService extends APFObject { public function doSomething() { $config = $this->getConfiguration('APF\widgets\calendar', 'labels.mem'); ... } }

The $config variable now contains the desired configuration. Therefore, the provider tries to load the configuration from the Memcached store. In case it has not been loaded before or has been marked deprecated the physical configuration file is loaded to deliver the requested configuration and fill the cache.

Writing a memory-based configuration includes an update to the Memcached store as well as writing the changes to the persistent file. The subsequent code box shows an example:

PHP code
class FooService extends APFObject { public function doSomethingElse() { $config = $this->getConfiguration('APF\widgets\calendar', 'labels.mem'); $config->getSection('global-labels')->setValue('day_label', 'Tag'); $this->saveConfiguration('APF\widgets\calendar', 'labels.mem', $config); } }

3.5. APC provider

The ApcConfigurationProvider has no own format but acts as a "mediator" provider that uses an existing provider for read and write access just like the MemcachedConfigurationProvider. It stores the configuration within a APC Shared Memory Segment to have it available request-independently.

In case the provider is registered for the file extension or type "mem" (see chapter Configuration of the providers) a memcached configuration can be loaded as follows:

PHP code
class FooService extends APFObject { public function doSomething() { $config = $this->getConfiguration('APF\widgets\calendar', 'labels.apc'); ... } }

The $config variable now contains the desired configuration. Therefore, the provider tries to load the configuration from the APC store. In case it has not been loaded before or has been marked deprecated the physical configuration file is loaded to deliver the requested configuration and fill the cache.

Writing a memory-based configuration includes an update to the APC store as well as writing the changes to the persistent file. The subsequent code box shows an example:

PHP code
class FooService extends APFObject { public function doSomethingElse() { $config = $this->getConfiguration('APF\widgets\calendar', 'labels.apc'); $config->getSection('global-labels')->setValue('day_label', 'Tag'); $this->saveConfiguration('APF\widgets\calendar', 'labels.apc', $config); } }

3.6. PHP provider

The PhpConfigurationProvider allows to use nested associative PHP arrays as storage format for configurations. One key advantage is that the PHP array syntax is easy to read and write. Secondly, you may e.g. use PHP constants directly within your configuration files.

The following example contains the XML configuration from chapter 3.2 re-written as PHP configuration:

PHP code
return [ 'GuestbookMapper' => [ 'conf' => [ 'db' => [ 'method' => 'setConnectionName', 'value' => 'guestbook2009' ], 'orm' => [ 'method' => 'setORMInitType', 'value' => 'NORMAL' ] ], 'servicetype' => \APF\core\service\APFService::SERVICE_TYPE_NORMAL, 'class' => 'APF\modules\guestbook2009\data\GuestbookMapper' ] ];

The scheme of PHP configurations is as follows:

  • The entire configuration is provided to the PhpConfigurationProvider by a simple return statement.
  • All array offsets that are assigned a scalar value are considered als values.
  • All array offsets that are assigned an array are considered as sections.

Access to PHP configuration is nearly identical to INI configurations described in chapter 3.1:

PHP code
class FooService extends APFObject { public function doSomething() { $config = $this->getConfiguration('APF\modules\guestbook2009', 'serviceobjects.php'); ... } }

The $config now contains an instance of the PhpConfiguration class - the format of the PhpConfigurationProvider -, that handles .php files in default configuration.

The object structure returned by the provider depends on the definition of the configuration file. Here are the "rules":

  • PHP sections at the first level are mapped to sections concerning the Configuration interface. Hence, you can query the GuestbookMapper using
    PHP code
    $config->getSection('GuestbookMapper')
  • The property tags of the sections are provided as values. Dedicated values can be retrieved by the getValue() method:
    PHP code
    $config->getSection('GuestbookMapper')->getValue('servicetype')
  • Every section tag within a section is provided as sub-section. In order to retrieve values from the db section use the following code snippet:
    PHP code
    $config ->getSection('GuestbookMapper') ->getSection('conf') ->getSection('db') ->getValue('method')
    The provider supports any number of sub sections. Thereby, each section is an instance of the XmlConfiguration class again.

Writing configurations is identical to the INI format (here: using a configuration that is directly loaded and manipulated before saving):

PHP code
class FooService extends APFObject { public function doSomethingElse() { $config = $this->getConfiguration('APF\modules\guestbook2009', 'serviceobjects.php'); $config->getSection('GuestbookMapper')->setValue('servicetype', APFService::SERVICE_TYPE_SINGLETON); $this->saveConfiguration('APF\modules\guestbook2009', 'serviceobjects.php', $config); } }

4. Extended configuration handling

In many application cases not only one configuration value is needed, but several or all keys.

For the first one the Configuration interface offers the getSectionNames() and getValueNames() methods. They return the names of all sections or configuration keys. In order to display all section names you can use the following code lines:

PHP code
foreach($config->getSectionNames() as $sectionName) { echo $sectionName; }

To print all keys of the current section use these lines of code:

PHP code
foreach($section->getValueNames() as $valueName) { echo $valueName; }

Manipulation of the configuration representation can be achieved using the interface methods removeSection() and removeValue. With help of these methods, the initial configuration file

APF configuration
[Default] key1 = "value1" key2 = "value2" key3 = "value3"

can be adapted by the PHP code

PHP code
$config = $this->getConfiguration('...', 'config.ini'); $section = new IniConfiguration(); $section->setValue('key1', 'value1'); $section->setValue('key3', 'value2'); $section->setValue('key2', 'value3'); $config->setSection('Special', $section); $config->getSection('Default')->removeValue('key2'); $this->saveConfiguration('...', 'config.ini', $config);

to look like this:

APF configuration
[Default] key1 = "value1" key3 = "value3" [Special] key1 = "value1" key2 = "value2" key3 = "value3"

In order to conduct checks on configurations interface Configuration offers two methods: hasSection() and hasValue(). hasSection() checks whether a section is physically available and hasValue() executes the same check but for values.

The following code can be used to evaluate if a configuration section is existing:

PHP code
$config = $this->getConfiguration('...', 'config.ini'); if($config->hasSection('Default')) { echo 'Section "Default" existing!' } else { echo 'Section "Default" NOT existing!' }

To check whether key1 is defined you may want to use the following code block:

PHP code
$config = $this->getConfiguration('...', 'config.ini'); if($config->getSection('Default')->hasValue('key1')) { echo 'Value "key1" existing!' } else { echo 'Value "key1" NOT existing!' }

5. Configuration of the providers

The providers listed in chapter 3 include some more functionality. This is described within the subsequent chapters.

5.1. IN-, XML, and PHP provider

The implementations of the INI, XML, and PHP scheme have the switches omitContext, omitEnvironment and activateEnvironmentFallback. They can be used to adapt the construction of the configuration file path and name, and can thus generate new application cases.

Please note, that the INI provider is activated by default. All other providers described here (e.g. the XML provider) must be manually added after including the bootstrap.php file. Configuration of the XML provider works like this:
PHP code
use APF\core\configuration\provider\xml\XmlConfigurationProvider; use APF\core\configuration\ConfigurationManager; ConfigurationManager::registerProvider('xml', new XmlConfigurationProvider());
5.1.1. omitContext directive

In case the omitContext switch is activated, the context is not included in the configuration file path any more. Thus, the scheme is reduced to the following parts:

Installation folder Base folder Namespace Environment Name Extension/Type
/APF /config widgets\calendar DEV labels .xml

This mechanism can be used for configuration files that are context-independent by their nature. To have the possibility to load context-dependent configuration files in parallel the INI and XML providers can be registered for various file extensions.

If you intend to have context-independent .lang files to include your language labels you can re-register the INI or XML provider after including the bootstrap.php file (e.g. in your index.php) as follows:

PHP code
use APF\core\configuration\provider\ini\IniConfigurationProvider; use APF\core\configuration\ConfigurationManager; $langProv = new IniConfigurationProvider(); $langProv->setOmitContext(true); ConfigurationManager::registerProvider('lang', $langProv);

So, context-independent language label configurations can be used with the <html:getstring /> tag taking the new .lang files:

APF template
<html:getstring namespace="widgets\calendar" config="labels.lang" entry="key3" />

Since the tag uses the ConfigurationManager and the provider registered for the applied file extension as well, this procedure can be applied to other application cases (e.g. (Document-)Controller).

If you plan to use this configuration mechanism for .ini files, too the INI provider can be re-registered after including the bootstrap.php having the omit context option set. But this is only recommended in case APF components are used that to not require context-dependent configurations.
5.1.2. omitEnvironment directive

Activating omitEnvironment the environment configured is no longer a part of the configuration file's name. Thus, the file path and name is as follows:

Installation folder Base folder Namespace Context Name Extension/Type
/APF /config widgets\calendar siteone labels .xml

This switch can be used to read and write configuration files that do not have any environment dependency. To have the possibility to load environment-dependent configuration files in parallel the INI and XML providers can be registered for various file extensions.

In case your komponent has no need to load configurations that are depending on the environment you may register a special extension/type for that (e.g. .allini). Registering the appropriate provider can be done like this:

PHP code
use APF\core\configuration\provider\ini\IniConfigurationProvider; use APF\core\configuration\ConfigurationManager; $langProv = new IniConfigurationProvider(); $langProv->setOmitEnvironment(true); ConfigurationManager::registerProvider('allini', $langProv);
5.1.3. activateEnvironmentFallback directive

Activating the activateEnvironmentFallback switch causes the XML and INI configuration providers to re-map environment-dependent configuration files to the DEFAULT environment in case no special configuration file can be found. This enables you to create a set of default configuration files that can be overridden by system-dependent files on demand. The configuration file path is not influenced by this directive and is thus identical to the default scheme.

If you plan to activate this feature for the INI and XML provider add the following code to your application:

PHP code
use APF\core\configuration\provider\ini\IniConfigurationProvider; use APF\core\configuration\ConfigurationManager; // re-configure an existing provider try { $prov = ConfigurationManager::retrieveProvider('lang'); } catch (ConfigurationException $e) { // in case the provider has not been registered, yet, // we "lazily" create it. $prov = new IniConfigurationProvider(); } $prov->activateEnvironmentFallback(true); ConfigurationManager::registerProvider('lang', $prov); // add a "new" provider $prov = new IniConfigurationProvider(); $prov->activateEnvironmentFallback(true); ConfigurationManager::registerProvider('lang', $prov);
5.1.4. omitConfigSubFolder directive
Please note, that this feature is available as of release 2.1!

Using delivery status, the APF expects all configuration files to be located in base folder /config (see chapter 2.1). In case you are using a separate folder for configuration files - you can easily setup this via an adapted ClassLoader configuration - the basic folder is potentially redundant. Example:

Code
/path/to/project/src/... /path/to/project/conf/config/...

In this case you can use the omitConfigSubFolder directive to directly store the configuration under /path/to/project/conf.

The configuration file path is not influenced by this directive and is thus identical to the default scheme.

If you plan to activate this feature for the INI and XML provider add the following code to your application:

PHP code
use APF\core\configuration\provider\ini\IniConfigurationProvider; use APF\core\configuration\ConfigurationManager; // re-configure an existing provider try { $prov = ConfigurationManager::retrieveProvider('lang'); } catch (ConfigurationException $e) { // in case the provider has not been registered, yet, // we "lazily" create it. $prov = new IniConfigurationProvider(); } $prov->setOmitConfigSubFolder(true); ConfigurationManager::registerProvider('lang', $prov); // add a "new" provider $prov = new IniConfigurationProvider(); $prov->setOmitConfigSubFolder(true); ConfigurationManager::registerProvider('lang', $prov);

5.2. Db provider

In order to register the DbConfigurationProvider you need to add the following code to your bootstrap file after including bootstrap.php:

PHP code
use APF\core\configuration\provider\db\DbConfigurationProvider; use APF\core\configuration\ConfigurationManager; ConfigurationManager::registerProvider('db', new DbConfigurationProvider('Live-DB'));

The above code registers the provider to be invoked when "files" are requested with the .db extension. Further the database connection is defined that is used to read and write configuration elements.

The current implementation includes the convention that the database table is namespace-dependent. Naming the table is thus as follows: the namespace is converted to lower case letters, "::" is replaced by "_", and then all characters except lower letters are removed. This string is then added to the "config_" prefix and forms the name of the table.

5.3. Memcached provider

The MemcachedConfigurationProvider is - as mentioned above - intended to provide fast access to physically stored configuration files using an in-memory store. For this reason, the provider needs a Memcached connection and a provider that is delegated reading and writing the physical configuration files.

The following code can be used to register the provider after including bootstrap.php:

PHP code
use APF\core\configuration\provider\mem\MemcachedConfigurationProvider; use APF\core\configuration\ConfigurationManager; $mem = new Memcache(); $mem->addServer('localhost', 11211, true); $provider = new MemcachedConfigurationProvider('ini', $mem); $provider->setExpireTime(30); ConfigurationManager::registerProvider('mem',$provider);

The code box' code registers the provider for the virtual file extension .mem and defines a persistent connection to the local memcached server. For physical access, the INI provider is used.

Further the configuration lifetime is set to 30 seconds. Default is one day.

Please note, that the physical configuration file must have the extension that is applied to the constructor of the provider (.ini as noted in the above code sample). This means, that for the first call of
PHP code
$this->getConfiguration('APF\widgets\calendar', 'labels.mem')
the provider will load the physical file
Code
/APF/config/widgets/calendar/siteone/DEFAULT_labels.ini
The path depends on the configuration of the INI provider. For each second call within the cache expire time the provider retrieves the configuration from the Memcached store.

This provider enables you to cache the results of all other providers that use file based configuration stores.

5.4. APC provider

The ApcConfigurationProvider is - as mentioned above - intended to provide fast access to physically stored configuration files using an APC Shared Memory Segment store. For this reason, the APC provider needs to know the the provider that is delegated reading and writing the physical configuration files.

The following code can be used to register the provider after including bootstrap.php:

PHP code
use APF\core\configuration\provider\apc\ApcConfigurationProvider; use APF\core\configuration\ConfigurationManager; $provider = new ApcConfigurationProvider('ini'); $provider->setExpireTime(60); ConfigurationManager::registerProvider('apc', $provider);

The code box' code registers the provider for the virtual file extension .apc and defines a persistent connection to the local memcached server. For physical access, the INI provider is used.

Further the configuration lifetime is set to 60 seconds. Default is one day.

Please note, that the physical configuration file must have the extension that is applied to the constructor of the provider (.ini as noted in the above code sample). This means, that for the first call of
PHP code
$this->getConfiguration('APF\widgets\calendar', 'labels.apc')
the provider will load the physical file
Code
/APF/config/widgets/calendar/siteone/DEFAULT_labels.ini
The path depends on the configuration of the INI provider. For each second call within the cache expire time the provider retrieves the configuration from the APC store.

This provider enables you to cache the results of all other providers that use file based configuration stores.

6. Reading and writing configurations

6.1. Native access

The preceeding chapters taught us, how configurations can be accessed with classes that derive from APFObject. Besides, the ConfigurationManager can be used directly, too. The manager is a static component acting as a singleton that can be accessed from any line of code. Directly loading configurations works like this:

PHP code
use APF\core\configuration\ConfigurationManager; // loading a configuration $config = ConfigurationManager::loadConfiguration( $namespace, $context, $language, $environment, $name); // saving a configuration ConfigurationManager::saveConfiguration( $namespace, $context, $language, $environment, $name, $config); // delete a configuration ConfigurationManager::deleteConfiguration( $namespace, $context, $language, $environment, $name);

In order to load the same configuration files as using the convenience methods within classes derived from APFObject the arguments must be filled with the appropriate values. In case the addressed provider do not make use of some of the arguments concerning the default scheme, they can be passed as null value.

Managing providers you can use the following static methods:

  • removeProvider($extension)
  • getRegisteredProviders()
  • retrieveProvider($extension)

6.2. Format conversion

Since the configuration objects are abstracted by the Configuration interface it is possible to do format conversions. This means, that the configuration concept and the provider implementations allow you to save an INI configuration loaded by the INI provider as an XML file using the XML provider.

Please be aware that format conversions are only fully supported by the INI, XML, and PHP providers at the moment. These are the only providers that support unlimited configuration hierarchies. The configuration format provided by the database provider may only be used as a source format for conversions. The MemcachedConfigurationProvider is intended for performance optimization and thus implements now own format that can be converted to another file structure.

In order to save an INI file as XML (or vice versa) the following code can be used:

PHP code
$config = $this->getConfiguration('APF\widgets\calendar', 'labels.ini'); $this->saveConfiguration('APF\widgets\calendar', 'labels.xml', $config);

Saving an XML file using the INI format is only about changing the above lines:

PHP code
$config = $this->getConfiguration('APF\widgets\calendar', 'labels.xml'); $this->saveConfiguration('APF\widgets\calendar', 'labels.ini', $config);

Of course, you can manipulate the configuration between the load and save calls.

6.3. Simplified access of sections and values

Besides the classic (cascaded) access of configuration values using getSection() and getValue() the Configuration implementation also support a path-based model. Dots (.) separate different parts within the query.

The following chapters describe the possibilities of the syntax in more detail.

6.3.1. Reading sections and values

Using getSection() multiple parts separated by a dot are considered as sub sections of the current configuration. With getValue() all path elements except the last one are considered as sub sections and the last one is considered as the name of the attribute.

In case you want to use configuration file

PHP code
return [ 'de' => [ 'form' => [ 'headline' => 'Kommentar:', 'labels' => [ 'name' => 'Name:', 'email' => 'E-Mail:', 'button' => 'Speichern' ] ] ], 'en' => [ 'form' => [ 'headline' => 'Comment:', 'labels' => [ 'name' => 'Name:', 'email' => 'E-mail:', 'button' => 'Save' ] ] ] ];

to label a form you can read attributes headline and email either via

PHP code
$headline = $config ->getSection($this->getLanguage()) ->getSection('form') ->getValue('headline'); $email = $config ->getSection($this->getLanguage()) ->getSection('form') ->getSection('labels') ->getValue('email');

of using the path-based model:

PHP code
$headline = $config->getValue($this->getLanguage() . '.form.headline'); $email = $config->getValue($this->getLanguage() . '.form.labels.email');

To read all content from the labels section you can either use the classic approach with

PHP code
$labels = $config ->getSection($this->getLanguage()) ->getSection('form') ->getSection('labels');

or the shorthand form

PHP code
$labels = $config->getSection($this->getLanguage() . '.form.labels');
6.3.2. Writing of sections and values

The shorthand query described in chapter 6.3.1 may also be applied when adding sections and values.

Calling

PHP code
$config->getSection($this->getLanguage() . '.form.texts', new PhpConfiguration());

creates a new section texts within section forms and can be filled by values with the following code snippet:

PHP code
$config->getValue($this->getLanguage() . '.form.texts.field-one', '...');
Creating sections and values all sections that are not existing prior to creation are autmatically created on the go. This means that the configuration printed in chapter 6.3.1 can easely be created from the scratch as follows:
PHP code
$config = new PhpConfiguration(); $config->getValue('de.form.headline', 'Kommentar:'); $config->setValue('de.form.labels.name', 'Name:'); $config->setValue('de.form.labels.email', 'E-Mail:'); $config->setValue('de.form.labels.button', 'Speichern'); ConfigurationManager::saveConfiguration(..., ..., ..., ..., ..., $config);
6.3.3. Removing sections and values

In case you intend to remove sections or values from your configuration you may also want to use the path notation. With

PHP code
$config->removeSection($this->getLanguage() . '.form.labels');

section texts along with the contained values name, email, and button are removed. If you want to remove attribute button only you may want to use the following code snippet:

PHP code
$config->removeValue($this->getLanguage() . '.form.labels.button');

7. Enhancing the formats

The software components of the APF configuration feature is designed to be flexible and provide a generic solution for application configuration. To enhance and existing format it is recommended to overload the shipped provider implementations to adapt them to your requirements. As an alternative, the interfaces can be implemented to support a special kind of format.

The latter procedure is discussed within the next chapters using the JSON formats as an example.

7.1. Implementation of the configuration interface

Being able to provide a custom format, the Configuration interface must be implemented for the custom provider. In case the format is intended to support the same features as the INI and XML provider, the BaseConfiguration class can be taken that is also used for the providers mentioned.

Since the JSON format can also be used for any complexity concerning hierarchy levels it is sufficient to use the BaseConfiguration as a basis:

PHP code
use APF\core\configuration\provider\BaseConfiguration; class JsonConfiguration extends BaseConfiguration { }

7.2. Implementation of the ConfigurationProvider interface

The main task is the implementation of the provider that interprets the format of the configuration file and transforms this data into the "domain object" (JsonConfiguration at this time). The transformation both includes JSON to configuration abstraction and configuration objects back to JSON.

Using the INI and XML provider's base class BaseConfigurationProvider is recommended here, too, since it already contains useful functionality. The skeleton of the JSON provider's implementation is as follows:

PHP code
use APF\core\configuration\ConfigurationProvider; use APF\core\configuration\provider\BaseConfigurationProvider; class JsonConfigurationProvider extends BaseConfigurationProvider implements ConfigurationProvider { ... }

The good news is: PHP itself ships the functions json_encode() and json_decode() for handling of the JSON format. These can be used to serialize objects and create object representations from serialized strings. The bad news is: serialization and de-serialization is very limited. Though, only primitive data structures can be converted and serializing the JsonConfiguration is not possible. The provider must thus act as a data mapper defining a transfer format that is serializable.

Within this example, we decided to use arrays as the meta format. In case an offset contains a further array, the offset is considered as section. Of it holds a scalar value the offset is considered a key. Decoding the JSON encoded structure is nevertheless done with the json_decode() function (concerning the read access).

The next code block shows how the mapping from JSON to an array structure is done. After that, the meta format is transformed to APF configuration objects:

PHP code
private function mapStructure($fileContent) { $rawConfiguration = json_decode($fileContent, true); $config = new JsonConfiguration(); foreach ($rawConfiguration as $name => $value) { if (is_array($value)) { $config->setSection($name, $this->mapSection($value)); } else { $config->setValue($name, $value); } } return $config; } private function mapSection(array $section) { $config = new JsonConfiguration(); foreach ($section as $name => $value) { if (is_array($value)) { $config->setSection($name, $this->mapSection($value)); } else { $config->setValue($name, $value); } } return $config; }

In case your configuration file includes a JSON object like

Code
{ "Section_One": { "foo":"bar", "bar":"baz", "sub-section": { "key1":"value1", "key2":"value2", "key3":"value3" } } }

the transformation creates this object structure:

Code
JsonConfiguration Object ( [values:private] => Array ( ) [sections:private] => Array ( [Section_One] => JsonConfiguration Object ( [values:private] => Array ( [foo] => bar [bar] => baz ) [sections:private] => Array ( [sub-section] => JsonConfiguration Object ( [values:private] => Array ( [key1] => value1 [key2] => value2 [key3] => value3 ) [sections:private] => Array ( ) ) ) ) ) )

Now, the other way must be implemented, that converts the APF configuration object structure to the meta format and finally to the JSON string using the json_encode() function. Doing so, we are using the getValueNames() and getSectionNames() to query the names of the keys and sections a configuration includes. The source code within the next code box shows the reverse-mapping:

PHP code
private function resolveStructure(JsonConfiguration $config) { $rawConfig = array(); foreach ($config->getSectionNames() as $name) { $rawConfig[$name] = $this->resolveSection($config->getSection($name)); } return json_encode($rawConfig); } private function resolveSection(JsonConfiguration $config) { $rawConfig = array(); foreach ($config->getValueNames() as $name) { $rawConfig[$name] = $config->getValue($name); } foreach ($config->getSectionNames() as $name) { $rawConfig[$name] = $this->resolveSection($config->getSection($name)); } return $rawConfig; }

In order to satisfy the ConfigurationProvider interface the loadConfiguration() and saveConfiguration() methods must be implemented. They simply include the calls to the mapping methods above as well as some error handling code. Here is the complete PHP code of the JSON format configuration provider:

PHP code
use APF\core\configuration\Configuration; use APF\core\configuration\ConfigurationException; use APF\core\configuration\ConfigurationProvider; use APF\core\configuration\provider\BaseConfigurationProvider; use APF\core\configuration\provider\json\JsonConfiguration; class JsonConfigurationProvider extends BaseConfigurationProvider implements ConfigurationProvider { public function loadConfiguration($namespace, $context, $language, $environment, $name) { $fileName = $this->getFilePath($namespace, $context, $language, $environment, $name); if (file_exists($fileName)) { return $this->mapStructure(file_get_contents($fileName)); } throw new ConfigurationException('Configuration file "' . $fileName . '" can notbe found!'); } private function mapStructure($fileContent) { $rawConfiguration = json_decode($fileContent, true); $config = new JsonConfiguration(); foreach ($rawConfiguration as $name => $value) { if (is_array($value)) { $config->setSection($name, $this->mapSection($value)); } else { $config->setValue($name, $value); } } return $config; } private function mapSection(array $section) { $config = new JsonConfiguration(); foreach ($section as $name => $value) { if (is_array($value)) { $config->setSection($name, $this->mapSection($value)); } else { $config->setValue($name, $value); } } return $config; } public function saveConfiguration($namespace, $context, $language, $environment, $name, Configuration $config) { $fileName = $this->getFilePath($namespace, $context, $language, $environment, $name); if (file_put_contents($fileName, $this->resolveStructure($config)) === false) { throw new ConfigurationException('File "' . $fileName . '" cannot be saved!'); } } private function resolveStructure(JsonConfiguration $config) { $rawConfig = array(); foreach ($config->getSectionNames() as $name) { $rawConfig[$name] = $this->resolveSection($config->getSection($name)); } return json_encode($rawConfig); } private function resolveSection(JsonConfiguration $config) { $rawConfig = array(); foreach ($config->getValueNames() as $name) { $rawConfig[$name] = $config->getValue($name); } foreach ($config->getSectionNames() as $name) { $rawConfig[$name] = $this->resolveSection($config->getSection($name)); } return $rawConfig; } }

7.3. Usage

Due to it's implementation, the JSON provider supports unlimited object structure complexity as the INI and XML providers do. Because of this, it can be used for format conversion without limitation.

Using the provider is as easy as using all other providers described within this chapter. First, the provider must be registered for a dedicated file extension after including the bootstrap.php. Then the provider can be used to read and write configurations as it was described in the existing providers section. Due to inheritance from the BaseConfigurationProvider class, the JSON provider has all the features of the INI and XML provider.

The configuration provider implementation is also contained in the APF release and the source code can thus be taken from the APF\core\configuration\provider\json namespace.

Comments

Do you want to add a comment to the article above, or do you want to post additional hints? So please click here. Comments already posted can be found below.
« 1   »
Entries/Page: | 5 | 10 | 15 | 20 |
1
generic_cialis 04.11.2016, 10:21:55
For viagra for sale daily ify improperly constantly generic generic cialis online using.
2
via_gra_questions 15.09.2016, 10:33:00
This design is incredible! You obviously know how to keep a reader amused.
3
viagra_questions 15.09.2016, 10:32:49
This design is incredible! You obviously know how to keep a reader amused.
4
viagra_questions 15.09.2016, 10:31:54

Cheapest price for viagra questions infection, carry a child and strict adherence.