MidТеория7 min

Конфигурация

YAML, PHP, XML конфигурация, environment variables, параметры, секреты, config/packages

Система конфигурации Symfony — одна из ключевых тем экзамена. Нужно знать форматы конфигурации, приоритеты загрузки, работу с environment variables, параметрами и секретами.

Форматы конфигурации

Symfony поддерживает три формата конфигурации: YAML, PHP и XML.

YAML (самый распространённый)

# config/packages/framework.yaml
framework:
    secret: '%env(APP_SECRET)%'
    http_method_override: false
    handle_all_throwables: true
    php_errors:
        log: true
    session:
        handler_id: null
        cookie_secure: auto
        cookie_samesite: lax

PHP (рекомендуемый в Symfony 8.0)

<?php

declare(strict_types=1);

// config/packages/framework.php
use Symfony\Config\FrameworkConfig;

return static function (FrameworkConfig $framework): void {
    $framework->secret('%env(APP_SECRET)%');
    $framework->httpMethodOverride(false);
    $framework->handleAllThrowables(true);

    $framework->phpErrors()
        ->log(true);

    $framework->session()
        ->handlerId(null)
        ->cookieSecure('auto')
        ->cookieSameSite('lax');
};

XML

<!-- config/packages/framework.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:framework="http://symfony.com/schema/dic/symfony">

    <framework:config secret="%env(APP_SECRET)%"
        http-method-override="false"
        handle-all-throwables="true">
        <framework:php-errors log="true"/>
        <framework:session handler-id="null"
            cookie-secure="auto"
            cookie-samesite="lax"/>
    </framework:config>
</container>

Подвох экзамена: PHP-формат конфигурации обеспечивает автодополнение IDE (typed config classes вроде FrameworkConfig). YAML — наиболее читаемый. XML — наиболее строгий (валидация по XSD). Все форматы равноценны по функциональности, но PHP стал рекомендуемым в Symfony 8.0.

Структура config/

config/
├── bundles.php                   # Registered bundles
├── packages/                     # Package configuration
│   ├── cache.yaml                # Cache configuration
│   ├── doctrine.yaml             # Doctrine ORM
│   ├── framework.yaml            # Framework core
│   ├── messenger.yaml            # Messenger (queues)
│   ├── routing.yaml              # Routing defaults
│   ├── security.yaml             # Security
│   ├── twig.yaml                 # Twig templates
│   └── validator.yaml            # Validator
├── packages/dev/                 # Dev-only overrides
│   ├── debug.yaml
│   ├── monolog.yaml
│   └── web_profiler.yaml
├── packages/prod/                # Prod-only overrides
│   └── monolog.yaml
├── packages/test/                # Test-only overrides
│   └── framework.yaml
├── routes.yaml                   # Main routes
├── routes/                       # Route imports
│   ├── framework.yaml
│   └── web_profiler.yaml
├── services.yaml                 # Service container
└── services_test.yaml            # Test service overrides

Порядок загрузки конфигурации

1. config/packages/*.yaml          — базовая конфигурация (все окружения)
2. config/packages/{env}/*.yaml    — переопределения для конкретного окружения
3. config/services.yaml            — определение сервисов
4. config/services_{env}.yaml      — переопределения сервисов для окружения

Подвох экзамена: Файлы из config/packages/dev/ загружаются ПОСЛЕ файлов из config/packages/ и ПЕРЕОПРЕДЕЛЯЮТ их. Если framework.yaml устанавливает session.handler_id: null, а dev/framework.yaml устанавливает session.handler_id: 'session.handler.native_file', в dev будет использоваться файловый handler.

Environment Variables

.env файлы

# .env — defaults, committed to Git
APP_ENV=dev
APP_SECRET=some-default-secret
DATABASE_URL="postgresql://user:[email protected]:5432/mydb?serverVersion=16"

# .env.local — local overrides, NOT committed
APP_SECRET=my-real-secret
DATABASE_URL="postgresql://root:root@db:5432/localdb"

# .env.test — test environment defaults
APP_ENV=test
DATABASE_URL="postgresql://test:[email protected]:5432/testdb"

# .env.test.local — local test overrides, NOT committed

Порядок загрузки .env

1. .env                   — defaults (committed)
2. .env.local             — local overrides (not committed)
3. .env.{env}             — environment-specific (committed)
4. .env.{env}.local       — local env-specific (not committed)
Файл Git Окружение Приоритет
.env Да Все Самый низкий
.env.local Нет Все кроме test 2
.env.test Да Test 3
.env.test.local Нет Test Самый высокий

Подвох экзамена: .env.local НЕ загружается в test-окружении! Это предотвращает случайное использование production-переменных в тестах. Для тестов используйте .env.test и .env.test.local. Реальные env-переменные системы ВСЕГДА имеют наивысший приоритет.

Использование env-переменных в конфигурации

# config/packages/doctrine.yaml
doctrine:
    dbal:
        url: '%env(DATABASE_URL)%'

# Typed env processors
framework:
    secret: '%env(APP_SECRET)%'           # string (default)

parameters:
    redis_port: '%env(int:REDIS_PORT)%'   # cast to int
    debug_mode: '%env(bool:DEBUG)%'       # cast to bool
    api_key: '%env(base64:API_KEY)%'      # base64 decode
    config: '%env(json:CONFIG_JSON)%'     # JSON decode
    db_url: '%env(resolve:DATABASE_URL)%' # resolve %param% inside
    file: '%env(file:SECRET_FILE)%'       # read file contents
    trimmed: '%env(trim:SOME_VAR)%'       # trim whitespace
    url_parts: '%env(key:host:url:DATABASE_URL)%' # extract URL part

Environment Variable Processors

<?php

declare(strict_types=1);

// Available env processors in Symfony:
// string  — default, no conversion
// bool    — 'true'/'1' → true, 'false'/'0'/'' → false
// int     — cast to integer
// float   — cast to float
// json    — json_decode
// base64  — base64_decode
// file    — read file contents
// trim    — trim whitespace
// key     — extract key from parsed value
// url     — parse_url()
// query_string — parse_str()
// resolve — resolve %parameters% in the value
// csv     — str_getcsv()
// shuffle — shuffle array
// const   — PHP constant value
// enum    — PHP enum case
// default — fallback value if empty
// not     — negate boolean
// require — throw if empty

// Chaining processors (right to left):
// '%env(int:trim:file:DATABASE_PORT_FILE)%'
// 1. Read file contents
// 2. Trim whitespace
// 3. Cast to int

Подвох экзамена: Env processors применяются справа налево (как функции). %env(int:trim:FILE_VAR)% сначала вызовет trim, потом int. Процессор default позволяет задать fallback: %env(default:fallback_param:MAYBE_EMPTY)%.

Parameters (параметры)

Параметры — это статические значения, доступные во всём приложении.

# config/services.yaml
parameters:
    app.items_per_page: 20
    app.supported_locales: ['en', 'ru', 'de']
    app.admin_email: '[email protected]'

services:
    App\Service\PaginationService:
        arguments:
            $itemsPerPage: '%app.items_per_page%'

Доступ к параметрам

<?php

declare(strict_types=1);

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\Response;

class SettingsController extends AbstractController
{
    public function __construct(
        // Inject parameter via #[Autowire]
        #[Autowire('%app.items_per_page%')]
        private readonly int $itemsPerPage,

        #[Autowire('%app.admin_email%')]
        private readonly string $adminEmail,

        // Inject env variable
        #[Autowire('%env(APP_SECRET)%')]
        private readonly string $appSecret,
    ) {
    }

    public function index(): Response
    {
        // In controller — access via getParameter()
        $locales = $this->getParameter('app.supported_locales');

        return $this->json([
            'items_per_page' => $this->itemsPerPage,
            'locales' => $locales,
        ]);
    }
}

Параметры vs Environment Variables

Характеристика Parameters Env Variables
Где определяются services.yaml .env, OS, Docker
Когда разрешаются Компиляция контейнера Runtime
Типизация Автоматическая Строки (нужны processors)
Использование %param_name% %env(VAR_NAME)%
Назначение Настройки приложения Инфраструктура, секреты

Подвох экзамена: Parameters разрешаются при компиляции контейнера (build time). Env variables разрешаются при ПЕРВОМ обращении (runtime). Поэтому для значений, которые зависят от среды (DATABASE_URL, API keys), всегда используйте env variables, а не parameters.

Secrets (секреты)

Symfony Secrets — встроенная система для безопасного хранения секретных значений.

# Generate encryption keys
php bin/console secrets:generate-keys

# Add a secret
php bin/console secrets:set DATABASE_PASSWORD

# Add for specific environment
php bin/console secrets:set DATABASE_PASSWORD --env=prod

# List secrets (names only, not values)
php bin/console secrets:list

# List with values (decrypted)
php bin/console secrets:list --reveal

# Remove a secret
php bin/console secrets:remove DATABASE_PASSWORD

Структура файлов секретов

config/
└── secrets/
    ├── prod/
    │   ├── prod.encrypt.public.php    # Public key (committed)
    │   ├── prod.decrypt.private.php   # Private key (NOT committed!)
    │   └── prod.DATABASE_PASSWORD.28a...php  # Encrypted secret
    └── dev/
        ├── dev.encrypt.public.php
        ├── dev.decrypt.private.php
        └── dev.DATABASE_PASSWORD.a3f...php
# config/packages/doctrine.yaml
doctrine:
    dbal:
        password: '%env(DATABASE_PASSWORD)%'  # Resolved from secrets vault

Подвох экзамена: Секреты доступны как обычные env-переменные (%env(SECRET_NAME)%). Если реальная env-переменная и секрет имеют одинаковое имя — env-переменная имеет приоритет. Это позволяет переопределять секреты в dev через .env.local.

Конфигурация сервисов

# config/services.yaml
services:
    # Default settings for all services
    _defaults:
        autowire: true        # Auto-inject dependencies
        autoconfigure: true   # Auto-apply tags (EventListener, Command, etc.)

    # Auto-register all classes in src/ as services
    App\:
        resource: '../src/'
        exclude:
            - '../src/DependencyInjection/'
            - '../src/Entity/'
            - '../src/Kernel.php'

    # Explicit service definition with arguments
    App\Service\PaymentGateway:
        arguments:
            $apiKey: '%env(PAYMENT_API_KEY)%'
            $sandbox: '%env(bool:PAYMENT_SANDBOX)%'

    # Service alias
    App\Contract\PaymentInterface: '@App\Service\PaymentGateway'

    # Tagged services
    App\EventListener\AuditListener:
        tags:
            - { name: kernel.event_listener, event: kernel.request, priority: 10 }

PHP-формат конфигурации сервисов

<?php

declare(strict_types=1);

// config/services.php
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $container): void {
    $services = $container->services()
        ->defaults()
            ->autowire()
            ->autoconfigure()
    ;

    $services->load('App\\', '../src/')
        ->exclude([
            '../src/DependencyInjection/',
            '../src/Entity/',
            '../src/Kernel.php',
        ])
    ;

    $services->set(App\Service\PaymentGateway::class)
        ->arg('$apiKey', '%env(PAYMENT_API_KEY)%')
        ->arg('$sandbox', '%env(bool:PAYMENT_SANDBOX)%')
    ;

    $services->alias(
        App\Contract\PaymentInterface::class,
        App\Service\PaymentGateway::class,
    );
};

When (условная конфигурация)

# config/packages/framework.yaml
when@dev:
    framework:
        profiler:
            collect: true

when@test:
    framework:
        test: true
        session:
            storage_factory_id: session.storage.factory.mock_file

when@prod:
    framework:
        profiler:
            collect: false
<?php

declare(strict_types=1);

// config/packages/framework.php — PHP equivalent
use Symfony\Config\FrameworkConfig;

return static function (FrameworkConfig $framework, string $env): void {
    if ($env === 'dev') {
        $framework->profiler()->collect(true);
    }

    if ($env === 'test') {
        $framework->test(true);
        $framework->session()->storageFactoryId('session.storage.factory.mock_file');
    }

    if ($env === 'prod') {
        $framework->profiler()->collect(false);
    }
};

Symfony 8.0: when@env синтаксис в YAML полностью заменяет отдельные файлы в config/packages/dev/, config/packages/prod/. Оба подхода работают, но when@ позволяет хранить всю конфигурацию пакета в одном файле.

Debugging конфигурации

# Show all container parameters
php bin/console debug:container --parameters

# Show specific parameter
php bin/console debug:container --parameter=app.items_per_page

# Show all env variables used in config
php bin/console debug:container --env-vars

# Show specific env variable
php bin/console debug:container --env-var=DATABASE_URL

# Dump resolved configuration for a bundle
php bin/console debug:config framework

# Dump default config for a bundle
php bin/console config:dump-reference framework

# Show all services
php bin/console debug:container

# Find service by class
php bin/console debug:container App\Service\PaymentGateway

Проверь себя

Файл `.env.local` загружается в test-окружении?

Что произойдёт, если секрет и env-переменная имеют одинаковое имя?

Чем parameters отличаются от env variables в Symfony?

Какой env processor преобразует `%env(int:trim:file:PORT_FILE)%`?

В каком порядке загружаются .env файлы в Symfony?