Symfony

icon picker
Запуск symfony-приложения

Описание

Что я понимаю под запуском symfony-проекта

Под запуском я буду понимать не только создание ядра, а полноценный запуск всего проекта начиная с самой первой строки исходного кода(безусловно после инициализации autoloader). Так как запуска symfony-приложения это не только ядро, но и разделение на две точки входа, обработка настроек и деббагера, а также само ядро

Разные точки входа (front controller)

Есть несколько точек входа для нашего приложения. Например, веб, консоль и тест. Для каждой точки входа создается свой файл, который содержит в себе код для реализации паттерна Front Controller. Этот паттерн позволяет все пользовательские запросы url или cli направлять через одну функцию, которая уже сама запускает нужный обработчик для конкретного запроса

Схема запуска

Парсинг и настройка переменных среды в зависимости от окружения. Для дева парсим переменные из одного источника, для прода совершено другое место.
В зависимости от среды подключаем дебаггер. Именно до создания ядра. Подробнее ниже.
Создание самого ядра, обработка запроса, его отправка обратно пользователю и terminate ядра.

Разносим код запуска на файлы (bootstrap.php)

Для запуска приложения в основном создают три файла. Два из которых точки входа(front controller), а третий содержит инициализацию окружения
index.php - сюда будет обращаться web сервер, поэтому здесь будет обрабатывать HTTP сообщения для симфони.
console.php - скрипт, который будет запускаться в консоли, поэтому здесь будем обрабатывать CLI для симфони.
bootstrap.php - общий файл для инициализации окружения, где подключается autoloader и будет содержаться парсинг основной конфигурации для будущего приложения(какая среда окружения, нужен ли деббаг и прочее). Сам дебаггер мы здесь не включаем, это уже ответственность front controller

Создание ядра

Для начала проверяем отладчик

Что такое отладка в рамках symfony

В symfony основной код отладки находится в компоненте errorHandler в классе ErrorHandler. Для разных окружений используются некие дополнительные классы или настройки, которые по итогу запускают тот же ErrorHandler, поэтому нельзя запускать отладчик в общем файле, для каждого окружения свои настройки
В основном отладчик используется для кастомного вывода перехваченных ошибок, исключений и трассировки стека вызовов
ErrorHandler будет запускаться всегда в ядре как минимум для минимальной обработки ошибок PHP, чтобы даже на проде была возможность их отловить и вывести в минимальном красивом виде или спрятать от глаз юзеров за красивой плашкой

Отладчик в web

Для web используется объект Debug с методом enable, который запускает ErrorHandler с специфичными настройками для web.
Ядро само не запускает отладчик, так как это является нарушением ответственности, а также появляется ситуация, когда ошибка во время инициализации ядра не будет обработана без заранее включенного отладчика.
Вывод ошибок происходит в виде html страницы

Отладчик в cli

В консольном приложении отладчик самостоятельно формируется в объекте консоли. Поэтому вызывать отдельно ничего не нужно, только передать в ядро флаг debug
Вывод ошибок формируется в текстовом выводе

Отладчик в test

В тестах Symfony автоматически активирует ErrorHandler, либо через bootstrap (если настроен), либо через интеграцию с PHPUnit. Все ошибки PHP (Notice, Warning, Deprecated) превращаются в исключения (ErrorException), чтобы тесты могли "падать красиво".

Зачем нужен флаг debug в ядре?

В ядро передается флаг debug, но это не означает, что симфони сможет самостоятельно включить дебагер. Как было ранее сказано, то симфони ответственность за отладку выносит за рамки ядра. В ядре флаг используется для:
Включения профайлера(если он подключен в настройках) WebProfilerBundle
Отключение кеширования. Конфигурация контейнера будет при каждом запросе заново проверяться. Изменения в конфигурациях, сервисах и аннотациях маршрутов, шаблоны Twig сразу же отображаются
Более подробные логи некоторых компонентов. Например, Doctrine логирует каждый sql запрос
Появляются консольные дебаг-команды. debug:container, debug:router.

Создание ядра для web

Создаем объект Kernel, где можем указать наш environment(режим работы) DEV или PROD. Переменные среды мы должны были заранее спарсить
Далее нам нужно создать Request из HttpFoundation, где указываем, что создаем его из глобальных переменных PHP для HTTP.
Этот Запрос передаем в обработку ядра и получаем Response
Response нужно отправить юзеру через метод send
и обязательно завершаем работу ядра через terminate. Интересно, что ядро само не завершит работу. Это завершение кидает свой event, на который подписан как миниму профайлер, который записывает в себя все данные о работе именно после завершения работы ядра
$app = new Kernel($_SERVER['APP_ENV'], true);
$request = Request::createFromGlobals();
$response = $app->handle($request);
$response->send();
$app->terminate($request, $response);

Создание ядра для cli

В консольных командах Symfony нет объектов Request и Response в привычном HTTP-смысле. Вместо этого используется абстракция Input/Output через компоненты Console (InputInterface, OutputInterface), что соответствует паттерну CommandBus.
Консольный объект Application использует HttpKernel внутри себя, но не вызывает функцию handle, где требуются HTTP Request и Response. Это сделано намеренно, чтобы упросить архитектуру фреймворка. Плюс исторические причины, ведь изначально фреймворк планировался чисто как HTTP MVC.
$console = new Application(new Kernel($_SERVER['APP_ENV'], true));
$console->run();

Создание ядра для test

Тесты запускаются с помощью класса WebTestCase из компонента frameworkBundle, который расширяет фреймворк PHPUnit. Этот класс самостоятельно создает ядро. Для некоторых unit тестов нам вообще не нужно создавать ядро фреймворка
WebTestCase::createClient() //внутри создается ядро

Парсинг переменных окружения ENV

Подробнее про переменные окружения можно почитать здесь

Парсинг через DotEnv и файлы .env

Получать переменные можно через файлы .env, который удобно парсить через библиотеку Dotenv. В библиотеке есть два основных метода.

Метод bootEnv

bootEnv является оберткой над loadEnv, которая определяет текущую среду окружения, чтобы выбрать способ парсинга. В случае если найдется продевский файл с массивом .env.local.php, то bootEnv загрузит только его и не запустит метод loadEnv

Метод loadEnv

loadEnv основной метод, который парсит файлы .env с текущим окружением

Парсинг напрямую через массив

Для ускорения парсинг переменных можно создать файл, который будет возвращать готовый PHP массив. Это полезно для прод режима, где переменные не меняются и не нужно тратить время на дополнительный парсинг, например, через Dotenv

Реализация парсинга с определением типа окружения

Обычно вызывается метод bootEnv в библиотеке Dotenv, который уже сам определит парсить .env.local.php или файлы .env. В моей реализации сначала идет самостоятельная проверка на наличие .env.local.php, чтобы не тратить ресурсы на библиотеку Dotenv, что полезно на проде. Если же файла нет, то в коде запускается Dotenv сразу метод loadEnv
require_once dirname(__DIR__).'/vendor/autoload.php';

if (is_array($env = @include dirname(__DIR__) . '/.env.local.php')) {
//Для прода упрощаем парсинг переменных окружения через подключения массив в .env.local.php,
// чтобы не тратить время на один и тот же парсинг env файлов. Плюс симфони сама собирает этот файл

//Используем оператор +=, так как он не будет перезаписывать существующие значения.
// То есть будет возможность гибко поменять переменные, например, через контейнер, не редактируя файл
$_SERVER += $env;
$_ENV += $env;
} else {
//Для дева мы не используем putenv и будем работать только с глобальным массивом,
// так как не нужно лишний раз грузить ОС системными вызовами.
// putenv нужен, когда необходимо взаимодействовать с процессамми, программами и прочим
// за рамками PHP скрипта. $_SERVER же доступен только в рамках PHP
// В Dotenv можно использовать метод usePutenv(), чтобы включить системные вызовы putenv

// Метод bootEnv проверяет наличие файла .env.local.php и если его не нашел, то вызывает loadEnv.
// Но так как выше уже стоит проверка на продевский конфиг, то bootEnv бесполезен и проще сразу вызвать loadEnv.
// Короче говоря, когда мы в ручную обрабатываем продевский конфиг, bootEnv бесполезен.
(new Dotenv())->loadEnv(dirname(__DIR__) . '/.env');
}

//Если не заданы заранее переменные, то ставим по умолчанию APP_ENV и APP_DEBUG в дев режим
$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev';
$_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV'];
$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0';
Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.