Конфігурація — Symfony
Файли конфігурації
Symfony-застосунки конфігуруються файлами, що зберігаються в теці config/, яка має таку структуру за замовчуванням:
your-project/
├─ config/
│ ├─ packages/
│ ├─ routes/
│ ├─ bundles.php
│ ├─ preload.php
│ ├─ reference.php
│ ├─ routes.yaml
│ └─ services.yaml
- Файл
routes.yamlвизначає конфігурацію маршрутизації; - Файл
services.yamlконфігурує сервіси контейнера сервісів; - Файл
bundles.phpвмикає/вимикає пакети у вашому застосунку; - Файл
preload.phpвизначає класи для попереднього завантаження через OPcache; - Файл
reference.phpгенерується Symfony автоматично й містить визначення, які покращують автодоповнення в IDE та статичний аналіз, коли ви використовуєте PHP як формат конфігурації; - Тека
config/packages/зберігає конфігурацію кожного пакета, встановленого у вашому застосунку; - Тека
config/routes/зберігає конфігурацію маршрутизації, яку завантажують встановлені пакети (наприклад,framework.yaml).
Пакети (їх також називають «бандлами» (bundles) в Symfony і «плагінами/модулями» в інших проєктах) додають до ваших проєктів готові до використання можливості.
Коли ви використовуєте Symfony Flex, який увімкнено за замовчуванням у Symfony-застосунках, пакети під час встановлення автоматично оновлюють файл bundles.php і створюють нові файли в config/packages/. Наприклад, ось файл, який за замовчуванням створює бандл «API Platform»:
# config/packages/api_platform.yaml
api_platform:
mapping:
paths: ['%kernel.project_dir%/src/Entity']
Розділення конфігурації на безліч маленьких файлів декому з новачків у Symfony може здатися лячним. Проте ви швидко до них звикнете, і вам рідко доведеться змінювати ці файли після встановлення пакета.
Порада
Щоб дізнатися про всі доступні опції конфігурації, перегляньте Довідник конфігурації Symfony або виконайте команду
config:dump-reference.
Формати конфігурації
На відміну від інших фреймворків, Symfony не нав'язує вам конкретного формату для конфігурування застосунків, а дає змогу обрати між YAML і PHP. У всій документації Symfony всі приклади конфігурації показані в цих двох форматах.
Практичної різниці між форматами немає. Насправді Symfony перетворює їх усі на PHP і кешує перед запуском застосунку, тож немає навіть різниці в продуктивності.
YAML використовується за замовчуванням при встановленні пакетів, бо він лаконічний і дуже читабельний. Ось основні переваги й недоліки кожного формату:
- YAML: простий, чистий і читабельний, але не всі IDE підтримують для нього автодоповнення й валідацію. Вивчіть синтаксис YAML;
- PHP: дуже потужний, дає змогу створювати динамічну конфігурацію з масивами й отримує переваги автодоповнення та статичного аналізу завдяки формам масивів (array shapes).
Імпорт файлів конфігурації
Symfony завантажує файли конфігурації за допомогою компонента Config, який надає розширені можливості, як-от імпорт інших файлів конфігурації, навіть якщо вони використовують інший формат:
# config/services.yaml
imports:
- { resource: 'legacy_config.php' }
# glob-вирази також підтримуються для завантаження кількох файлів
- { resource: '/etc/myapp/*.yaml' }
# ignore_errors: not_found мовчки відкидає помилки, якщо завантажуваного файлу не існує
- { resource: 'my_config_file.yaml', ignore_errors: not_found }
# ignore_errors: true мовчки відкидає всі помилки (включно з некоректним кодом і відсутністю файлу)
- { resource: 'my_other_config_file.yaml', ignore_errors: true }
# виключення конкретних файлів при використанні glob-шаблонів
- { resource: 'services/*.yaml', exclude: ['services/legacy_*.yaml'] }
# ...
// config/services.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
return App::config([
'imports' => [
['resource' => 'legacy_config.php'],
// glob-вирази також підтримуються для завантаження кількох файлів
['resource' => '/etc/myapp/*.yaml'],
// ignore_errors: not_found мовчки відкидає помилки, якщо завантажуваного файлу не існує
['resource' => 'my_config_file.xml', 'ignore_errors' => 'not_found'],
// ignore_errors: true мовчки відкидає всі помилки (включно з некоректним кодом і відсутністю файлу)
['resource' => 'my_other_config_file.xml', 'ignore_errors' => true],
// виключення конкретних файлів при використанні glob-шаблонів
['resource' => 'services/*.php', 'exclude' => ['services/legacy_*.php']],
],
]);
Параметри конфігурації
Параметри — це пари ключ-значення, які зберігаються в контейнері сервісів. Вони дають змогу централізувати значення конфігурації, перевикористовувати їх у конфігурації застосунку та впроваджувати їх у сервіси.
Коли одне й те саме значення конфігурації використовується в кількох місцях, визначте його як параметр, щоб уникнути повторень. Параметри також можна перевизначати для кожного середовища. За домовленістю параметри визначаються під ключем parameters у файлі config/services.yaml:
# config/services.yaml
parameters:
# ім'я параметра — довільний рядок (префікс 'app.' рекомендовано,
# щоб краще відрізняти ваші параметри від параметрів Symfony).
app.admin_email: '[email protected]'
# булеві параметри
app.enable_v2_protocol: true
# параметри-масиви/колекції
app.supported_locales: ['en', 'es', 'fr']
# параметри з бінарним вмістом (закодуйте вміст через base64_encode())
app.some_parameter: !!binary VGhpcyBpcyBhIEJlbGwgY2hhciAH
# PHP-константи як значення параметрів
app.some_constant: !php/const GLOBAL_CONSTANT
app.another_constant: !php/const App\Entity\BlogPost::MAX_ITEMS
# випадок переліку (enum) як значення параметра
app.some_enum: !php/enum App\Enum\PostState::Published
# ...
// config/services.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use App\Entity\BlogPost;
use App\Enum\PostState;
return App::config([
'parameters' => [
// ім'я параметра — довільний рядок (префікс 'app.' рекомендовано,
// щоб краще відрізняти ваші параметри від параметрів Symfony).
'app.admin_email' => '[email protected]',
// булеві параметри
'app.enable_v2_protocol' => true,
// параметри-масиви/колекції
'app.supported_locales' => ['en', 'es', 'fr'],
// параметри з бінарним вмістом (закодуйте вміст через base64_encode())
'app.some_parameter' => base64_decode('VGhpcyBpcyBhIEJlbGwgY2hhciAA'),
// PHP-константи як значення параметрів
'app.some_constant' => GLOBAL_CONSTANT,
'app.another_constant' => BlogPost::MAX_ITEMS,
// випадок переліку (enum) як значення параметра
'app.some_enum' => PostState::Published,
],
]);
Після визначення ви можете посилатися на значення цього параметра з будь-якого іншого файлу конфігурації за допомогою спеціального синтаксису: обгорніть ім'я параметра двома % (наприклад, %app.admin_email%):
# config/packages/some_package.yaml
some_package:
# будь-який рядок, оточений двома %, замінюється значенням цього параметра
email_address: '%app.admin_email%'
// config/packages/some_package.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
return App::config([
'some_package' => [
// при використанні функції param() достатньо передати лише ім'я параметра...
'email_address' => param('app.admin_email'),
// ... але якщо вам так більше до вподоби, ви можете передати ім'я як рядок,
// оточений двома % (так само, як у форматі YAML), і Symfony
// замінить його значенням цього параметра
'email_address' => '%app.admin_email%',
],
]);
Зверніть увагу
Якщо значення якогось параметра містить символ
%, вам потрібно екранувати його, додавши ще один%, щоб Symfony не вважав це посиланням на ім'я параметра:# config/services.yaml parameters: # Розбирається як 'https://symfony.com/?foo=%s&bar=%d' url_pattern: 'https://symfony.com/?foo=%%s&bar=%%d'// config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; return App::config([ 'parameters' => [ 'url_pattern' => 'http://symfony.com/?foo=%%s&bar=%%d', ], ]);
Параметри конфігурації дуже поширені в Symfony-застосунках. Symfony сам визначає декілька параметрів, зокрема ті, що стосуються конфігурації ядра (наприклад, kernel.project_dir, kernel.debug), а деякі пакети при встановленні додають власні параметри до вашого config/services.yaml.
Порада
За домовленістю параметри, імена яких починаються з крапки
.(наприклад,.mailer.transport), доступні лише під час компіляції контейнера. Вони корисні при роботі з Compiler Passes, щоб оголосити тимчасові параметри, які згодом не будуть доступні в застосунку.
Параметри конфігурації зазвичай не валідуються, але ви можете переконатися, що суттєві для роботи вашого застосунку параметри не порожні:
/** @var ContainerBuilder $container */
$container->parameterCannotBeEmpty('app.private_key', 'Did you forget to set a value for the "app.private_key" parameter?');
Якщо непорожній параметр дорівнює null, порожньому рядку '' або порожньому масиву [], Symfony викине виняток. Ця валідація виконується не під час компіляції, а при спробі отримати значення параметра.
Див. також
Далі в цій статті ви можете прочитати, як отримувати параметри конфігурації в контролерах і сервісах.
Середовища конфігурації
У вас є лише один застосунок, але, усвідомлюєте ви це чи ні, вам потрібно, щоб він поводився по-різному в різний час:
- Під час розробки ви хочете логувати все й мати гарні інструменти для налагодження;
- Після розгортання на продакшн ви хочете, щоб той самий застосунок був оптимізований за швидкістю й логував лише помилки.
Файли, що зберігаються в config/packages/, використовуються Symfony для конфігурування сервісів застосунку. Іншими словами, ви можете змінити поведінку застосунку, змінивши те, які файли конфігурації завантажуються. Це і є ідея середовищ конфігурації Symfony.
Типовий Symfony-застосунок починається з трьох середовищ:
devдля локальної розробки,prodдля продакшн-серверів,testдля автоматизованих тестів.
Під час запуску застосунку Symfony завантажує файли конфігурації в такому порядку (останні файли можуть перевизначати значення, задані в попередніх):
- Файли в
config/packages/*.<extension>; - файли в
config/packages/<environment-name>/*.<extension>; config/services.<extension>;config/services_<environment-name>.<extension>.
Візьмімо для прикладу пакет framework, встановлений за замовчуванням:
- Спочатку
config/packages/framework.yamlзавантажується в усіх середовищах і конфігурує фреймворк деякими опціями; - У середовищі prod нічого додаткового не буде задано, бо файлу
config/packages/prod/framework.yamlнемає; - У середовищі dev файлу теж немає (
config/packages/dev/framework.yamlне існує). - У середовищі test завантажується файл
config/packages/test/framework.yaml, щоб перевизначити деякі з налаштувань, раніше сконфігурованих уconfig/packages/framework.yaml.
Насправді кожне середовище відрізняється від інших лише частково. Це означає, що всі середовища мають велику спільну базу конфігурації, яка розміщується у файлах безпосередньо в теці config/packages/.
Порада
Ви також можете визначати опції для різних середовищ в одному файлі конфігурації за допомогою спеціального ключового слова
when:# config/packages/webpack_encore.yaml webpack_encore: # ... output_path: '%kernel.project_dir%/public/build' strict_mode: true cache: false # кеш увімкнено лише в середовищі "prod" when@prod: webpack_encore: cache: true # вимкнути строгий режим лише в середовищі "test" when@test: webpack_encore: strict_mode: false # синтаксис YAML дозволяє перевикористовувати вміст за допомогою "якорів" (&some_name) та "аліасів" (*some_name). # У цьому прикладі конфігурація 'test' використовує точно ту саму конфігурацію, що й 'prod' when@prod: &webpack_prod webpack_encore: # ... when@test: *webpack_prod// config/packages/webpack_encore.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; return [ 'webpack_encore' => [ 'output_path' => '%kernel.project_dir%/public/build', 'strict_mode' => true, 'cache' => false, ], // кеш увімкнено лише в середовищі "prod" 'when@prod' => [ 'webpack_encore' => [ 'cache' => true, ], ], // вимкнути строгий режим лише в середовищі "test" 'when@test' => [ 'webpack_encore' => [ 'strict_mode' => false, ], ], ],
Коли ви використовуєте PHP-замикання для конфігурування своїх сервісів, окрім виклику методу $container->env(), показаного вище, ви можете автоматично отримати значення поточного середовища, додавши до замикання рядковий аргумент з іменем $env:
// config/packages/my_config.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
return function(ContainerConfigurator $containerConfigurator, string $env): void {
// `$env` заповнюється автоматично, тож ви можете конфігурувати свої
// сервіси залежно від того, у якому середовищі ви перебуваєте
};
Див. також
Перегляньте метод
configureContainer()класу Kernel, щоб дізнатися все про порядок завантаження файлів конфігурації.
Вибір активного середовища
Symfony-застосунки постачаються з файлом .env, розташованим у кореневій теці проєкту. Цей файл використовується для визначення значень змінних середовища й детально описаний далі в цій статті.
Відкрийте файл .env (або, краще, файл .env.local, якщо ви його створили) і змініть значення змінної APP_ENV, щоб змінити середовище, у якому працює застосунок. Наприклад, щоб запустити застосунок у продакшні:
# .env (or .env.local)
APP_ENV=prod
Це значення використовується як для вебу, так і для консольних команд. Проте ви можете перевизначити його для команд, задавши значення APP_ENV перед їх запуском:
# Use the environment defined in the .env file
$ php bin/console command_name
# Ignore the .env file and run this command in production
$ APP_ENV=prod php bin/console command_name
Створення нового середовища
Трьох середовищ, які Symfony надає за замовчуванням, достатньо для більшості проєктів, але ви можете визначати й власні середовища. Наприклад, ось як можна визначити середовище staging, де клієнт може протестувати проєкт перед виходом у продакшн:
- Створіть теку конфігурації з такою самою назвою, як і середовище (у цьому випадку
config/packages/staging/); - Додайте потрібні файли конфігурації до
config/packages/staging/, щоб визначити поведінку нового середовища. Symfony спочатку завантажує файлиconfig/packages/*.yaml, тож вам потрібно сконфігурувати лише відмінності від цих файлів; - Виберіть середовище
stagingза допомогою змінної середовищаAPP_ENV, як пояснено в попередньому розділі.
Порада
Середовища часто схожі одне на одне, тож ви можете використовувати символьні посилання між теками
config/packages/<environment-name>/, щоб перевикористовувати ту саму конфігурацію.
Замість створення нових середовищ ви можете використовувати змінні середовища, як пояснено в наступному розділі. Так ви можете використовувати той самий застосунок і середовище (наприклад, prod), але змінювати його поведінку завдяки конфігурації на основі змінних середовища (наприклад, щоб запускати застосунок у різних сценаріях: staging, забезпечення якості, перегляд клієнтом тощо).
Конфігурація на основі змінних середовища
Використання змінних середовища (або скорочено «env vars») — поширена практика для того, щоб:
- Конфігурувати опції, які залежать від того, де запущено застосунок (наприклад, облікові дані бази даних зазвичай різні на продакшні й на вашій локальній машині);
- Конфігурувати опції, які можуть динамічно змінюватися в продакшн-середовищі (наприклад, щоб оновити значення простроченого API-ключа без повторного розгортання всього застосунку).
В інших випадках рекомендується й далі використовувати параметри конфігурації.
Використовуйте спеціальний синтаксис %env(ENV_VAR_NAME)%, щоб посилатися на змінні середовища. Значення цих опцій обчислюються під час виконання (лише один раз на запит, щоб не впливати на продуктивність), тож ви можете змінювати поведінку застосунку без очищення кешу.
Цей приклад показує, як можна сконфігурувати secret застосунку за допомогою змінної середовища:
# config/packages/framework.yaml
framework:
# за домовленістю імена змінних середовища завжди у верхньому регістрі
secret: '%env(APP_SECRET)%'
# ...
// config/packages/framework.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
return App::config([
'framework' => [
// за домовленістю імена змінних середовища завжди у верхньому регістрі
'secret' => env('APP_SECRET'),
],
]);
Зверніть увагу
До ваших змінних середовища також можна отримати доступ через PHP-суперглобали
$_ENVі$_SERVER(обидві еквівалентні):$databaseUrl = $_ENV['DATABASE_URL']; // mysql://db_user:[email protected]:3306/db_name $env = $_SERVER['APP_ENV']; // prodПроте в Symfony-застосунках у цьому немає потреби, бо система конфігурації надає кращий спосіб роботи зі змінними середовища.
Див. також
Значеннями змінних середовища можуть бути лише рядки, але Symfony містить кілька процесорів змінних середовища для перетворення їхнього вмісту (наприклад, щоб перетворити рядкове значення на ціле число).
Нове у версії 8.1
Підтримку
.в іменах змінних середовища (наприклад,FOO.BAR) було додано в Symfony 8.1.
Щоб задати значення змінної середовища, у вас є кілька варіантів:
- Додати значення до файлу
.env; - Зашифрувати значення як secret;
- Задати значення як справжню змінну середовища у вашій оболонці або вебсервері.
Якщо ваш застосунок намагається використати змінну середовища, яка не була визначена, ви побачите виняток. Ви можете цьому запобігти, визначивши значення за замовчуванням для змінної середовища. Для цього визначте параметр з тим самим іменем, що й змінна середовища, використовуючи такий синтаксис:
# config/services.yaml
parameters:
# якщо значення змінної середовища SECRET ніде не визначено, Symfony використає це значення
env(SECRET): 'some_secret'
# ...
// config/services.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
return App::config([
'parameters' => [
'env(SECRET)' => 'some_secret',
],
]);
Порада
Деякі хостинги — як-от Upsun.com — пропонують зручні утиліти для керування змінними середовища на продакшні.
Зверніть увагу
Деякі можливості конфігурації несумісні зі змінними середовища. Наприклад, визначення деяких параметрів контейнера умовно, залежно від наявності іншої опції конфігурації. При використанні змінної середовища опція конфігурації існує завжди, бо її значенням буде
null, коли відповідна змінна середовища не визначена.
Небезпека
Пам'ятайте, що виведення вмісту змінних
$_SERVERі$_ENVабо виведення вмістуphpinfo()покаже значення змінних середовища, розкривши чутливу інформацію, як-от облікові дані бази даних.Значення змінних середовища також відображаються у вебінтерфейсі профайлера Symfony. На практиці це не має бути проблемою, бо вебпрофайлер ніколи не повинен бути увімкнений на продакшні.
Конфігурування змінних середовища у файлах .env
Замість визначення змінних середовища у вашій оболонці чи вебсервері Symfony надає зручний спосіб визначити їх усередині файлу .env (з крапкою на початку), розташованого в корені вашого проєкту.
Файл .env читається й розбирається на кожному запиті, а його змінні середовища додаються до PHP-змінних $_ENV і $_SERVER. Наявні змінні середовища ніколи не перезаписуються значеннями, визначеними в .env, тож ви можете поєднувати обидва підходи.
Наприклад, щоб визначити змінну середовища DATABASE_URL, показану раніше в цій статті, ви можете додати:
# .env
DATABASE_URL="mysql://db_user:[email protected]:3306/db_name"
Цей файл слід комітити до вашого репозиторію і (з огляду на це) він має містити лише «типові» значення, придатні для локальної розробки. Цей файл не повинен містити продакшн-значень.
Окрім ваших власних змінних середовища, цей файл .env також містить змінні середовища, визначені сторонніми пакетами, встановленими у вашому застосунку (вони додаються автоматично Symfony Flex при встановленні пакетів).
Порада
Оскільки файл
.envчитається й розбирається на кожному запиті, вам не потрібно очищати кеш Symfony чи перезапускати PHP-контейнер, якщо ви використовуєте Docker.
Синтаксис файлу .env
Додавайте коментарі, ставлячи перед ними #:
# database credentials
DB_USER=root
DB_PASS=pass # this is the secret password
Використовуйте змінні середовища у значеннях, ставлячи перед змінними $:
DB_USER=root
DB_PASS=${DB_USER}pass # include the user as a password prefix
Попередження
Порядок важливий, коли якась змінна середовища залежить від значення інших змінних середовища. У наведеному вище прикладі
DB_PASSмає бути визначено післяDB_USER. Ба більше, якщо ви визначите кілька файлів.envі поставитеDB_PASSпершим, її значення залежатиме від значенняDB_USER, визначеного в інших файлах, а не від значення, визначеного в цьому файлі.
Визначте значення за замовчуванням на випадок, якщо змінну середовища не задано:
DB_USER=
DB_PASS=${DB_USER:-root}pass # results in DB_PASS=rootpass
Обгортайте значення в одинарні лапки, щоб використовувати їх як буквальні рядки, де $, # та інші спеціальні символи не мають спеціального значення:
DB_PASS='p@ss#w$rd'
З подвійними лапками змінні все ще інтерполюються, але # та інші символи трактуються буквально:
DB_PASS="p@ss#word"
DB_NAME="my_${DB_USER}_database"
Вбудовуйте команди через $() (не підтримується у Windows):
START_TIME=$(date)
Попередження
Використання
$()може не спрацювати залежно від вашої оболонки.
Порада
Оскільки файл
.env— це звичайний shell-скрипт, ви можете виконати для ньогоsourceу власних shell-скриптах:$ source .env
Перевизначення значень середовища через .env.local
Якщо вам потрібно перевизначити значення середовища (наприклад, на інше значення на вашій локальній машині), ви можете зробити це у файлі .env.local:
# .env.local
DATABASE_URL="mysql://root:@127.0.0.1:3306/my_database_name"
Цей файл має ігноруватися git і не повинен комітитися до вашого репозиторію. Доступні ще кілька файлів .env для задання змінних середовища саме в потрібній ситуації:
.env: визначає значення за замовчуванням змінних середовища, потрібних застосунку;.env.local: перевизначає значення за замовчуванням для всіх середовищ, але лише на машині, яка містить цей файл. Цей файл не слід комітити до репозиторію, і він ігнорується в середовищіtest(бо тести мають давати однакові результати для всіх);.env.<environment>(наприклад,.env.test): перевизначає змінні середовища лише для одного середовища, але для всіх машин (ці файли комітяться);.env.<environment>.local(наприклад,.env.test.local): визначає специфічні для машини перевизначення змінних середовища лише для одного середовища. Це схоже на.env.local, але перевизначення застосовуються лише до одного середовища.
Справжні змінні середовища завжди мають пріоритет над змінними середовища, створеними будь-яким із файлів .env. Зауважте, що ця поведінка залежить від конфігурації variables_order, яка має містити E, щоб суперглобал $_ENV був доступним. Це конфігурація PHP за замовчуванням.
Файли .env і .env.<environment> слід комітити до репозиторію, бо вони однакові для всіх розробників і машин. Проти env-файли, що закінчуються на .local (.env.local і .env.<environment>.local), не слід комітити, бо ними користуватиметеся лише ви. Насправді файл .gitignore, який постачається із Symfony, запобігає їх комітенню.
Перевизначення змінних середовища, визначених системою
Якщо вам потрібно перевизначити змінну середовища, визначену системою, використовуйте параметр overrideExistingVars, визначений методами Symfony\Component\Dotenv\Dotenv::loadEnv, Symfony\Component\Dotenv\Dotenv::bootEnv і Symfony\Component\Dotenv\Dotenv::populate:
use Symfony\Component\Dotenv\Dotenv;
$dotenv = new Dotenv();
$dotenv->loadEnv(__DIR__.'/.env', overrideExistingVars: true);
// ...
Це перевизначить змінні середовища, визначені системою, але не перевизначить змінні середовища, визначені у файлах .env.
Конфігурування змінних середовища на продакшні
На продакшні файли .env також розбираються й завантажуються на кожному запиті. Тож найпростіший спосіб визначити змінні середовища — створити файл .env.local на вашому продакшн-сервері (серверах) з вашими продакшн-значеннями.
Щоб покращити продуктивність, ви можете за бажанням виконати Composer-команду dump-env:
# parses ALL .env files and dumps their final values to .env.local.php
$ composer dump-env prod
Виведення змінних середовища без Composer
Якщо у вас на продакшні не встановлено Composer, ви можете натомість використати команду
dotenv:dump(доступна в Symfony Flex 1.2 або пізніше). Команда не зареєстрована за замовчуванням, тож спочатку вам потрібно зареєструвати її у своїх сервісах:# config/services.yaml services: Symfony\Component\Dotenv\Command\DotenvDumpCommand: ~Потім виконайте команду:
# parses ALL .env files and dumps their final values to .env.local.php $ APP_ENV=prod APP_DEBUG=0 php bin/console dotenv:dump
Після виконання цієї команди Symfony завантажуватиме файл .env.local.php, щоб отримати змінні середовища, і не витрачатиме час на розбір файлів .env.
Порада
Оновіть свої інструменти/процес розгортання, щоб виконувати команду
dotenv:dumpпісля кожного деплою — це покращить продуктивність застосунку.
Зберігання змінних середовища в інших файлах
За замовчуванням змінні середовища зберігаються у файлі .env, розташованому в корені вашого проєкту. Проте ви можете зберігати їх в інших файлах кількома способами.
Якщо ви використовуєте компонент Runtime, шлях до dotenv є частиною опцій, які ви можете задати у своєму файлі composer.json:
{
// ...
"extra": {
// ...
"runtime": {
"dotenv_path": "my/custom/path/to/.env"
}
}
}
Як альтернативний варіант, ви можете безпосередньо викликати клас Dotenv у вашому файлі bootstrap.php або будь-якому іншому файлі вашого застосунку:
use Symfony\Component\Dotenv\Dotenv;
new Dotenv()->bootEnv(dirname(__DIR__).'my/custom/path/to/.env');
Тоді Symfony шукатиме змінні середовища в цьому файлі, а також у локальних і специфічних для середовища файлах (наприклад, .*.local і .*.<environment>.local). Прочитайте, як перевизначати змінні середовища, щоб дізнатися про це більше.
Якщо вам потрібно знати шлях до файлу .env, який використовує Symfony, ви можете прочитати змінну середовища SYMFONY_DOTENV_PATH у вашому застосунку.
Шифрування змінних середовища (secrets)
Замість визначення справжньої змінної середовища або додавання її до файлу .env, якщо значення змінної є чутливим (наприклад, API-ключ чи пароль до бази даних), ви можете зашифрувати це значення за допомогою системи керування secrets.
Перелік змінних середовища
Використовуйте команду debug:dotenv, щоб зрозуміти, як Symfony розбирає різні файли .env для встановлення значення кожної змінної середовища:
$ php bin/console debug:dotenv
Dotenv Variables & Files
========================
Scanned Files (in descending priority)
--------------------------------------
* ⨯ .env.local.php
* ⨯ .env.dev.local
* ✓ .env.dev
* ⨯ .env.local
* ✓ .env
Variables
---------
---------- ------- ---------- ------
Variable Value .env.dev .env
---------- ------- ---------- ------
FOO BAR n/a BAR
ALICE BOB BOB bob
---------- ------- ---------- ------
# look for a specific variable passing its full or partial name as an argument
$ php bin/console debug:dotenv foo
Крім того, незалежно від того, як ви задаєте змінні середовища, ви можете побачити всі змінні середовища з їхніми значеннями, на які є посилання в конфігурації контейнера Symfony:
$ php bin/console debug:container --env-vars
------------ ---------------- -----------------------------------
Name Default value Real value
------------ ---------------- -----------------------------------
APP_SECRET n/a "471a62e2d601a8952deb186e44186cb3"
BAR n/a n/a
BAZ n/a "value"
FOO "[1, "2.5", 3]" n/a
------------ ---------------- -----------------------------------
# you can also filter the list of env vars by name:
$ php bin/console debug:container --env-vars foo
# run this command to show all the details for a specific env var:
$ php bin/console debug:container --env-var=FOO
Створення власної логіки завантаження змінних середовища
Ви можете реалізувати власну логіку завантаження змінних середовища, якщо поведінка Symfony за замовчуванням не відповідає вашим потребам. Для цього створіть сервіс, клас якого реалізує Symfony\Component\DependencyInjection\EnvVarLoaderInterface.
Зверніть увагу
Якщо ви використовуєте конфігурацію
services.yamlза замовчуванням, можливість автоконфігурації увімкне й позначить цей сервіс автоматично. Інакше вам потрібно зареєструвати свій сервіс і позначити його тегомcontainer.env_var_loader.
Припустімо, у вас є JSON-файл з іменем env.json, який містить ваші змінні середовища:
{
"vars": {
"APP_ENV": "prod",
"APP_DEBUG": false
}
}
Ви можете визначити клас на кшталт наведеного нижче JsonEnvVarLoader, щоб заповнити змінні середовища з файлу:
namespace App\DependencyInjection;
use Symfony\Component\DependencyInjection\EnvVarLoaderInterface;
final class JsonEnvVarLoader implements EnvVarLoaderInterface
{
private const ENV_VARS_FILE = 'env.json';
public function loadEnvVars(): array
{
$fileName = __DIR__.\DIRECTORY_SEPARATOR.self::ENV_VARS_FILE;
if (!is_file($fileName)) {
// викиньте виняток або просто проігноруйте цей завантажувач, залежно від ваших потреб
}
$content = json_decode(file_get_contents($fileName), true);
return $content['vars'];
}
}
Ось і все! Тепер застосунок шукатиме файл env.json у поточній теці, щоб заповнити змінні середовища (додатково до вже наявних файлів .env).
Порада
Якщо ви хочете, щоб змінна середовища мала значення в одному середовищі, але в іншому середовищі поверталася до завантажувачів, призначте змінній середовища порожнє значення для того середовища, де ви хочете використовувати завантажувачі:
# .env (or .env.local) APP_ENV=prod # .env.prod (or .env.prod.local) - this will fallback on the loaders you defined APP_ENV=
Змінні середовища в конфігурації бандлів
Коли ви використовуєте %env(...)% у конфігурації бандла (наприклад, config/packages/doctrine.yaml), значення не читається під час компіляції. Натомість Symfony замінює його унікальним плейсхолдером. Фактична змінна середовища обчислюється лише під час виконання, коли створюється сервіс, який її використовує.
Автори бандлів мають дотримуватися певних правил, щоб їхній бандл коректно підтримував змінні середовища під час виконання:
У класі Configuration (TreeBuilder):
- Не пишіть кроки
beforeNormalization(), які перевіряють або перетворюють значення опцій конфігурації (кроки, які лише реорганізують ключі, не читаючи значень, — це нормально); - Не пишіть кроки
validate(), які перевіряють значення опцій конфігурації (під час компіляції значення все ще є рядком-плейсхолдером, а не справжнім значенням).
У DI-розширенні (метод load()):
- Не пишіть логіку, яка перевіряє оброблені значення опцій конфігурації перед їх впровадженням у DI-параметри чи аргументи визначень сервісів. Під час компіляції ці значення є рядками-плейсхолдерами, а не фактичними значеннями змінних середовища.
Порада
Загальне правило таке: підключайте значення до контейнера, не перевіряйте його. Передавання значень змінних середовища до аргументів сервісів чи параметрів без їх інтерпретації достатньо, щоб обчислення під час виконання працювало автоматично.
Обробка DSN і значень, які потребують розбору
Якщо сервісу потрібна розібрана версія DSN (наприклад, витягнути хост, порт та облікові дані з URL бази даних), не розбирайте його в DI-розширенні під час компіляції контейнера. Натомість створіть сервіс-фабрику, який розбирає DSN під час виконання:
// src/Factory/ClientFactory.php
namespace App\Factory;
class ClientFactory
{
public static function create(string $dsn): SomeClient
{
$params = parse_url($dsn);
return new SomeClient(
host: $params['host'],
port: $params['port'] ?? 5432,
username: $params['user'] ?? '',
password: $params['pass'] ?? '',
);
}
}
Потім зареєструйте фабрику в конфігурації сервісів:
# config/services.yaml
services:
App\SomeClient:
factory: ['App\Factory\ClientFactory', 'create']
arguments:
- '%env(DATABASE_DSN)%'
// config/services.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use App\Factory\ClientFactory;
use App\SomeClient;
return App::config([
'services' => [
SomeClient::class => service()
->factory([ClientFactory::class, 'create'])
->args([env('DATABASE_DSN')]),
],
]);
Цей підхід використовує DoctrineBundle зі своєю ConnectionFactory, яка розбирає URL бази даних під час виконання, а не під час компіляції контейнера.
Доступ до параметрів конфігурації
Контролери й сервіси можуть отримувати доступ до всіх параметрів конфігурації. Це включає як параметри, визначені вами, так і параметри, створені пакетами/бандлами. Виконайте наведену нижче команду, щоб побачити всі параметри, які існують у вашому застосунку:
$ php bin/console debug:container --parameters
У контролерах, що успадковують AbstractController, використовуйте помічник getParameter():
// src/Controller/UserController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
class UserController extends AbstractController
{
// ...
public function index(): Response
{
$projectDir = $this->getParameter('kernel.project_dir');
$adminEmail = $this->getParameter('app.admin_email');
// ...
}
}
У сервісах і контролерах, які не успадковують AbstractController, впроваджуйте параметри як аргументи їхніх конструкторів:
// src/Service/MessageGenerator.php
namespace App\Service;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
class MessageGenerator
{
public function __construct(
#[Autowire(param: 'app.contents_dir')]
private string $contentsDir,
) {
}
// ...
}
# config/services.yaml
parameters:
app.contents_dir: '...'
services:
App\Service\MessageGenerator:
arguments:
$contentsDir: '%app.contents_dir%'
// config/services.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use App\Service\MessageGenerator;
return App::config([
'parameters' => [
'app.contents_dir' => '...',
],
'services' => [
MessageGenerator::class => [
'arguments' => [
'$contentsDir' => param('app.contents_dir'),
],
],
],
]);
Рекомендований спосіб — використовувати атрибут #[Autowire].
Якщо ви впроваджуєте одні й ті самі параметри знову і знову, використовуйте натомість опцію services._defaults.bind. Аргументи, визначені в цій опції, впроваджуються автоматично щоразу, коли конструктор сервісу або дія контролера визначає аргумент з таким самим іменем. Наприклад, щоб впроваджувати значення параметра kernel.project_dir щоразу, коли сервіс/контролер визначає аргумент $projectDir, використовуйте це:
# config/services.yaml
services:
_defaults:
bind:
# передавати це значення до будь-якого аргументу $projectDir для будь-якого сервісу,
# який створюється в цьому файлі (включно з аргументами контролерів)
$projectDir: '%kernel.project_dir%'
# ...
// config/services.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
return App::config([
'services' => [
'_defaults' => [
'bind' => [
// передавати це значення до будь-якого аргументу $projectDir для будь-якого сервісу,
// який створюється в цьому файлі (включно з аргументами контролерів)
'$projectDir' => param('kernel.project_dir'),
],
],
],
]);
Див. також
Прочитайте статтю про привʼязку аргументів за іменем та/або типом, щоб дізнатися більше про цю потужну можливість.
Нарешті, якщо якомусь сервісу потрібен доступ до багатьох параметрів, замість впровадження кожного з них окремо ви можете впровадити всі параметри застосунку одразу, вказавши для будь-якого з аргументів його конструктора тип Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface:
// src/Service/MessageGenerator.php
namespace App\Service;
// ...
use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface;
class MessageGenerator
{
public function __construct(
private ContainerBagInterface $params,
) {
}
public function someMethod(): void
{
// отримати будь-який параметр контейнера з $this->params, який зберігає їх усі
$sender = $this->params->get('mailer_sender');
// ...
}
}
Рухаймося далі!
Вітаємо! Ви опанували основи Symfony. Далі вивчайте кожну частину Symfony окремо, слідуючи посібникам. Перегляньте:
- Forms
- Doctrine
- Service Container
- Security
- Mailer
- Logging
А також усі інші теми, пов'язані з конфігурацією:
configuration/*
Виправити терміни, дописати розділ або взяти нову главу може кожен. Термінологію узгоджуємо в глосарії, щоб переклад лишався однорідним.