Application configuration is handled by implementations of Configuration. To use the configuration throughout the application, an instance of the appropriate implementation is passed to ObjectFactory.

The following code demonstrates how to set up and use the InifileConfiguration configuration in code:

$configPath = WCMF_BASE.'app/config/';
// setup configuration
$configuration = new InifileConfiguration($configPath);
// register with object factory
ObjectFactory::registerInstance('configuration', $configuration);
// get a configuration value
$configuration = ObjectFactory::getInstance('configuration');
$tz = $configuration->getValue('timezone', 'application');

Configuration Format

Currently wCMF provides an implementation for using the INI file format. InifileConfiguration handles ini files that consist of key-value pairs that are grouped in sections:

1 [SectionA]
2 key1 = value1
3 key2 = value2
5 [SectionB]
6 key3 = value3

Additionally it defines array values (enclosed in curly braces) and inclusion of additional files. Included files are defined in the include key of the Config section. Keys in the current file override keys from included files.

The following example shows how to include an array of additional configuration files to separate concerns:

1 [Config]
2 include = {server.ini, persistence.ini, presentation.ini, security.ini}
While path values in the configuration are normally resolved relative to the value of the WCMF_BASE constant, the included files are expected to be located in the directory that is passed to the constructor of InifileConfiguration.

The possibility to include configuration files into others is useful, if you want to separate varying parts from static ones to simplify the deployment process. In the default application for example all server specific configuration (e.g. database connection) are separated into a file called server.ini.

Configuration section and key names are treated case-insensitive.

Dependency injection

wCMF supports Dependency injection through ObjectFactory and it's DefaultFactory implementation. This class implements constructor and setter injection, which means that dependencies are either set through the constructor or a setter method of the client class or a public member variable.

Assembly details for client instances are defined in the application configuration in special sections containing a __class key. The value of the key denotes the class to be used to create the client instance. All other keys are tried to be mapped to constructor parameters or setters/members of that class. Complex dependencies of an instance may be set by using the $ notation, which refers to another configuration section. If a section does not contain a __class key it is instantiated as an associative array.

The following example demonstrates the pattern by means of the PersistenceFacade instance:

1 [PersistenceFacade]
2 __class = wcmf\lib\persistence\impl\DefaultPersistenceFacade
3 mappers = $typeMapping
4 logStrategy = $auditingLogStragegy
6 [AuditingLogStragegy]
7 __class = wcmf\lib\persistence\output\impl\AuditingOutputStrategy
9 [TypeMapping]
10 app.src.model.wcmf.DBSequence = $app_src_model_wcmf_DBSequenceRDBMapper
11 app.src.model.wcmf.Lock = $app_src_model_wcmf_LockRDBMapper
13 [app_src_model_wcmf_DBSequenceRDBMapper]
14 __class = app\src\model\wcmf\DBSequenceRDBMapper
15 connectionParams = $database
17 [app_src_model_wcmf_LockRDBMapper]
18 __class = app\src\model\wcmf\LockRDBMapper
19 connectionParams = $database
21 [Database]
22 dbType = sqlite
23 dbHostName =
24 dbName = app/test-db.sq3
25 dbUserName =
26 dbPassword =
27 dbCharSet = utf8
29 [EventManager]
30 __class = wcmf\lib\core\impl\DefaultEventManager

The first section defines that the instance named PersistenceFacade is an object of the class DefaultPersistenceFacade.

So let's have a look at the relevant code in DefaultPersistenceFacade:

class DefaultPersistenceFacade implements PersistenceFacade {
public function __construct(EventManager $eventManager,
OutputStrategy $logStrategy) {
public function setMappers($mappers) {

When requested the first time, the PersistenceFacade instance is created in the following steps:

  1. Constructor parameters:
    • $eventManager is not explicitly defined in the instance configuration, so the factory looks it up in the configuration. Since there is a EventManager section, it uses it to create an instance of DefaultEventManager and injects it into the constructor. If there would be no section with this name, the factory would use the type hint and search for a class called EventManager.
    • $logStrategy is explicitly defined as an instance of AuditingOutputStrategy. This is an example of a complex dependency that is defined in another configuration section.
  2. Setter injection of the remaining parameters:
    • The mappers configuration parameter is not used in the constructor. The factory searches for a setter method (setMappers) or a public member variable ($mappers). Since the class defines a setter method, it is used to inject the dependency. The TypeMapping section does not contain a __class key, which means that the mappers property will be an associative array. In this example it maps class names (e.g. app.src.model.wcmf.DBSequence) to mapper instances (e.g. DBSequenceRDBMapper). Further sections show that the connectionParams property of the DBSequenceRDBMapper instance is set to an associative array describing the database connection (section Database).

The PersistenceFacade instance can be retrieved by using the following code:

$persistenceFacade = ObjectFactory::getInstance('persistenceFacade');

Shared and Non-Shared instances

By default all instances created by ObjectFactory are shared instances, which means, that they will be created on the first call and only returned by succeeding calls. If you want to create a non-shared instance, you must use the __shared key in the configuration and set it to false.

The following example shows the configuration of a non-shared View instance:

1 [View]
2 __class = wcmf\lib\presentation\view\impl\SmartyView
3 __shared = false
4 compileCheck = true
5 caching = false
6 cacheLifetime = 3600
7 cacheDir = app/cache/smarty/

Circular dependencies

Since DefaultFactory tries to resolve all dependencies when constructing an instance, it might happen that a dependency depends on the instance being currently constructed. This is called a circular dependency and would cause infinite recursion. To solve this problem the dependency in question must not be injected in the constructor, but in a setter method or member variable. This allows the factory to construct the initial instance first without requiring the child dependency. It is then available when constructing the child dependency.

Configuration for individual users

wCMF's user base class AbstractUser has a persistent property config which may be used to set a individual configuration for a user. This will be loaded after the standard configuration and extends its settings. If two keys have the same name the one from the user configuration overwrites the standard one. Using the RoleConfig configuration section, you can easily assign configuration files to groups:

1 [RoleConfig]
2 administrators = admin.ini

If a user is added to a listed role, the given configuration file will be automatically assigned to it, if no individual configuration is set yet.

Default application configuration

The default application uses some special configuration sections:

1 [Application]
3 color = #428BCA
4 rootTypes = {Author, Book, Publisher}
5 timezone = Europe/Berlin
6 listeners = {Search, EventListener}
8 [Media]
9 uploadDir = app/public/media/
  • Application
    • title Application title
    • color Color to be used in the header of the login screen
    • rootTypes List of entity types to create tabs for
    • timezone Application timezone
    • listeners List of listeners to install (see Implicit listener installation)
  • Media
    • uploadDir Directory, to which media files should be uploaded

Logging configuration

Since logging is potentially required right from application startup, it is usually the first service to set up - even before setting up the application configuration. This implies that logging should not depend on other services, especially not on the application configuration.

wCMF provides integrations for log4php and Monolog through implementations of the Logger interface. The concrete classes are configured using a configuration file, that is passed to the constructor. An instance of the concrete class is then passed to the LogManager::configure method.


To use the log4php library, instantiate MonologFileLogger in the following way:

$logger = new MonologFileLogger('main', WCMF_BASE.'app/config/log.ini');

MonologFileLogger is a simple wrapper class that allows logging to files or streams. The configuration is defined in the INI file format using the following sections and keys:

  • Root section defines properties valid for all loggers unless otherwise stated
    • level The default log level
    • target The logging target. This could be either a
      • directory (e.g. app/log/) for daily rotating log files or a
      • stream (e.g. php://output) for logging to a stream
  • Logger section defines log levels for individual loggers
    • logger name (e.g. wcmf\lib\presentation\Application) is used as key and the log level (e.g. DEBUG) as value

Since in wCMF each class uses it's own logger named after the class name, setting up the log level for individual classes is straightforward.

The following configuration sets the default log level to ERROR and the log level for the Controller class to DEBUG:

1 [Root]
2 level = ERROR
3 target = app/log/
5 [Logger]
6 wcmf\lib\presentation\Controller = DEBUG


To use the log4php library, instantiate Log4phpLogger in the following way:

$logger = new Log4phpLogger('main', WCMF_BASE.'app/config/log4php.php');

The configuration file log4php.php uses log4php's PHP configuration format.

The following configuration sets the default log level to ERROR and the log level for the Controller class to DEBUG:

return array(
'rootLogger' => array(
'level' => 'ERROR',
'appenders' => array('dailyFile', 'echo'),
'loggers' => array(
'wcmf.lib.presentation.Controller' => array('level' => 'DEBUG', 'appenders' => array('dailyFile')),