Начало работы

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

Каталога config/ в Winter нет. Значения живут в .env, состав приложения — атрибутами на классе-точке входа, а всё остальное описывается обычными классами, которые находит сканер. Регистрировать их нигде не нужно.

Значения .envСостав атрибуты #[Enable*]Поведение классы-конфигураторы

Три уровня

Настройка разнесена по трём местам, и деление между ними простое.

Где Что там Пример
.env Значения, меняющиеся от окружения к окружению адрес базы, уровень логов
Класс приложения Из чего состоит приложение #[EnableWeb], #[EnableScheduler]
Классы-конфигураторы Поведение, выраженное кодом CORS, параметры сервера, бины

Почему нет конфиг-файлов. То, что является значением, живёт в окружении и меняется без пересборки. То, что является решением — какую реализацию подставить, как собрать объект, какие источники разрешить, — выражается кодом, где работают типы и подсказки редактора. Промежуточного слоя из массивов в PHP-файлах здесь нет намеренно.

.env — значения

Файл создаётся из шаблона командой php call cfg init и содержит три строки:

.env
WINTER_KEY=<сгенерированный ключ>
TIME_ZONE=UTC
DEBUG=true
Переменная По умолчанию Что задаёт
WINTER_KEY Секрет проекта: подписи, токены. Генерируется на месте
TIME_ZONE UTC Часовой пояс по умолчанию
DEBUG false Режим разработки

Что именно меняет `DEBUG`

Это не только «показывать ошибки». Переменная переключает четыре вещи:

  • отображение ошибок — при false PHP их прячет, и упавший скрипт молчит;
  • тело ответа при ошибке — стектрейс и отладочные данные уходят клиенту только при true;
  • кеш списка классов — при false проект сканируется один раз и результат ложится в di.php; при true кеш не используется вовсе;
  • прокси #[Async] — при true перегенерируются, если исходник изменился.

В проде — только false. При true наружу утекают внутренности приложения.

Логирование

Переменная По умолчанию Что задаёт
LOG_LEVEL не задан — вывод выключен Минимальный уровень записи
LOG_OUTPUT auto auto, stdout, stderr, file, syslog, null
LOG_FORMAT line line или json
LOG_FILE storage/log/<канал>.log Путь при LOG_OUTPUT=file
LOG_FILE_MAX 30 Сколько файлов хранить при ротации
LOG_COLOR auto auto, always, never

Любую из них можно задать отдельно для канала — префиксом LOG_{КАНАЛ}_:

.env
LOG_LEVEL=info
LOG_HTTP_LEVEL=warning     # от запросов — только проблемы
LOG_SYS_OUTPUT=file        # системные события в файл

Подробнее — на странице Логирование.

Веб-сервер

Действуют, только когда приложение объявляет #[EnableWeb].

Переменная Что задаёт
SERVER_PROFILE Профиль памяти: stable, balance, performance, stress
SERVER_WORKERS Число процессов-обработчиков
SERVER_TASKS Число процессов под фоновые задачи Swoole
SERVER_MAX_CONCURRENCY Одновременных запросов на воркер
SERVER_MAX_CONNECTIONS Одновременных соединений
SERVER_MAX_REQUEST Запросов до замены воркера
SERVER_MAX_REQUEST_GRACE Разброс, чтобы воркеры не перезапускались разом
SERVER_MAX_REQUEST_SIZE Наибольший размер запроса в байтах
SERVER_REQUEST_TIMEOUT Дедлайн запроса в секундах
SERVER_IDLE_TIMEOUT Через сколько закрыть молчащее соединение
SERVER_MEMORY_LIMIT Лимит памяти каждого воркера
SERVER_MEMORY_TRIM Когда возвращать неиспользуемую память системе

Что означает каждая и как они связаны между собой — на странице Настройка веб-слоя. Адрес и порт переменными не задаются: они приходят флагами --host и --port.

Прочее

Переменная Что задаёт
WINTER_BANNER off убирает баннер при запуске
PPA_POOL_TELEMETRY Интервал публикации статистики пула соединений; 0 выключает

Переменные базы данных объявляете вы

Ядро не читает DB_HOST и подобные — за подключение отвечает ваш класс конфигурации, и какие переменные он спросит, решаете вы. Принято брать их через env(), см. раздел База данных.

Манифест приложения

Из чего состоит приложение — объявляется атрибутами на классе-точке входа:

bootstrap.php
#[EnableWeb]
#[EnableScheduler]
#[EnableProcess(\Main\Process\QueueWorker::class)]
final class Application extends WinterApplication
{
  public static function main(array $argv): never
  {
      parent::run($argv);
  }
}

Все доступные атрибуты и их сочетания — на странице Состав приложения.

Единственный хук — configure()

Он нужен, только если раскладка проекта нестандартная: этот метод выполняется до скана и решает, где сканеру искать.

bootstrap.php
use Flytachi\Winter\Kernel\App\ApplicationArguments;
use Flytachi\Winter\Kernel\Kernel;

protected static function configure(ApplicationArguments $args): void
{
  Kernel::init(
      pathRoot:     __DIR__,
      pathResource: __DIR__ . '/assets',
      pathStorage:  '/var/lib/my-app',
  );
}

Без переопределения пути выводятся из расположения класса приложения, и для обычной раскладки трогать его не нужно.

Классы-конфигураторы

Всё остальное описывается обычными классами. Их не нужно нигде регистрировать — сканер находит их сам, по реализованному интерфейсу или по атрибуту.

Что настроить Чем
Привязки контейнера, фабрики, значения из .env #[Configuration] с методами #[Bean]
CORS и параметры веб-сервера Класс, наследующий WebConfigurerAdapter
Дополнительные каналы логов Класс, реализующий LoggingConfigurer
Подключение плагинов Атрибут #[Import] на классе приложения
Диагностические эндпоинты Атрибут #[EnableActuator]
main/AppConfig.php
#[Configuration]
final class AppConfig
{
  #[Bean]
  public function cache(#[Value('REDIS_URL')] string $url): CacheInterface
  {
      return new RedisCache($url);
  }
}

Каждый из них разобран на своей странице: Внедрение зависимостей, Настройка веб-слоя, Логирование, Пакеты.

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

Один и тот же для всех точек входа — и для сервера, и для консольной команды:

text
1. configure()      — пути, .env, часовой пояс, логирование
2. Контейнер        — создаётся пустым
3. Один обход проекта:
    · классы с областью видимости      → контейнер
    · #[Configuration] / #[Bean]       → фабрики
    · WebConfigurer, LoggingConfigurer → собираются
    · граф областей видимости          → проверка
4. Проверка конфликтов #[Singleton] ↔ #[Request]
5. Применение найденного: логи, CORS, плагины, актуатор
6. Дальше — либо сервер, либо консольная команда

Обход проекта один, а не по разу на каждый вид конфигурации. Отсюда два следствия:

  • порядок объявления классов-конфигураторов не важен — они собираются за один проход и применяются после него;
  • новый конфигуратор подхватывается перезапуском, как и всё остальное.

Конфликт областей ловится здесь

Шаг 4 обходит граф зависимостей и отказывается поднимать приложение, если #[Singleton] держит внутри #[Request]-объект: иначе данные первого запроса замёрзли бы в нём на всё время жизни воркера. В логе будет цепочка A → $prop: B.

Обслуживание ключа

bash
php call cfg key -g     # сгенерировать новый
php call cfg key -s     # показать текущий
php call cfg env -s     # показать все переменные окружения

Дальше