DotEnv Component
DotEnv загружает переменные окружения из .env файлов. Symfony автоматически использует его в dev и test окружениях.
Порядок загрузки .env файлов
1. .env # Base values, committed to VCS
2. .env.local # Local overrides, NOT committed
3. .env.{env} # Environment-specific (e.g., .env.test)
4. .env.{env}.local # Local env-specific, NOT committed
Каждый следующий файл переопределяет значения из предыдущего. Реальные переменные окружения имеют наивысший приоритет.
# .env
APP_ENV=dev
APP_SECRET=change_me
DATABASE_URL="postgresql://app:[email protected]:5432/app?serverVersion=18"
MAILER_DSN=smtp://localhost:1025
# .env.local (not committed to VCS)
DATABASE_URL="postgresql://real_user:[email protected]:5432/myapp"
APP_SECRET=a_real_secret_value
# .env.test
KERNEL_CLASS='App\Kernel'
DATABASE_URL="postgresql://app:[email protected]:5432/app_test"
MAILER_DSN=null://null
Подвох экзамена: В production
.envфайлы НЕ используются. Переменные окружения устанавливаются через Docker, systemd или конфигурацию хостинга. Symfony проверяет наличие.env.local.php(скомпилированные переменные) или использует системные переменные.
Синтаксис .env
# Simple values
APP_DEBUG=true
APP_NAME=MyApp
# Quoted values (for spaces and special chars)
APP_TITLE="My Application"
# Referencing other variables
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_URL="postgresql://user:pass@${DATABASE_HOST}:${DATABASE_PORT}/db"
# Default values
REDIS_URL="${REDIS_URL:-redis://localhost:6379}"
# Multiline (use quotes)
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA...
-----END RSA PRIVATE KEY-----"
ExpressionLanguage Component
ExpressionLanguage позволяет компилировать и выполнять выражения. Используется в Security, Routing, Workflow и DI.
<?php
declare(strict_types=1);
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
$expressionLanguage = new ExpressionLanguage();
// Evaluate expression
$result = $expressionLanguage->evaluate(
'product.price * quantity * (1 - discount / 100)',
[
'product' => (object)['price' => 100],
'quantity' => 3,
'discount' => 10,
]
);
// $result = 270.0
// Compile to PHP
$phpCode = $expressionLanguage->compile(
'user.isActive() and user.age >= 18',
['user']
);
// $phpCode = "$user->isActive() && $user->getAge() >= 18"
ExpressionLanguage в конфигурации
# config/services.yaml
services:
App\Service\ApiClient:
arguments:
$timeout: "@=parameter('app.default_timeout') * 2"
$baseUrl: "@=service('router').generate('api_base')"
<?php
declare(strict_types=1);
use Symfony\Component\DependencyInjection\Attribute\Autowire;
final class ApiClient
{
public function __construct(
#[Autowire(expression: 'parameter("app.default_timeout") * 2')]
private readonly int $timeout,
) {
}
}
Файлы конфигурации
YAML конфигурация
# config/packages/framework.yaml
framework:
secret: '%env(APP_SECRET)%'
csrf_protection: true
http_method_override: false
handle_all_throwables: true
session:
handler_id: null
cookie_secure: auto
cookie_samesite: lax
storage_factory_id: session.storage.factory.native
php_errors:
log: true
PHP конфигурация
<?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->csrfProtection()->enabled(true);
$framework->httpMethodOverride(false);
$framework->session()
->handlerId(null)
->cookieSecure('auto')
->cookieSamesite('lax');
};
Symfony 8.0: Конфигурация через XML и аннотации удалена из экзамена. Только YAML и PHP-атрибуты.
Environment-specific конфигурация
# config/packages/framework.yaml
# Base config for all environments
framework:
secret: '%env(APP_SECRET)%'
# Override for dev only
when@dev:
framework:
profiler:
collect: true
# Override for test
when@test:
framework:
test: true
session:
storage_factory_id: session.storage.factory.mock_file
# Override for prod
when@prod:
framework:
router:
strict_requirements: null
Параметры
# config/services.yaml
parameters:
# Scalar parameters
app.admin_email: '[email protected]'
app.items_per_page: 20
# Array parameter
app.supported_locales: ['en', 'fr', 'de']
# Environment variable processors
app.feature_flags: '%env(json:FEATURE_FLAGS)%'
app.debug_emails: '%env(csv:DEBUG_EMAILS)%'
app.cache_ttl: '%env(int:CACHE_TTL)%'
app.use_ssl: '%env(bool:USE_SSL)%'
# Nested reference
app.upload_dir: '%kernel.project_dir%/var/uploads'
# Constant
app.max_connections: !php/const App\Config::MAX_CONNECTIONS
Доступ к параметрам
<?php
declare(strict_types=1);
namespace App\Controller;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
final class ConfigController extends AbstractController
{
public function __construct(
#[Autowire('%app.admin_email%')]
private readonly string $adminEmail,
#[Autowire('%app.items_per_page%')]
private readonly int $itemsPerPage,
// Full parameter bag
private readonly ParameterBagInterface $params,
) {
}
public function index(): Response
{
// Via parameter bag
$locales = $this->params->get('app.supported_locales');
$projectDir = $this->params->get('kernel.project_dir');
// Via AbstractController
$secret = $this->getParameter('kernel.secret');
return new Response("Admin: {$this->adminEmail}");
}
}
Configuration Tree (Bundle Configuration)
<?php
declare(strict_types=1);
namespace App\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
final class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder(): TreeBuilder
{
$treeBuilder = new TreeBuilder('app');
$treeBuilder->getRootNode()
->children()
->scalarNode('api_key')->isRequired()->end()
->integerNode('timeout')
->defaultValue(30)
->min(1)->max(300)
->end()
->enumNode('mode')
->values(['sync', 'async', 'batch'])
->defaultValue('async')
->end()
->booleanNode('debug')->defaultFalse()->end()
->arrayNode('providers')
->arrayPrototype()
->children()
->scalarNode('name')->isRequired()->end()
->scalarNode('dsn')->isRequired()->end()
->integerNode('priority')->defaultValue(0)->end()
->end()
->end()
->end()
->end();
return $treeBuilder;
}
}
Отладка конфигурации
# Show current config for a bundle
php bin/console debug:config framework
# Show full reference config
php bin/console config:dump-reference framework
# Show all parameters
php bin/console debug:container --parameters
Итоги
- DotEnv загружает переменные из
.envфайлов (не в production) - Порядок:
.env->.env.local->.env.{env}->.env.{env}.local - ExpressionLanguage -- выражения в конфигурации и Security
when@dev,when@test,when@prod-- конфигурация по окружению- Параметры доступны через
#[Autowire('%param%')]илиParameterBagInterface debug:config,config:dump-reference-- отладка конфигурации