Configuração
Um app Yii3 é montado a partir de arrays de configuração: definições do container, parâmetros, rotas, listeners de eventos. Parte desses arrays vem dos pacotes instalados em vendor/, parte vem da pasta config/ da aplicação, e parte muda conforme o ambiente. Quem junta tudo é o pacote yiisoft/config. Este capítulo explica como ele decide o que carregar e em que ordem, e onde colocar a sua configuração.
yiisoft/config e o merge plan
O pacote tem duas partes:
- Um plugin do Composer. Sempre que o Composer atualiza o autoload (
install,require,update,dump-autoload), o plugin procura os pacotes que declaram configuração e grava um plano de merge emconfig/.merge-plan.php. - Um carregador, a classe
Yiisoft\Config\Config, que lê o plano em tempo de execução e devolve cada grupo já mesclado.
Um pacote declara a sua configuração no extra.config-plugin do próprio composer.json. O yiisoft/session, por exemplo, traz config/params.php (opções da sessão) e config/di-web.php (a definição do SessionInterface). Por isso, depois de um composer require, o serviço já está disponível sem você configurar nada.
A aplicação declara a sua parte em config/configuration.php, apontado pelo extra.config-plugin-file do composer.json. Um trecho do arquivo do template:
return [
'config-plugin' => [
'params' => [
'common/params.php',
'?common/params/*.php',
],
'params-web' => [
'$params',
'web/params.php',
],
'di' => 'common/di/*.php',
'di-web' => [
'$di',
'web/di/*.php',
],
'events' => '?common/events/*.php',
'events-web' => ['$events'],
'routes' => [
'common/routes.php',
'?common/routes/*.php',
],
// ...
],
'config-plugin-environments' => [
Environment::DEV => [
'params' => [
'environments/dev/params.php',
],
],
// ...
],
'config-plugin-options' => [
'source-directory' => 'config',
],
];Cada chave de config-plugin é um grupo, e o valor é a lista de arquivos que formam o grupo, relativos a config/. Na lista valem três marcadores:
*: curinga.common/di/*.phpcarrega todos os arquivos da pasta, então um arquivo novo emconfig/common/di/passa a valer sem você registrar em lugar nenhum.?: arquivo opcional. Sem o?, um arquivo que não existe causa erro; com ele, um curinga que não encontra nada também é aceito.$: referência a outro grupo.params-webcomeça com tudo que está emparamse acrescentaweb/params.php.
Depois de alterar config/configuration.php, reconstrua o plano:
composer yii-config-rebuildArquivos novos dentro de uma pasta já coberta por um curinga não precisam disso: o curinga é resolvido em tempo de execução.
Rotas, params e eventos em vários arquivos
O template original tem só common/routes.php no grupo routes, só common/params.php no grupo params e o grupo events vazio. O app de exemplo deste guia acrescentou as entradas '?common/routes/*.php', '?common/params/*.php' e '?common/events/*.php' mostradas acima. Assim, cada seção do guia tem o seu próprio arquivo de rotas (config/common/routes/requisicoes.php, config/common/routes/conceitos.php...), de params e de eventos, sem mexer nos arquivos das outras. O ? deixa as pastas começarem vazias.
Num projeto real, a mesma técnica serve para separar a configuração por módulo ou por área do sistema.
Os grupos
Os grupos que o template define, e quem os usa:
params,params-web,params-console: parâmetros, ou seja, valores simples como nomes, chaves e opções. Disponíveis como$paramsdentro dos outros arquivos de configuração.di,di-web,di-console: definições do container. Veja Container de DI.di-providers*,di-delegates*: provedores de serviço e containers delegados, para casos avançados.events,events-web,events-console: listeners de eventos. Veja Eventos.routes: as rotas, lidas emconfig/common/di/router.phpcom$config->get('routes').bootstrap*: funções executadas logo depois de o container ser criado.
Os grupos com sufixo -web são lidos pelo HttpApplicationRunner (em public/index.php), e os com -console pelo ConsoleApplicationRunner (no ./yii). É assim que o container da web tem o SessionInterface e o do console não. Os grupos sem sufixo são a base comum, incluída nos outros pela referência $.
Params na prática
Parâmetros servem para tirar valores do código. Crie config/common/params/conceitos.php:
<?php
declare(strict_types=1);
return [
'conceitos' => [
'saudacao' => 'Bem-vindo ao guia',
'itensPorPagina' => 10,
'remetente' => 'loja@guia.test',
],
'yiisoft/aliases' => [
'aliases' => [
'@relatorios' => '@runtime/relatorios',
],
],
];A chave conceitos é nossa. A chave yiisoft/aliases pertence ao pacote de aliases e é explicada em Aliases. Agrupe os seus parâmetros sob uma chave própria, para não colidir com os pacotes.
A classe que usa os valores não lê $params. Ela recebe os valores no construtor, como qualquer dependência. Em src/Conceitos/Saudacao.php:
<?php
declare(strict_types=1);
namespace App\Conceitos;
final readonly class Saudacao
{
public function __construct(
private string $mensagem,
private int $itensPorPagina,
) {}
public function texto(): string
{
return "{$this->mensagem} (mostrando {$this->itensPorPagina} itens por página)";
}
}E a ligação entre os dois fica numa definição do container, em config/common/di/conceitos.php. Um trecho:
/** @var array $params */
return [
Saudacao::class => [
'__construct()' => [
'mensagem' => $params['conceitos']['saudacao'],
'itensPorPagina' => $params['conceitos']['itensPorPagina'],
],
],
// ...
];Dentro dos arquivos de configuração, a variável $params já existe, com o grupo de params mesclado. O comentário @var só serve para o editor. Uma action que pede Saudacao no construtor recebe a instância pronta; com a rota /conceitos/configuracao, a resposta é:
Bem-vindo ao guia (mostrando 10 itens por página)Essa separação é proposital: Saudacao não sabe que existe um arquivo de configuração, e num teste você cria new Saudacao('Oi', 5) sem carregar nada.
Também existe a variável $config nos arquivos de configuração, com o próprio objeto Config. O template a usa em config/common/di/router.php para ler o grupo routes.
Ordem e sobrescrita
A configuração é montada em três camadas, nesta ordem:
- Pacotes em
vendor/, que trazem os valores padrão. - A aplicação, em
config/. - O ambiente atual, em
config/environments/<ambiente>/.
Uma camada pode sobrescrever o que veio da anterior. Para os params, o merge é recursivo: o seu arquivo pode mudar só ['yiisoft/session']['session']['options'] sem repetir o resto da configuração do pacote. Os runners do Yii3 ligam esse merge recursivo para os grupos de params e de eventos.
Dentro de uma mesma camada, a mesma chave definida em dois arquivos gera erro. Se dois arquivos de config/common/di/ definirem Saudacao::class, o carregamento falha com Duplicate key "..." in the following configs, citando os dois arquivos. A regra protege contra um arquivo sobrescrever o outro sem ninguém perceber. Arrays de params com chaves diferentes, como as duas entradas yiisoft/aliases do template e do arquivo acima, são mesclados normalmente.
Ambientes: dev, prod e test
A seção config-plugin-environments acrescenta arquivos a um grupo só num ambiente. No template, cada ambiente tem o seu environments/<ambiente>/params.php, e o de dev define o parâmetro traceLink, usado pela página de erro para abrir arquivos no editor.
O ambiente vem da variável APP_ENV, lida pela classe App\Environment do template e passada ao runner:
$runner = new HttpApplicationRunner(
rootPath: $root,
debug: Environment::appDebug(),
checkEvents: Environment::appDebug(),
environment: Environment::appEnv(),
// ...
);Os valores aceitos são dev, test e prod, e o padrão, quando a variável não existe, é prod. Em desenvolvimento, o .env do template define APP_ENV=dev e APP_DEBUG=true.
Para conferir o que cada ambiente enxerga, dá para usar o carregador direto, da raiz do projeto:
php -r '
require "vendor/autoload.php";
use Yiisoft\Config\Config;
use Yiisoft\Config\ConfigPaths;
use Yiisoft\Config\Modifier\RecursiveMerge;
foreach (["dev", "prod"] as $env) {
$config = new Config(new ConfigPaths(getcwd(), "config"), $env, [RecursiveMerge::groups("params")]);
$params = $config->get("params");
echo $env, ": ", $params["traceLink"] ?? "(não definido)", PHP_EOL;
}'dev: phpstorm://open?url=file://{file}&line={line}
prod: (não definido)O RecursiveMerge::groups("params") reproduz o que o runner faz. Sem ele, o carregamento dos params falha com Duplicate key "yiisoft/yii-debug", porque dois pacotes definem essa chave e esperam o merge recursivo.
APP_ENV escolhe quais arquivos de configuração entram. APP_DEBUG é independente: liga o modo debug do tratamento de erros e a validação das definições do container e dos eventos. Em produção, os dois ficam desligados.
Para ver os grupos e arquivos que um pacote fornece, use composer yii-config-info yiisoft/session (sem o nome do pacote, ele mostra os da aplicação). O pacote também tem o composer yii-config-copy, que copia os arquivos de configuração de um pacote para a sua pasta config/, com um prefixo no nome, como ponto de partida para editar.
Comparando com o Yii2
- O
config/web.phpcomcomponentseparams, montado à mão comrequireeArrayHelper::merge(), vira grupos mesclados automaticamente peloyiisoft/config. - A configuração dos pacotes vem junto com eles. No Yii2, extensões usavam
bootstrapou pediam que você copiasse a configuração. Yii::$app->params['chave']não existe. Os params só são lidos dentro dos arquivos de configuração e chegam às classes pelo construtor.YII_ENVeYII_DEBUGviramAPP_ENVeAPP_DEBUG.