В свое время в Symfony 3 появились каталоги bin/, src/, var/, что по-моему мнению очень удобно и понятно. Мы все привыкли работать с такими каталогами. В свою очередь, Symfony 4 движется в этом же направлении и предлагает обновленную структуру каталогов приложения.
tests/ для тестов
Все тесты теперь будут располагаться непосредственно в каталоге tests/. Ранее в каталоге tests/ всегда присутствовали каталоги с именами бандлов вашего приложения в которых находились тесты. В Symfony 4, приложения не будут (или не обязательно должны) реализовывать код в каком либо бандле. Это позволит определить собственное пространство имен тестов для автозагрузки:
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Tests\\": "tests/"
}
}
}
templates/ для шаблонов
При установке шаблонизатора Twig, теперь будет создаваться каталог templates/ в корне приложения, где и будут размещаться все шаблоны. Мне очень нравится эта идея, потому что каталог Resources, который содержал в себе до этого и публичные файлы (css, js и т.п.) и шаблоны, вносил некоторый дискомфорт. Возможно, что каталог будет называться tpl/, это еще пока окончательно не известно. В целом, перемещение шаблонов из каталога src/ делает приложение более структурированным. Теперь в src/ лежит только код. Класс Kernel, который ранее располагался в папке app/, так же перемещен в src/ где ему и место.
etc/ для конфигураций
Каталог etc/ заменит существовавший ранее app/config. Параметры окружения, которые ранее определялись в файле parameters.yml, теперь конфигурируются с помощью переменных окружения операционной системы. По умолчанию в каталоге etc/ вы обнаружите пустой файл container.yaml (yaml — это не опечатка, в Symfony 4 конфигурационные файлы в формате YAML, теперь имеют расширение yaml, вместо yml)в котором как и прежде можете определять конфигурацию контейнера. Так же там будет файл routing.yaml. В данных файлах вы будете определять только собственные настройки. Компоненты, установленные с помощью Composer, будут хранить свои настройки в отдельных файлах для каждого окружения.
Конфигурацию как и прежде можно будет задавать в трех форматах: YAML, XML, PHP. Помимо конфигурационных файлов, в каталоге etc/ появится bundles.php, который будет представлен массивом бандлов, активных в вашей системе.
//bundles.php
<?php
return [
'Symfony\Bundle\FrameworkBundle\FrameworkBundle' => ['all' => true],
'Symfony\Bundle\TwigBundle\TwigBundle' => ['all' => true],
];
Изменения в этот файл будут вноситься автоматически плагином Synfony Flex, при установке или удалении любого бандла в системе с помощью Composer.
var/cache/ только для кеша
Небольшие изменения коснулись каталога var/. Теперь в var/cache/ будет храниться только «вечный» кеш (скомпилированный контейнер и переводы, или кеш доктрины например). Таким образом все файлы в var/cache/ должны иметь свой warmup класс. Никаких временных файлов, только кеш, который не изменяется после деплоя проекта. Это позволит сделать var/cache/ каталог доступным только для чтения. При таком подходе вы сможете работать с read-only файловыми системами (например как на платформах Heroku или SensioCloud).
web/ только для public содержимого
Так же изменилось содержимое каталога web/. Убраны файлы config.php, .htaccess, favicon.ico, apple-touch-icon.png, robots.txt. Не каждый проект нуждается в этих файлах. Если вам потребуются шаблоны для этих файлов, то Symfony 4, предоставит вам возможность опционально получить их.
Изменился и фронт-контроллер. Теперь вместо привычных app.php и app_dev.php будет присутствовать один файл index.php для всех окружений:
if (!getenv('APP_ENV')) {
(new Dotenv())->load(__DIR__.'/../.env');
}
if (getenv('APP_DEBUG')) {
if (isset($_SERVER['HTTP_CLIENT_IP'])
|| isset($_SERVER['HTTP_X_FORWARDED_FOR'])
|| !(in_array(@$_SERVER['REMOTE_ADDR'], ['127.0.0.1', '::1']) || PHP_SAPI === 'cli-server')
) {
header('HTTP/1.0 403 Forbidden');
exit('You are not allowed to access this file.');
}
Debug::enable();
}
// Request::setTrustedProxies(['0.0.0.0/0'], Request::HEADER_FORWARDED);
$kernel = new Kernel(getenv('APP_ENV'), getenv('APP_DEBUG'));
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
Используем переменные окружения для конфигурации
Настройки, специфичные для конкретной платформы и окружения, ранее принято было хранить в конфигурационном файле app/config/parameters.yml. Теперь такие параметры следует задавать с помощью переменных окружения. Это дает некоторые преимущества перед использованием parameters.yml. Например вы можете динамически менять данные параметры без необходимости чистить кеш. Переменные окружения можно использовать совместно в нескольких приложениях и языках программирования. Наконец вы можете администрировать данные параметры с помощью сторонних приложений.
На боевом сервере такой подход выглядит весьма привлекательно. Но в момент разработки приложения, использование переменных окружения может доставить много неудобств. Symfony 3.3 уже поставляется с компонентом Dotenv, который призван решить проблему. В корне вашего приложения будет располагаться файл .env, в котором можно определить переменные окружения. Компонент Dotenv, позволяет «переключаться» между использованием .env файла и реальными переменными окружения.
Как насчет Makefile?
Во многих проектах существуют сценарии, для которых использование скриптов, написанных на PHP, не совсем целесообразно(например такие вещи как рестарт веб-сервера или перезагрузка конфигурации php-fpm после очередного деплоя).
Для этих целей предлагается использовать Makefile. Утилита make знакома всем. Ее использование лучше, чем выполнение скриптов с помощью Composer (если вы прописали их в composer.json). Так же утилита make выполняется централизованно на вашей системе и не зависит от PHP. Давайте рассмотрим реальный пример, в Symfony есть консольные команды для очистки и разогрева кеша. Использование двух команд в одном процессе работает не так как нужно, потому что PHP не умеет перезагружать классы если они изменились. Эту проблему можно легко решить с помощью вот такого Makefile:
cache-clear:
@test -f bin/console && bin/console cache:clear --no-warmup || rm -rf var/cache/*
.PHONY: cache-clear
cache-warmup: cache-clear
@test -f bin/console && bin/console cache:warmup || echo "cannot warmup the cache (needs symfony/console)"
.PHONY: cache-warmup
Именно такой файл вы получите в стандартной сборке Symfony 4 приложения.
Я столкнулся с небольшой проблемой тестируя установку на Windows машине. Если для nix систем утилита make является стандартной, то на Windows нужно немного постучать в бубен. Проблема не в том, чтобы найти реализацию make для Windows, а в том, что в сценариях присутствуют вызовы таких команд test, rm и т.п. Обсуждение данного вопроса было тут.
Для себя я определил два способа решения этой проблемы:
1. Использовать GitBash в качестве терминала. Если вы используете Git, то скорее всего GitBash присутствует в вашей Windows системе.
2. Сегодня я настраивал свою любимую IDE PhpStorm на использование Cygwin терминала по умолчанию. И мне это решение понравилось больше, чем использование GitBash.
На этом пока все. Впереди материал о первых впечатлениях и инструкция по сборке приложения с помощью плагина Symfony Flex.
Комментарии (59)
VolCh
23.06.2017 23:19Уже перевёл основные проекты на Symfony 4 like index.php и %env()%. Заметил, что чистый index.php плохо работает с APP_ENV=prod и, главное, с env параметрами в route — они разрешаются в compile-time (прогрева кеша), а не в рантайме. Пришлось переносить прогрев кэша в рантайм докер-контейнера из билд. Хотелось бы пофиксить, может есть идеи?
shude
23.06.2017 23:37Мне кажется, что рановато вы решили переводить проекты под Symfony 4 style. С APP_ENV=prod на скелетном Hello world с включенными аннотациями для роутинга проблем не было. Можно по подробнее что означает index.php плохо работает с APP_ENV=prod?
Fesor
24.06.2017 11:10рановато вы решили
symfony 3.3 вышел, а значит не рано. У меня схожие подходы уже больше года используются на проектах и я доволен.
VolCh
26.06.2017 14:42+1Да по сути перевод давно начался, с релизом 3.2 примерно, если не раньше. parameters.yml очень неудобен для автоматического развертывания, а разные точки входа для прод и дев окружения очень неудобны при тестировании клиентов сервиса с симфони слабо связанными: задать через переменные окружения всё — идея, лежащая на поверхности.
У меня для для конструкций типа Route ("host": "sub.{domain}", "defaults": {"domain": "%env(DOMAIN)%"}, "requirements": {"domain": "%env(DOMAIN)%"}}) всё разрешалось в момент прогрева кэша, а не в рантайме.
Не эквивалентны по поведению неуказанная среда и APP_ENV=prod.
Fesor
24.06.2017 11:09они разрешаются в compile-time (прогрева кеша), а не в рантайме.
как раз таки Cache Warmup это не совсем compile time, а скорее рантайм. То есть на момент компиляции роутов контейнер зависимостей уже скомпилен.
Хотелось бы пофиксить, может есть идеи?
У меня не вышло. Как минимум потому что часть того что прогревается все же требует рантайма. Есть как бы варианты… Например — отказаться от Dockerfile, то есть собрать контейнер, прогреть кэши и уже потом сделать docker commit и уже этот образ дистрибьютить. Но хз.
shude
24.06.2017 11:21С Докером есть некоторые не решенные моменты. Можно почитать дискуссию на эту тему тут.
Fesor
24.06.2017 12:00Если под нерешенными моментами имеется ввиду желание "а давайте flex будет патчить мои compose файлики" — то нет, это мне не нужно. Да и выглядит это странно, или flex будет pecl пакеты сам ставить тоже?
Других моментов с docker нет.
VolCh
26.06.2017 14:48+1как раз таки Cache Warmup это не совсем compile time, а скорее рантайм. То есть на момент компиляции роутов контейнер зависимостей уже скомпилен.
Всё равно это компиляция, а не обработка запроса. Ну, если кэш в проде прогревать до начала поступления запросов.
Например — отказаться от Dockerfile, то есть собрать контейнер, прогреть кэши и уже потом сделать docker commit и уже этот образ дистрибьютить. Но хз.
Не имеет значения. Зафиксируются значения на момент прогрева, что нарушит концепцию "строим образ и деплоим в разных окружениях". Работает только прогрев кеша на момент запуска контейнера из образа в ENTRYPOINT и(или) CMD.
kraso4niy
24.06.2017 11:07Как в том же fos user bundle я переопределю шаблоны, если мой бандл унаследован, а папка для всех общая. Только переписыванием контроллеров?
shude
24.06.2017 11:19Если вы наследуетесь от бандла, то ничего переписывать не нужно. Вы по прежнему можете иметь бандлы в src/, просто теперь это не обязательно. Не забудьте прописать свой бандл в etc/bundles.php. Если же вы определяете только шаблоны в app/resources, то возможно стоит подумать о переносе в src/.
Tekill
24.06.2017 16:07С .env файлами не все так просто ввиду того, что ENV переменные это всегда строки. Часто параметры должны быть четко типизированы, например, тот же параметр disable_delivery (swift mailer) ожидает строго тип bool, и передать в него ENV переменную напрямую не удается. Поэтому полный переход на .env файлы весьма затруднителен.
shude
24.06.2017 16:08Согласен. Но на то оно и альфа, чтобы получить фидбэк. Дискуссии по этому вопросу еще ведутся. Вполне возможно, что мы увидим какой-то гибридный механизм или останемся в файлах в плане настроек окружения.
Fesor
24.06.2017 22:07Поэтому полный переход на .env файлы весьма затруднителен.
посмотрите на это с другой стороны — как насчет минимизировать различия между окружением? Что бы у вас параметры вроде disable_delivery просто лежали себе в yaml файлике для определенного окружения и все, а подменялось только то что реально должно подменяться. Какой энвайрмент. креденшелы, токены и т.д.
shude
24.06.2017 22:38На эту тему есть дискуссия. Кстати никто не запрещает вам как и ранее использовать некоторые специфичные параметры в parameters.yaml. Если ваш проект не может обслуживаться исключительно переменными окружения, пожалуйста, используйте конфигурационные файлы. Я думаю будет еще много разговоров на эту тему. Меня тоже в некоторых моментах смущает использование переменных окружения.
Tekill
24.06.2017 23:22Да, тоже находил подобную дискуссию, где Фабьен сказал, что .env файл полностью не заменяют параметры.
А я всего лишь хотел уйти от поддержки parameters.yml.dist -> parameters.yml и остаться только на .env.dist -> .env файле. Но пока приходится поддерживать оба механизма конфигурации.
Fesor
25.06.2017 01:22некоторые специфичные параметры в parameters.yam
он не нужен. Я предлагаю все различия окружения выносить в конфигурацию именно окружения (config_prod.yml к примеру). А parameters.yml — он не нужен.
К этому приходят почти все кто держит свое приложение в каком-либо контейнере.
shude
25.06.2017 07:05Согласен. Переменные окружения по сути будут задавать только два параметра APP_ENV и APP_DEBUG,
все остальное можно разнести.Fesor
25.06.2017 11:44только два параметра
ну у меня еще есть всякие
DB_HOST
,DB_PASS
,REDIS_HOST
, ELASTICSEARCH_URL` и прочие штуки.shude
25.06.2017 15:48Вот по поводу DB_PASS у меня почему-то легкий холодок пробегает по спине когда я думаю о том чтобы такие данные прописывать в окружении. Пока не могу определить для себя позицию по этому вопросу.
pbatanov
25.06.2017 16:02если это докеры и иже с ним, то это ок, т.к приложение одно и оно и так имеет доступ к этим параметрам. если на площадке несколько сервисов, тогда боязно, да
Fesor
25.06.2017 16:28но эти несколько сервисов могут быть в своих окружениях и не иметь к параметрам друг друга доступа.
pbatanov
25.06.2017 18:00А могут и иметь, мы же рассмативаем не идеальный сферический мир, не так ли? Конечно можно распихать все приложения в индивидуальные контейнеры, я с этого и начал свой тезис.
pbatanov
25.06.2017 11:38А что делать, если у нас три десятка разработчиков с разными параметрами (персональные окружения)?
shude
25.06.2017 11:39У каждого разработчика свое окружение и настройки. При разработке используется компонент symfony/dotenv для эмуляции переменных окружения из файла .env.
Реальные переменные окружения предполагается использовать на stage, prod и т.п. серверах.
Fesor
25.06.2017 11:45+1три десятка разработчиков с разными параметрами
Избавиться от "персонального окружения"? Если у вас 30 человек и у каждого свое окружение, которое отличается от продакшена — у меня возникают большие вопросы.
shude
25.06.2017 13:47Вы меня опередили. Как раз хотел тоже самое сказать. На самом деле нет ничего плохого если у каждого разработчика значения параметров окружения отличаются, для того оно собственно и предназначено. Но если у разработчиков одного проекта разные параметры окружения, то вам не помогут и любые другие решения. В переменных окружения нормально задать порт мемкеша, если у кого-то он отличается от стандартного, но совсем не правильно задавать параметры от которых зависит приложение и которых нет в продакшене или у других разработчиков. По-моему это более чем очевидно.
pbatanov
25.06.2017 15:46у нас есть, например, дебаг тулбар, который управляется через параметры, т.к не всем разработчикам (фронтам, например) он нужен. да и вообще окружение по умолчанию отличается тем, что в разработке используется dev режим по умолчанию, на контурах дальше — прод. но мне казалось, что это логичная разница с продом, иначе зачем вообще тогда dev режим
Fesor
25.06.2017 16:30т.к не всем разработчикам (фронтам, например) он нужен.
export SYMFONY_ENV=stage
например. У меня к примеру фронтэндеры работают на своих стэйджинг серверах (не хотят они себе докер). Деплоят туда что им нам надо, и норм. Я там отключенным держу дебаг панель, но профайлер доступен на всякий случай.
pbatanov
25.06.2017 18:10Я правильно понимаю, что на каждую группу разработки (бэки, фронты, итд) у вас есть отдельный env в symfony? Сколько у вас их всего, если не секрет? Мы просто используем классический dev,test,prod набор, а кастомизируем именно параметрами.
Fesor
25.06.2017 21:05у вас есть отдельный env в symfony?
Ну давайте думать какие окружения нам нужны:
- prod — продакшен.
- stage — сервера которые используют QA, фронтэнды, мобильщики, в том числе для e2e тестов. Они же используются для demo внутренних к примеру (а тут уже дизайнеры, аналитики и все желающие). Тут отличие от продакшена только в том что мы врубаем профайлер и частенько используем всякие мэйлкэтчеры. Но это как раз таки через env разруливается на уровне настроек SMTP сервера. А так различия с продакшеном минимальны.
- dev — локальная разработка, много всего накручено поверх stage. И для более удобной отладки, и в целом в силу ограничений инфраструктуры и работы с third-party сервисами.
- test — очень похоже на dev но ориентируется на приемочные тесты.
Как-то так. По итогу с таким подходом можно намного более уверенно говорить фразы вроде "локально работает".
На парочке проектов есть ситуации когда в конфиг заносятся например фича тоглы, которые не привязаны к окружению, это скорее просто часть конфигурации.
pbatanov
25.06.2017 21:21test — очень похоже на dev но ориентируется на приемочные тесты.
А отдельного env для юнитов нет? С моками и пр.
stage — сервера которые используют QA, фронтэнды, мобильщики, в том числе для e2e тестов.
Про фронтов — я правильно понял, что у вас бэк — это чистое API и фронты пишут отдельное приложение, поэтому им не нужны всякие тормозящие фичи типа автоматических перегенераций кэша, статики приложения и пр? Если так, то ваша позиция понятна, у вас немного другой кейс.
У нас более «классическое» приложение с рендером на стороне твига, и фронты технически пишут то же самое приложение что и бэки, поэтому в нашем случае вот такой выделенный stage env не нужен (точней может и нужен, но не в таком варианте). QA, мобильщики и пр. используют просто prod вариант развернутый на внутренних контурахFesor
25.06.2017 23:52А отдельного env для юнитов нет? С моками и пр.
А с каких пор юнит тесты хоть как-то привязаны к окружению? Они на то и юнит что тестируют юниты изолировано от любой инфраструктуры. И как раз таки то что проходит границу модуля и предоставляет доступ к инфраструктуре мы и мокаем.
То есть нам не нужен ни контейнер, ни кернел. А вот интеграционные/приемочные тесты — тут уже нужно.
у вас бэк — это чистое API
именно так.
shude
26.06.2017 00:04На одном проекте мы юнит тесты в продакшене пускаем после деплоя. Никаких проблем с окружением не испытываем.
pbatanov
26.06.2017 07:15вот в этом месте я для себя пока не решил для себя. после того, как вы замокали инфраструктуру — ваши тесты не стали изолированными?
я пишу тесты так, чтобы мокать, но именно эти моки и определены в тестовом енве. эти же тесты без проблем запускаются и в dev, но они попытаются сходить в реальные сервисы, а не в замоканные
Fesor
26.06.2017 17:17после того, как вы замокали инфраструктуру — ваши тесты не стали изолированными?
ну типа да. Вы как бы тестировать юнит тестами должны контракты объектов, а контракты зависимостей вы подменяете на требуемое от них поведение. Соответственно если ваши зависимости стабильнее тестируемого кода, и контракты хорошо протещены, то все хорошо.
и определены в тестовом енве.
если вы контейнер зависимостей юзаете — то это не юнит тесты. Это интеграционные тесты. А интеграционные тесты — это обман (то это не значит что они не нужны).
pbatanov
26.06.2017 20:08А на каком из ваших env вы производите интеграционное тестирование?
Fesor
26.06.2017 22:00test
, я ж написал выше.pbatanov
26.06.2017 22:34В списке вы упомянули только приемочные, но при этом делите эти тесты, так что я решил уточнить на всякий случай. Спасибо за объяснение.
test — очень похоже на dev но ориентируется на приемочные тесты.
А вот интеграционные/приемочные тесты — ...
pbatanov
25.06.2017 15:42любое окружение, которое не продакшен отличается от него по определению. не все параметры, как уже сказано выше, можно переопределить через dotenv, так что parameters.yml остается необходимым инструментом в конфигурировании площадки.
понятно что структура самих конфигов совпадает, вопрос именно в безаппеляционом тезисе "parameters.yml — он не нужен"
Fesor
25.06.2017 16:31+1остается необходимым инструментом
я бы сказал не "необходимым" а опциональным. Сам по себе parameters.yml это не отдельный механизм, это часть механизма импортов конфигов который никто не собирается выпиливать.
он не нужен
по умолчанию — не нужен. Но абсолютных правил нет и есть исключения.
pbatanov
25.06.2017 18:04«необходимым» в некоторых кейсах, давайте так. Т.е. есть такие случаи, когда без него не обойтись, в таких случаях его сложно назвать «опциональным»
по умолчанию — не нужен.
Вот с такой формулировкой я соглашусь, можно без него.
PQR
26.06.2017 17:00Есть и критика новой структуры директорий в Symfony 4: http://paul-m-jones.com/archives/6564
Инициатива по стандартизации структуры PHP пакетов: https://github.com/php-pds/skeletonshude
26.06.2017 17:10Да, критика вполне справедлива я считаю. По поводу конфига в /config, очень поддерживаю как в прочем и другие доводы.
pbatanov
26.06.2017 18:28навязывать папку src — это имхо что выкидывать psr-4. плохо ложится на концепцию монорепозиториев, которые потом делят на пакеты через subtree. при том, что у нас есть такая крутая вещь, как композер — описывать структуры файлов можно и в нем (где конфиги лежат, где сурсы итд)
shude
26.06.2017 18:35Тут получается неоднозначность. С одной стороны мы можем изменить в composer.json каталог с сорцами на любой, который нам нужен. С другой, все рецепты основываются на предложенной структуре и будут копировать необходимые файлы в строго заданные места. Таким образом при установке каких-то бандлов мы все равно получим что-то в /src
pbatanov
26.06.2017 20:06Стоп, стоп. Фиксированная (каноничная, если угодно) структура приложения — это нормально, приложение не подразумевает быть встроенным в другие пакеты. Предложенный же скелетон хочет, чтобы данная структура была у любых пакетов, что на мой взгляд, весьма неудобно.
Я согласен, что предложенная флексом структура весьма нелогичная, но она навязана рецепту, который будет встраиваться в ваше прилложение, но структура вашего пакета остается полностью произвольной. Т.е. рецепт — это в некотором смысле адаптер между пакетом и флексомshude
26.06.2017 20:17Как раз таки рецепт — это адаптер между пакетом и вашим приложением, а не флексом. Флекс лишь инструмент который выполняет рецепты. Если вы пишете рецепт сами, то можете написать его конкретно под вашу структуру. Большинство же рецептов написанные core-team Symfony будут следовать структуре о которой написано в статье. Поэтому либо выбирать предложенную структуру, либо писать рецепты под все пакеты, которые собираетесь использовать и отказываться от официального репозитория рецептов.
P.S. Тема очень глубокая с множеством точек зрения. Я согласен с вашими мыслями и сам пока еще не определился с какой я стороны.pbatanov
26.06.2017 22:32ну да, под флексом я именно понимал приложение, использующее флекс в качестве инструмента конфигурации.
я лично буду просто ждать релиза, т.к. не имею пока проблем с конфигурацией зависимостей в приложении, мы все таки не веб студия, штампующая проекты десятками
VolCh
28.06.2017 09:20Как вариант, в composer.json можно указывать переменные, переопределяющие стандартные пути, а в рецептах использовать плэйсхолдеры в путях "%app_src% и т. п.
VolCh
28.06.2017 09:25Как я понимаю, основная проблема в web и etc вместо public и config? Вообще, посмотрев на начальные данные, сложилось впечатление, что Symfony очень сильно влияет на тренды именования и потому посое релиза в текущем виде тренды могут измениться.
А я бы вообще выбрал etc и www
Caravus
Это перевод http://fabien.potencier.org/symfony4-directory-structure.html?
shude
Я бы не сказал что это перевод. Я прочитал данный пост Фабьена, и на его основе написал этот материал.
poxvuibr
И, если бы вы поставили в статье ссылку на пост — вас не в чем было бы упрекнуть :)
shude
Спасибо, исправился.