yiisoft/config

Composer plugin and a library for config assembling

Fund package maintenance!
Opencollective
yiisoft

Installs: 33 146

Dependents: 16

Suggesters: 0

Security: 0

Stars: 19

Watchers: 16

Forks: 8

Open Issues: 2

Type:composer-plugin

1.1.1 2022-01-05 23:30 UTC

This package is auto-updated.

Last update: 2022-01-28 14:12:38 UTC


README

68747470733a2f2f796969736f66742e6769746875622e696f2f646f63732f696d616765732f7969695f6c6f676f2e737667 logo.png

Yii Config


Latest Stable Version Total Downloads Build status Scrutinizer Code Quality Code Coverage Mutation testing badge static analysis type-coverage

This Composer plugin provides assembling of configurations distributed with composer packages. It allows putting configuration needed to use a package right inside thus implementing a plugin system. The package becomes a plugin holding both the code and its configuration.

Requirements

  • PHP 7.4 or higher.
  • Composer 2.0 or higher.

Installation

composer require yiisoft/config --prefer-dist

How it works?

The package consist of two parts: Composer plugin and config loader.

After composer updates its autoload file, and that happens after dump-autoload, require, update or remove, Composer plugin:

  • Scans installed packages for config-plugin extra option in their composer.json.
  • Writes a merge plan into config/.merge-plan.php. It includes configuration from each package composer.json.

In the application entry point, usually index.php, we create an instance of config loader and require a configuration we need:

use Yiisoft\Config\Config;
use Yiisoft\Config\ConfigPaths;

$config = new Config(
    new ConfigPaths(dirname(__DIR__)),
);

$web = $config->get('web');

The web in the above is a config group. The config loader obtains it runtime according to the merge plan. The configuration consists of three layers that are loaded as follows:

  • Vendor configurations from each vendor/package-name. These provide default values.
  • Root package configurations from config. These may override vendor configurations.
  • Environment specific configurations from config. These may override root and vendor configurations.

Please note that same named keys are not allowed within a configuration layer.

When calling the get() method, if the configuration group does not exist, an \ErrorException will be thrown. If you are not sure that the configuration group exists, then use the has() method:

use Yiisoft\Config\Config;
use Yiisoft\Config\ConfigPaths;

$config = new Config(
    new ConfigPaths(dirname(__DIR__)),
);

if ($config->has('web')) {
    $web = $config->get('web');
}

Config groups

Each config group represents a set of configs that is merged into a single array. It is defined per package in each package composer.json:

"extra": {
    "config-plugin": {
        "params": [
            "params.php",
            "?params-local.php"
        ],
        "common": "common.php",
        "web": [
            "$common",
            "web.php",
            "../src/Modules/*/config/web.php"
        ],
        "other": "other.php"
    }
}

Markers

  • ? - marks optional files. Absence of files not marked with it will cause exception.

    "params": [
       "params.php",
       "?params-local.php"
    ]
    

    It's okay if params-local.php will not found, but it's not okay if params.php will be absent.

  • * - marks wildcard path. It means zero or more matches by wildcard mask.

    "web": [
       "../src/Modules/*/config/web.php"
    ]
    

    It will collect all web.php in any sub-folders of src/Modules/ in config folder. However, if the configuration folder is packaged as part of the PHAR archive, the configuration files will not be uploaded. In this case, you must explicitly specify each configuration file.

  • $ - reference to another config.

    "params": [
       "params.php",
       "?params-local.php"
    ],
    "params-console": [
       "$params",
       "params-console.php"
    ],
    "params-web": [
       "$params",
       "params-web.php"
    ]
    

    Output files params-console.php and params-web.php will contain params.php and params-local.php.

Define your configs like the following:

<?php

return [
    'components' => [
        'db' => [
            'class' => \my\Db::class,
            'name' => $params['db.name'],
            'password' => $params['db.password'],
        ],
    ],
];

A special variable $params is read from config (by default, group is named params).

Using custom group for $params

By default, $params variable is read from params group. You can customize the group name via constructor of Config:

$config = new Config(
    new ConfigPaths(__DIR__ . '/configs'),
    null,
    [],
    'custom-params' // Group name for `$params`
);

Using sub-configs

In order to access a sub-config, use the following in your config:

'routes' => $config->get('routes');

Options

A number of options is available both for Composer plugin and a config loader. Composer options are specified in composer.json:

"extra": {
    "config-plugin-options": {
      "source-directory": "config"
    },
    "config-plugin": {
        // ...
    }
}

source-directory

The source-directory option specifies where to read the configs from for a package the option is specified for. It is available for all packages, including the root package, which is typically an application. The value is a path relative to where the composer.json file is located. The default value is an empty string.

If you change the source directory for the root package, don't forget to adjust configs path when creating an instance of Config. Usually that is index.php:

use Yiisoft\Config\Config;
use Yiisoft\Config\ConfigPaths;

$config = new Config(
    new ConfigPaths(dirname(__DIR__), 'path/to/config/directory'),
);

$web = $config->get('web');

vendor-override-layer

The vendor-override-layer option adds a sublayer to the vendor, which allocates packages that will override the vendor's default configurations. This sublayer is located between the vendor and application layers.

This can be useful if you need to redefine default configurations even before the application layer. To do this, you need to create your own package with configurations meant to override the default ones:

"name": "vendor-name/package-name",
"extra": {
    "config-plugin": {
        // ...
    }
}

And in the root file composer.json of your application, specify this package in the vendor-override-layer option:

"require": {
    "vendor-name/package-name": "version",
    "yiisoft/config": "version"
},
"extra": {
    "config-plugin-options": {
        "vendor-override-layer": "vendor-name/package-name"
    },
    "config-plugin": {
        // ...
    }
}

In the same way, several packages can be added to this sublayer:

"extra": {
    "config-plugin-options": {
        "vendor-override-layer": [
            "vendor-name/package-1",
            "vendor-name/package-2"
        ]
    }
}

You can use wildcard pattern if there are too many packages:

"extra": {
    "config-plugin-options": {
        "vendor-override-layer": [
            "vendor-1/*",
            "vendor-2/config-*"
        ]
    }
}

For more information about the wildcard syntax, see the yiisoft/strings.

Please note that in this sublayer keys with the same names are not allowed similar to other layers.

build-merge-plan

The build-merge-plan option allows you to disable creation/updating of the config/.merge-plan.php. Enabled by default, to disable it, set the value to false:

"extra": {
    "config-plugin-options": {
        "build-merge-plan": false
    }
}

This can be useful when developing. If the config package is a dependency of your package, and you do not need to create a merge plan file when developing your package. For example, this is implemented in yiisoft/yii-runner.

Environments

The plugin supports creating additional environments added to the base configuration. This allows you to create multiple configurations for the application such as production and development.

The environments are specified in the composer.json file of your application:

"extra": {
    "config-plugin-options": {
        "source-directory": "config"
    },
    "config-plugin": {
        "params": "params.php",
        "web": "web.php"
    },
    "config-plugin-environments": {
        "dev": {
            "params": "dev/params.php",
            "app": [
                "$web",
                "dev/app.php"
            ]
        },
        "prod": {
            "app": "prod/app.php"
        }
    }
}

Configuration defines the merge process. One of the environments from config-plugin-environments is merged with the main configuration defined by config-plugin. In given example, in the dev environment we use $web configuration from the main environment.

This configuration has the following structure:

config/             Configuration root directory.
    dev/            Development environment files.
        app.php     Development environment app group configuration.
        params.php  Development environment parameters.
    prod/           Production environment files.
        app.php     Production environment app group configuration.
    params.php      Main configuration parameters.
    web.php         Мain configuration web group configuration.

To choose an environment to be used you must specify its name when creating an instance of Config:

use Yiisoft\Config\Config;
use Yiisoft\Config\ConfigPaths;

$config = new Config(
    new ConfigPaths(dirname(__DIR__)),
    'dev',
);

$app = $config->get('app');

If defined in an environment, params will be merged with params from the main configuration, and could be used as $params in all configurations.

Configuration in a PHP file

You can define configuration in a PHP file. To do it, specify a PHP file path in the extra section of the composer.json:

"extra": {
    "config-plugin-file": "path/to/configuration/file.php"
}

Configurations are specified in the same way, only in PHP format:

return [
    'config-plugin-options' => [
        'source-directory' => 'config',  
    ],
    'config-plugin' => [
        'params' => [
            'params.php',
            '?params-local.php',
        ],
        'web' => 'web.php', 
    ],
    'config-plugin-environments' => [
        'dev' => [
            'params' => 'dev/params.php',
            'app' => [
                '$web',
                'dev/app.php',
            ],
        ],
        'prod' => [
            'app' => 'prod/app.php',
        ],
    ],
];

If you specify the file path, the remaining sections (config-plugin-*) in composer.json will be ignored and configurations will be read from the PHP file specified. The path is relative to where the composer.json file is located.

Configuration modifiers

Recursive merge of arrays

By default, recursive merging of arrays in configuration files is not performed. If you want to recursively merge arrays in a certain group of configs, such as params, you must pass RecursiveMerge modifier with specified group names to the Config constructor:

use Yiisoft\Config\Config;
use Yiisoft\Config\ConfigPaths;
use Yiisoft\Config\Modifier\RecursiveMerge;

$config = new Config(
    new ConfigPaths(dirname(__DIR__)),
    'dev',
    [
        RecursiveMerge::groups('params', 'events', 'events-web', 'events-console'),
    ],
);

$params = $config->get('params'); // merged recursively

Reverse merge of arrays

Result of reverse merge is being ordered descending by data source. It is useful for merging module config with base config where more specific config (i.e. module's) has more priority. One of such cases is merging events.

To enable reverse merge pass ReverseMerge modifier with specified group names to the Config constructor:

use Yiisoft\Config\Config;
use Yiisoft\Config\ConfigPaths;
use Yiisoft\Config\Modifier\ReverseMerge;

$config = new Config(
    new ConfigPaths(dirname(__DIR__)),
    'dev',
    [
        ReverseMerge::groups('events', 'events-web', 'events-console'),
    ],
);

$events = $config->get('events-console'); // merged reversed

Remove elements from vendor package configuration

Sometimes it is necessary to remove some elements of vendor packages configuration. To do this, pass RemoveFromVendor modifier to the Config constructor.

Remove specified key paths:

use Yiisoft\Config\Config;
use Yiisoft\Config\ConfigPaths;
use Yiisoft\Config\Modifier\RemoveFromVendor;

$config = new Config(
    new ConfigPaths(dirname(__DIR__)),
    'dev',
    [
        // Remove elements `key-for-remove` and `nested→key→for-remove` from all groups in all vendor packages
        RemoveFromVendor::keys(
            ['key-for-remove'],
            ['nested', 'key', 'for-remove'],
        ),
        
        // Remove elements `a` and `b` from all groups in package `yiisoft/auth`
        RemoveFromVendor::keys(['a'], ['b'])
            ->package('yiisoft/auth'),
        
        // Remove elements `c` and `d` from groups `params` and `web` in package `yiisoft/view`
        RemoveFromVendor::keys(['c'], ['d'])
            ->package('yiisoft/view', 'params', 'web'),
        
        // Remove elements `e` and `f` from all groups in package `yiisoft/auth`
        // and from groups `params` and `web` in package `yiisoft/view`
        RemoveFromVendor::keys(['e'], ['f'])
            ->package('yiisoft/auth')
            ->package('yiisoft/view', 'params', 'web'),
    ],
);

$params = $config->get('params');

Remove specified configuration groups:

use Yiisoft\Config\Config;
use Yiisoft\Config\ConfigPaths;
use Yiisoft\Config\Modifier\RemoveFromVendor;

$config = new Config(
    new ConfigPaths(dirname(__DIR__)),
    'dev',
    [
        RemoveFromVendor::groups([
            // Remove group `params` from all vendor packages
            '*' => 'params',
            
            // Remove groups `common` and `web` from all vendor packages
            '*' => ['common', 'web'],
            
            // Remove all groups from package `yiisoft/auth`
            'yiisoft/auth' => '*',
            
            // Remove groups `params` from package `yiisoft/http`
            'yiisoft/http' => 'params',
            
            // Remove groups `params` and `common` from package `yiisoft/view`
            'yiisoft/view' => ['params', 'common'],
        ]),
    ],
);

Combine modifiers

Config supports simultaneous use of several modifiers:

use Yiisoft\Config\Config;
use Yiisoft\Config\ConfigPaths;
use Yiisoft\Config\Modifier\RecursiveMerge;
use Yiisoft\Config\Modifier\RemoveFromVendor;
use Yiisoft\Config\Modifier\ReverseMerge;

$config = new Config(
    new ConfigPaths(dirname(__DIR__)),
    'dev',
    [
        RecursiveMerge::groups('params', 'events', 'events-web', 'events-console'),
        ReverseMerge::groups('events', 'events-web', 'events-console'),
        RemoveFromVendor::keys(
            ['key-for-remove'],
            ['nested', 'key', 'for-remove'],
        ),
    ],
);

Commands

yii-config-copy

The plugin adds extra yii-config-copy command to Composer. It copies the package config files from the vendor to the config directory of the root package:

composer yii-config-copy <package-name> [target-path] [files]

Copies all config files of the yiisoft/view package:

# To the `config` directory
composer yii-config-copy yiisoft/view

# To the `config/my/path` directory
composer yii-config-copy yiisoft/view my/path

Copies the specified config files of the yiisoft/view package:

# To the `config` directory
composer yii-config-copy yiisoft/view / params.php web.php

# To the `config/my/path` directory and without the file extension
composer yii-config-copy yiisoft/view my/path params web

In order to avoid conflicts with file names, a prefix is added to the names of the copied files: yiisoft-view-params.php, yiisoft-view-web.php.

yii-config-rebuild

The yii-config-rebuild command updates merge plan file. This command may be used if you have added files or directories to the application configuration file structure and these were not specified in composer.json of the root package. In this case you need to add to the information about new files to composer.json of the root package by executing the command:

composer yii-config-rebuild

Testing

Unit testing

The package is tested with PHPUnit. To run tests:

./vendor/bin/phpunit --testdox --no-interaction

Mutation testing

The package tests are checked with Infection mutation framework with Infection Static Analysis Plugin. To run it:

./vendor/bin/roave-infection-static-analysis-plugin

Static analysis

The code is statically analyzed with Psalm. To run static analysis:

./vendor/bin/psalm

License

The config package is free software. It is released under the terms of the BSD License. Please see LICENSE for more information.

Maintained by Yii Software.

Credits

The plugin is heavily inspired by Composer config plugin originally created by HiQDev (http://hiqdev.com/) in 2016 and then adopted by Yii.

Support the project

Open Collective

Follow updates

Official website Twitter Telegram Facebook Slack