Configuration
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:
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:
- Note
- Configuration section and key names are treated case-insensitive.
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. If a configuration section with the same name exists in both files, the values are merged, whereby values in the current file override values from included files. This behavior is comparable to the concept of inheritance and can be disabled by adding the __inherit
configuration key to the section and setting it to false.
The following example shows how to include an array of additional configuration files to separate concerns:
Configuration sections from following files override identically named sections from preceding files.
- Note
- 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 ofInifileConfiguration
.
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.
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 are 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.
- Note
- Instance names are treated case-insensitive.
The following example demonstrates the pattern by means of the PersistenceFacade
instance:
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
:
When requested the first time, the PersistenceFacade instance is created in the following steps:
- 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 ofDefaultEventManager
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 calledEventManager
. - $logStrategy is explicitly defined as an instance of
AuditingOutputStrategy
. This is an example of a complex dependency that is defined in another configuration section.
- $eventManager is not explicitly defined in the instance configuration, so the factory looks it up in the configuration. Since there is a
- 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 theDBSequenceRDBMapper
instance is set to an associative array describing the database connection (sectionDatabase
).
- 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
The PersistenceFacade
instance can be retrieved by using the following code:
- Note
- An exception is thrown if a constructor parameter can't be resolved. That means if a constructor parameter is supposed to be optional, a default value must be provided.
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:
Aliases
The same instance can be referenced using different names (aliases). To define an alias, the __ref
key is used. Aliases help avoiding duplicate configuration sections.
The following example shows the configuration of different caches, that actually use the same configuration.
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 is used to set an 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:
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:
Application
title
Application titlecolor
Color to be used in the header of the login screenrootTypes
List of entity types to create tabs fortimezone
Application timezonelisteners
List of listeners to install (see Implicit listener installation)
Media
uploadDir
Directory, to which media files should be uploaded
PHP configuration
PHP configuration values can be set in the PhpConfig
section. The ini_set
function will be called for each key value pair when initializing the Application
instance.
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.
Monolog
To use the log4php library, instantiate MonologFileLogger
in the following way:
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 statedlevel
The default log leveltarget
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
- logger name (e.g. wcmf\lib\presentation\Application) is used as key and the log level (e.g.
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
:
log4php
To use the log4php library, instantiate Log4phpLogger
in the following way:
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
: