The Concept of Configuration.

Nov 10, 2023Yii2
featured-image

A configuration is a set of data that determines the values of some variables for a computer program or an operating system, as Wikipedia defines it. In this article, we will explore the significance of configuration for an application.

These options are usually loaded when the program starts, and sometimes a restart is required to apply changes. Yii Framework offers a powerful tool for managing configurations: yiisoft/config.

1. Installation.

First, make sure you have PHP 8.0 or higher and Composer 2.0 or higher installed. Then run the following command to install the package.

composer require yiisoft/config:^1.4 --prefer-dist -vvv 
2. Package Configuration.

The yiisoft/config package acts as a plugin. It scans installed packages for the config-plugin extra option in their composer.json files. It then creates a merge plan in the config/.merge-plan.php file, including configuration from each package.

3. Loading Configuration.

In your application’s entry point (usually index.php), create an instance of the configuration loader, example Yii2.

<?php

declare(strict_types=1);

use yii\web\Application;
use Yiisoft\Config\Modifier\RecursiveMerge;
use Yiisoft\Config\Config;
use Yiisoft\Config\ConfigPaths;

// comment out the following two lines when deployed to production
defined('YII_DEBUG') or define('YII_DEBUG', false);

if (getenv('YII_ENV')) {
    defined('YII_ENV') or define('YII_ENV', getenv('YII_ENV'));
} else {
    defined('YII_ENV') or define('YII_ENV', 'prod');
}

if (getenv('YII_C3')) {
    $c3 = dirname(__DIR__) . '/c3.php';

    if (file_exists($c3)) {
        require_once $c3;
    }
}

require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';

$config = new Config(
    new ConfigPaths(dirname(__DIR__), 'config', 'vendor'),
    modifiers: [RecursiveMerge::groups('web', 'params', 'params-web')],
    paramsGroup: 'params-web',
);

$container = Yii::$container->setSingleton(Application::class, $config->get('web'));
$app = Yii::$container->get(Application::class);
$app->run();

4. Configuration composer.json.

In the composer.json file, we add the extra section where we include configurations for our app. In this case, we specify that the configuration file is located at /config/config-plugin.php.

"extra": {
   "config-plugin-file": "/config/config-plugin.php",
}, 

5. Configuration config-pluging.

The library's approach involves organizing configurations into arrays, which are subsequently merged to construct the complete configuration. In our scenario, we define three configuration groups: common for shared configurationsconsole for console-related configurations, and web for web-related configurations.

<?php

declare(strict_types=1);

return [
    'config-plugin' => [
        'common' => 'common/*.php',
        'console' => [
            '$common',
            'console/*.php',
        ],
        'web' => [
            '$common',
            '$yii2-bootstrap5',
            '$yii2-debug',
            '$yii2-gii',
            '$yii2-localeurls',
            'web/*.php'
        ],
        'params' => 'params.php',
        'params-console' => [
            '$params',
            'params-console.php'
        ],
        'params-web' => [
            '$params',
            'params-web.php'
        ],
    ],
    'config-plugin-options' => [
        'package-types' => [
            'composer-plugin',
            'library',
            'yii2-extension',
        ],
        'source-directory' => 'config',
    ],
];

To implement this structure, we create three directories within the config directory. Each directory contains files with the configurations relevant to its designated group.

This is a basic example, but you can have your own custom configuration – that's the true essence of yiisoft/config. In my example, i'll have the directory structure of the config as follows:

root
└── config                  
    ├── Common              Common configuration.
    │   ├── components.php  
    │   └── container.php   
    ├── Console             Console configuration.
    │   ├── app.php
    │   └── components.php     
    ├── Web                 Web configuration.
    │   ├── app.php
    │   ├── bootstrap.php    
    │   ├── components.php
    │   ├── container.php
    │   └── modules.php 
    ├── build.php           Build configuration codeception tests.
    ├── config-plugin.php   Plugin configuration.
    ├── messages.php        Translation configuration.
    ├── params-console.php  Console parameters.
    ├── params-web.php      Web parameters.
    └── params.php          Common parameters.
In common components.php, configuring shared elements between console and web applications: cache and log.

As we can see, yiisoft/config allows us to access parameters within configurations. We just need to access them through $params.

In common/container.php, we configure our definitions in the di container of Yii2, which will be used by both our console and web applications.

<?php

declare(strict_types=1);

use yii\symfonymailer\Mailer;

/**
 * @var array $params
 */
return [
    'container' => [
        'definitions' => [
            Mailer::class => [
                'useFileTransport' => $params['common.mailer.useFileTransport'],
            ],
       ],
   ],
];

In the consoleapplication configuration is simplified by leveraging yiisoft/config. Now, we define properties with configurable parameters, showcasing the full power of yiisoft/config.

console.php:

<?php

declare(strict_types=1);

/**
 * @var array $params
 */
return [
   'id' => $params['console.id'],
   'aliases' => $params['common.aliases'],
   'basePath' => dirname(__DIR__, 2),
   'bootstrap' => $params['common.bootstrap'],
   'controllerMap' => $params['console.controllerMap'],
   'params' => $params['console.params'],
   'runtimePath' => $params['common.runtime.path'],
];

Now, let's define our common parameters in params.php and console specific ones in params-console.php. These will configure our properties. The beauty is, we don't need to edit configuration files; just adjust parameters. A significant advantage with yiisoft/config.

params.php:

<?php

declare(strict_types=1);

$rootDir = dirname(__DIR__);

return [
    'common.aliases' => [
        '@app' => $rootDir,
        '@resource' => '@app/src/Framework/resource',
    ],
    'common.bootstrap' => ['log'],
    'common.log.levels' => ['error', 'warning', 'info'],
    'common.log.logFile' => '@runtime/logs/app.log',
    'common.mailer.useFileTransport' => true,
    'common.root.dir' => $rootDir,
    'common.runtime.path' => $rootDir . '/public/runtime',
];
params-console.php:
<?php

declare(strict_types=1);

use App\UseCase\Hello\HelloController;
use yii\console\controllers\ServeController;

$rootDir = dirname(__DIR__);

return [
    'console.id' => 'console.basic',
    'console.controllerMap' => [
        'hello' => HelloController::class,
        'serve' => [
            'class' => ServeController::class,
            'docroot' => "$rootDir/public",
        ],
    ],
    'console.params' => [],
];
Script yii (console).
#!/usr/bin/env php
<?php

declare(strict_types=1);

use yii\console\Application;
use Yiisoft\Config\Config;
use Yiisoft\Config\ConfigPaths;
use Yiisoft\Config\Modifier\RecursiveMerge;

defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');

require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/vendor/yiisoft/yii2/Yii.php';

$config = new Config(
    new ConfigPaths(__DIR__, 'config', 'vendor'),
    modifiers: [RecursiveMerge::groups('console', 'params', 'params-console')],
    paramsGroup: 'params-console',
);

$container = Yii::$container->setSingleton(Application::class, $config->get('console'));
$app = Yii::$container->get(Application::class);
$exitCode = $app->run();

exit($exitCode);
After completing these steps, simply calling $config->get('console') gives us our fully assembled configuration automatically. And if we want to make changes, we just modify the parameters.
array:9 [
"components" => array:2 [
    "cache" => array:1 [
      "class" => "yii\caching\FileCache"
    ]
    "log" => array:2 [
      "traceLevel" => 3
      "targets" => array:1 [
        0 => array:3 [
          "class" => "yii\log\FileTarget"
          "levels" => array:3 [
            0 => "error"
            1 => "warning"
            2 => "info"
          ]
          "logFile" => "@runtime/logs/app.log"
        ]
      ]
    ]
  ]
  "container" => array:1 [
    "definitions" => array:1 [
      "yii\symfonymailer\Mailer" => array:1 [
        "useFileTransport" => true
      ]
    ]
  ]
  "id" => "console.basic"
  "aliases" => array:3 [
    "@yii2-extension-datetime-picker" => "@vendor/yii2-extensions/datetime-picker"
    "@app" => "K:\yii2-extensions\app-basic"
    "@resource" => "@app/src/Framework/resource"
  ]
  "basePath" => "K:\yii2-extensions\app-basic"
  "bootstrap" => array:1 [
    0 => "log"
  ]
  "controllerMap" => array:2 [
    "hello" => "App\UseCase\Hello\HelloController"
    "serve" => array:2 [
      "class" => "yii\console\controllers\ServeController"
      "docroot" => "K:\yii2-extensions\app-basic/public"
    ]
  ]
  "params" => []
  "runtimePath" => "K:\yii2-extensions\app-basic/public/runtime"
]

Happy coding!

For more details, you can check out our repository app-basic and explore the complete demonstration of configuration using yiisoft/config.

Soon the second part of this post.