arnapou/appcfg

Library - Application config system.

v1.5 2024-04-14 15:37 UTC

This package is auto-updated.

Last update: 2024-04-18 20:07:46 UTC


README

pipeline coverage

Application config system.

Installation

composer require arnapou/appcfg

packagist 👉️ arnapou/appcfg

Introduction

The global purpose of this lib is to manage a configuration for your applications.

The main features are :

  • Allows a mechanism of override: hostname > environment > generic.
  • Expression parser of the form %<processor>(<label>:<value>)% to manage dependency values inside your configuration, with the possibility to add custom processors.
  • Dump of the compiled configuration into proper php typed objects, 100% compliant with your static analysis tools.

Parser

Look at the Parser class.

Parsed characters:

  • % is the enclosure character, double it %% to escape if you have a conflict like a string containing %something(, then escape to %%something(.
  • () parentheses are used to gather parts of an expression %env(VAR)%
  • : colon is used to separate the label from the value %env(VAR:value)%

Examples:

  • %<processor>(<label>)% : expression without value
  • %<processor>(<label>:<value>)% : main expression
  • %<processor>(<label>:<string>%<processor>(<label>)%<string>)% : nested is allowed in value part

Unlikely cases where you want to escape the % :

stringwhereescapedexplanation
%%everywhere%%%%%% is the escaped version of %
%word(everywhere%%word(doubling the % avoids the parser to think it is an expression
)%nested value%(:))%%we isolate the ) from the % using the expression %(:<default>)% where <default> is the )

Note: the parser is lax about the % if there is no doubt of its usage. Example writing foo%bar baz is valid because it is obviously not an expression. But writing foo%%bar baz is also valid because this is the same string with the % escaped.

Bundled processors:

  • DefaultProcessor with name '': used to manage default values where labels are paths of other values (i.e. parameter.database.name).
  • EnvProcessor with name 'env': used to retrieve environment variables with an optional default.

The parsed string is a full immutable object structure like below.

Note that if you cast to string an Expression, you will get back the original raw string.

Parser schema

Processor

A Processor is an interface which can compute a value into a result.

If a value cannot be computed due to dependencies not already processed, you need to return a UnprocessableYet object.

Compiler

A Compiler is an interface which can compile an input array into another.

We provide the StaticCompiler implementation which can parse strings and compile in several passes an array.

This compiler does not explicitly manage cyclic loops but has a max passes security.

Dumper

A Dumper is an interface which can render an input array into php code.

We provide the StaticDumper implementation which produce php typed objects for each associative array of your configuration structure.

This code is 100% compliant with your static analysis tools.

Note: the objects rendered can be mutable or immutable (default).

DataSource / Appcfg

A DataSource is an interface which has the responsibility to manage the source of data in order to be used by Appcfg.

Appcfg is a simple concrete class which is basically an orchestra conductor which can do several classic operations with all the previous objects.

Example

Input data

$data = [
    'db' => [
        'main' => [
            'host' => 'localhost',
            'port' => 3306,
            'name' => 'project_main',
            'password' => 'project_password',
        ],
        'data' => [
            'host' => '%(db.main.host)%',
            'port' => '%(db.main.port)%',
            'name' => 'project_data',
            'password' => '%(db.main.password)%',
        ],
    ],
    'db|prod' => [
        'main' => [
            'password' => '%env(DB_PASSWORD)%',
        ],
    ],
];

💡 If the expression is alone like in db.data.port, the referenced type is kept when compiled (int in the above example).

Compile & render

use Arnapou\Appcfg\Compiler\CompileOptions;
use Arnapou\Appcfg\Compiler\StaticCompiler;
use Arnapou\Appcfg\Dumper\DumpOptions;
use Arnapou\Appcfg\Dumper\StaticDumper;

$compiler = new StaticCompiler();
$compiled = $compiler->compile($data, new CompileOptions('prod'));

$dumper = new StaticDumper();
echo $dumper->dump($compiled, new DumpOptions('MyProject\Config'));

Result

Run command : DB_PASSWORD=123456 php example/readme.php

namespace MyProject;

final readonly class ConfigDbMain
{
    public function __construct(
        public string $host = 'localhost',
        public int $port = 3306,
        public string $name = 'project_main',
        public string $password = '123456',
    ) {}
}

final readonly class ConfigDbData
{
    public function __construct(
        public string $host = 'localhost',
        public int $port = 3306,
        public string $name = 'project_data',
        public string $password = '123456',
    ) {}
}

final readonly class ConfigDb
{
    public function __construct(
        public ConfigDbMain $main = new ConfigDbMain(),
        public ConfigDbData $data = new ConfigDbData(),
    ) {}
}

final readonly class Config
{
    public function __construct(
        public ConfigDb $db = new ConfigDb(),
    ) {}
}

Changelog versions

StartTag, BranchPhp
01/01/20241.x, main8.3