Trait to support creating complex nested class structures from JSON/YAML objects.

1.0.3 2020-03-28 19:27 UTC

This package is auto-updated.

Last update: 2020-03-28 23:29:15 UTC


This trait facilitates the conversion of a set of data structures, typically generated by decoding JSON or YAML configuration files, into PHP classes. Configurable can convert arrays of objects into associative arrays using a property of the object. It can also validate inputs as well as guard and remap property names.


Configurable makes it easy to take data like this:

    "application-name": "MyApp",
    "database": [
            "label": "crm",
            "driver": "mysql",
            "host": "localhost",
            "name": "crm",
            "pass": "insecure",
            "port": 3306,
            "user": "admin"
            "label": "geocoder",
            "driver": "mysql",
            "host": "localhost",
            "name": "geo",
            "pass": "insecure",
            "port": 3306,
            "user": "admin"

and turn it into a class structure like this:

Environment Object
    [appName] => MyApp
    [database] => Array
            [crm] => DatabaseConfiguration Object
                    [driver:protected] => mysql
                    [host:protected] => localhost
                    [key] => crm
                    [label:protected] => crm
                    [name:protected] => crm
                    [pass:DatabaseConfiguration:private] => insecure
                    [port:protected] => 3306
                    [user:protected] => admin

            [geocoder] => DatabaseConfiguration Object
                    [driver:protected] => mysql
                    [host:protected] => localhost
                    [key] => geocoder
                    [label:protected] => geocoder
                    [name:protected] => geo
                    [pass:DatabaseConfiguration:private] => insecure
                    [port:protected] => 3306
                    [user:protected] => admin


Configurable supports property mapping, gated properties (via allow, block, and ignore methods), data validation, and data-driven class instantiation. It will map arrays of objects to associative arrays of PHP classes that are indexed by any unique scalar property in the object.

Loading can be either fault-tolerant or strict. Strict validation can either fail with a false result or by throwing an Exception you specify.


Old School: Configurable has no dependencies, so you can just include it and use it.

Composer: require abivia/configurable

Basic Usage

  • Implement configureClassMap() for the top level class and any properties that map to your classes.
  • Add the configurable trait to your classes.
  • Instantiate the top level class and pass your decoded JSON or YAML to the configure() method.

Basic example:

class ConfigurableObject {
    use \Abivia\Configurable\Configurable;

    protected $userName;
    protected $password;

$json = '{"userName": "admin"; "password": "insecure"}';
$obj = new ConfigurableObject();
echo $obj->userName . ', ' . $obj->password;

Output: admin, insecure

Advanced Usage

  • If you need to map properties, implement configurePropertyMap() where needed.
  • Add property validation by implementing configureValidate().
  • Gate access to properties by implementing any of configurePropertyAllow(), configurePropertyBlock() or configurePropertyIgnore().
  • You can also initialize the class instance at run time with configureInitialize().
  • Semantic validation of the result can be performed at the end of the loading process by implementing configureComplete().


The options parameter can contain these elements:

  • 'newLog' If missing or set true, Configurable's error log is cleared before any processing.
  • 'parent' when instantiating a subclass, this is a reference to the parent class.
  • 'strict' Controls error handling. If strict is false, Configurable will ignore minor issues such as additional properties. If strict is true, Configurable will return false if any errors are encountered. If strict is a string, this will be taken as the name of a Throwable class, and an instance of that class will be thrown.

Applications can also pass in their own context via options. The current options are available via the $configureOptions property. Option names starting with an underscore are guaranteed to not conflict with future options used by Configurable.

Note that a copy of the options array is passed to subclass configuration, no data can be returned to the parent via this array.


The properties of the configured object can be explicitly permitted by overriding the configurePropertyAllow() method, blocked by overriding the configurePropertyBlock() method, or ignored via the configurePropertyIgnore() method. Ignore takes precedence, then blocking, then allow. By default, attempts to set guarded properties are ignored, but if the $strict parameter is either true or the name of a Throwable class, then the configuration will terminate when the invalid parameter is encountered, unless it has been explicitly ignored.

For a JSON input like this

    "depth": 15,
    "length": 22,
    "primary": "Red",
    "width": 3

with a class that does not have the primary property, the result depends on the strict option:

    class SomeClass {
        use \Abivia\Configurable;

        protected $depth;
        protected $length;
        protected $width;


    $obj = new SomeClass();
    // Returns true
    // Lazy validation: Returns true
    $obj->configure($jsonDecoded, ['strict' => false]);
    // Strict validation: Returns false
    $obj->configure($jsonDecoded, ['strict' => true]);
    // Strict validation: throws MyException
    $obj->configure($jsonDecoded, ['strict' => 'MyException']);

Initialization and Completion

In many cases it is required that the object be in a known state before configuration, and that the configured object has acceptable values. Configurable supplies configureInitialize() and configureComplete() for this purpose. configureInitialize() can be used to return a previously instantiated object to a known state. configureInitialize() gets passed references to the configuration data and the options array, and is thus able to pre-process the inputs if required.

One use case for pre-processing during initialization is to allow shorthand expressions. For example, if you have an object with one property:

{"name": "foo"}

Your application can support a shorthand expression:


With this code in the initialization:

protected function configureInitialize(&$config) {
    if (is_string($config)) {
        $obj = new stdClass;
        $obj->name = $config;
        $config = $obj;


Scalar properties can be validated with configureValidate(). This method takes the property name and the input value as arguments. The value is passed by reference so that the validation can enforce specific formats required by the object (for example by forcing case or cleaning out unwanted characters).

The configureComplete() method provides a mechanism for object level validation. For example, this method could be used to validate a set of access credentials, logging an error or aborting the configuration process entirely if they are not valid.

Property Name Mapping

Since configuration files allow properties that are not valid PHP property names, configurePropertyMap() can be used to convert illegal input properties to valid PHP identifiers.

protected function configurePropertyMap($property) {
    if ($property[0] == '$') {
        $property = 'dollar_' . substr($property, 1);
    return $property;

If the property does not reference another configurable class then the method can also return an array containing a property name and array index. For example this json:

    "prop.one": "element one",
    "prop.six": "element six"

Can be used to create an array:

$configured->prop = ['one' => 'element one', 'six' => 'element six']

Contained Classes

The real power of Configurable is through configureClassMap() which can be used to instantiate and configure classes that are contained in the current class. Contained classes must provide the configure() method, either via the Configurable trait or by providing their own method.

configureClassMap() takes the name and value of a property as arguments and returns:

  • the name of a class to be instantiated and configured, or
  • an array or object that has the className property and any of the optional properties.

className (string|callable)

className can be the name of a class that will be instantiated and configured, or it can be a callable that takes the current property value as an argument. This allows the creation of data-specific object classes.

key (string|callable)

The key property is optional and tells Configurable to populate an array.

  • if key is absent or blank, the constructed object is appended to the array,
  • if key is a string, then it is taken as the name of a property or method (if keyIsMethod is true) in the constructed object, and this value is used as the key for an associative array, and
  • if key is a callable array, then it is called with the object under construction as an argument.

keyIsMethod (bool)

The keyIsMethod property is only used when key is present and not a callable. When set, key is treated as a method of the constructed object. Typically this is a getter.

allowDups (bool)

If Configurable is creating an associative array, the normal response to a duplicate key is to generate an error message. if the allowDups flag is present and set, no error is generated.

Error Logging

Any problems encountered during configuration are logged. An array of errors can be retrieved by calling the configureGetErrors() method. The error log is cleared by an application call to configure() unless the newLog option is set to false.


The unit tests contain a number of examples that should be illustrative. More detailed examples with sample output can be found at https://gitlab.com/abivia/configurable-examples