Symfony 4: структура приложения

Данный пост написан на основе публикации Фабьена Потенсье.

В свое время в 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/ где ему и место.

config/ для конфигураций


Каталог config/ заменит существовавший ранее app/config. Параметры окружения, которые ранее определялись в файле parameters.yml, теперь конфигурируются с помощью переменных окружения операционной системы. По умолчанию в каталоге config/ вы обнаружите пустой файл container.yaml (yaml — это не опечатка, в Symfony 4 конфигурационные файлы в формате YAML, теперь имеют расширение yaml, вместо yml)в котором как и прежде можете определять конфигурацию контейнера. Так же там будет файл routing.yaml. В данных файлах вы будете определять только собственные настройки. Компоненты, установленные с помощью Composer, будут хранить свои настройки в отдельных файлах для каждого окружения.

Конфигурацию как и прежде можно будет задавать в трех форматах: YAML, XML, PHP. Помимо конфигурационных файлов, в каталоге config/ появится 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/


Так же изменилось содержимое каталога public/. Убраны файлы 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.
Поделиться публикацией
Похожие публикации
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 62
    • 0
      Я бы не сказал что это перевод. Я прочитал данный пост Фабьена, и на его основе написал этот материал.
      • +2

        И, если бы вы поставили в статье ссылку на пост — вас не в чем было бы упрекнуть :)

        • +1
          Спасибо, исправился.
    • 0
      GitBash хорошо, но можно же Ubuntu for Windows в Win10 )
      • 0
        Да, конечно. Я описал инструменты которые мне использовать удобнее. Дело вкуса.
      • 0
        Уже перевёл основные проекты на Symfony 4 like index.php и %env()%. Заметил, что чистый index.php плохо работает с APP_ENV=prod и, главное, с env параметрами в route — они разрешаются в compile-time (прогрева кеша), а не в рантайме. Пришлось переносить прогрев кэша в рантайм докер-контейнера из билд. Хотелось бы пофиксить, может есть идеи?
        • 0
          Мне кажется, что рановато вы решили переводить проекты под Symfony 4 style. С APP_ENV=prod на скелетном Hello world с включенными аннотациями для роутинга проблем не было. Можно по подробнее что означает index.php плохо работает с APP_ENV=prod?
          • 0
            рановато вы решили

            symfony 3.3 вышел, а значит не рано. У меня схожие подходы уже больше года используются на проектах и я доволен.

            • +1

              Да по сути перевод давно начался, с релизом 3.2 примерно, если не раньше. parameters.yml очень неудобен для автоматического развертывания, а разные точки входа для прод и дев окружения очень неудобны при тестировании клиентов сервиса с симфони слабо связанными: задать через переменные окружения всё — идея, лежащая на поверхности.


              У меня для для конструкций типа Route ("host": "sub.{domain}", "defaults": {"domain": "%env(DOMAIN)%"}, "requirements": {"domain": "%env(DOMAIN)%"}}) всё разрешалось в момент прогрева кэша, а не в рантайме.


              Не эквивалентны по поведению неуказанная среда и APP_ENV=prod.

            • 0
              они разрешаются в compile-time (прогрева кеша), а не в рантайме.

              как раз таки Cache Warmup это не совсем compile time, а скорее рантайм. То есть на момент компиляции роутов контейнер зависимостей уже скомпилен.


              Хотелось бы пофиксить, может есть идеи?

              У меня не вышло. Как минимум потому что часть того что прогревается все же требует рантайма. Есть как бы варианты… Например — отказаться от Dockerfile, то есть собрать контейнер, прогреть кэши и уже потом сделать docker commit и уже этот образ дистрибьютить. Но хз.

              • 0
                С Докером есть некоторые не решенные моменты. Можно почитать дискуссию на эту тему тут.
                • 0

                  Если под нерешенными моментами имеется ввиду желание "а давайте flex будет патчить мои compose файлики" — то нет, это мне не нужно. Да и выглядит это странно, или flex будет pecl пакеты сам ставить тоже?


                  Других моментов с docker нет.

                • +1
                  как раз таки Cache Warmup это не совсем compile time, а скорее рантайм. То есть на момент компиляции роутов контейнер зависимостей уже скомпилен.

                  Всё равно это компиляция, а не обработка запроса. Ну, если кэш в проде прогревать до начала поступления запросов.


                  Например — отказаться от Dockerfile, то есть собрать контейнер, прогреть кэши и уже потом сделать docker commit и уже этот образ дистрибьютить. Но хз.

                  Не имеет значения. Зафиксируются значения на момент прогрева, что нарушит концепцию "строим образ и деплоим в разных окружениях". Работает только прогрев кеша на момент запуска контейнера из образа в ENTRYPOINT и(или) CMD.

              • 0
                Как в том же fos user bundle я переопределю шаблоны, если мой бандл унаследован, а папка для всех общая. Только переписыванием контроллеров?
                • 0
                  Если вы наследуетесь от бандла, то ничего переписывать не нужно. Вы по прежнему можете иметь бандлы в src/, просто теперь это не обязательно. Не забудьте прописать свой бандл в etc/bundles.php. Если же вы определяете только шаблоны в app/resources, то возможно стоит подумать о переносе в src/.
                • 0
                  С .env файлами не все так просто ввиду того, что ENV переменные это всегда строки. Часто параметры должны быть четко типизированы, например, тот же параметр disable_delivery (swift mailer) ожидает строго тип bool, и передать в него ENV переменную напрямую не удается. Поэтому полный переход на .env файлы весьма затруднителен.
                  • 0
                    Согласен. Но на то оно и альфа, чтобы получить фидбэк. Дискуссии по этому вопросу еще ведутся. Вполне возможно, что мы увидим какой-то гибридный механизм или останемся в файлах в плане настроек окружения.
                    • 0
                      Поэтому полный переход на .env файлы весьма затруднителен.

                      посмотрите на это с другой стороны — как насчет минимизировать различия между окружением? Что бы у вас параметры вроде disable_delivery просто лежали себе в yaml файлике для определенного окружения и все, а подменялось только то что реально должно подменяться. Какой энвайрмент. креденшелы, токены и т.д.

                      • 0
                        На эту тему есть дискуссия. Кстати никто не запрещает вам как и ранее использовать некоторые специфичные параметры в parameters.yaml. Если ваш проект не может обслуживаться исключительно переменными окружения, пожалуйста, используйте конфигурационные файлы. Я думаю будет еще много разговоров на эту тему. Меня тоже в некоторых моментах смущает использование переменных окружения.
                        • 0
                          Да, тоже находил подобную дискуссию, где Фабьен сказал, что .env файл полностью не заменяют параметры.
                          А я всего лишь хотел уйти от поддержки parameters.yml.dist -> parameters.yml и остаться только на .env.dist -> .env файле. Но пока приходится поддерживать оба механизма конфигурации.
                          • 0
                            некоторые специфичные параметры в parameters.yam

                            он не нужен. Я предлагаю все различия окружения выносить в конфигурацию именно окружения (config_prod.yml к примеру). А parameters.yml — он не нужен.


                            К этому приходят почти все кто держит свое приложение в каком-либо контейнере.

                            • 0
                              Согласен. Переменные окружения по сути будут задавать только два параметра APP_ENV и APP_DEBUG,
                              все остальное можно разнести.
                              • 0
                                только два параметра

                                ну у меня еще есть всякие DB_HOST, DB_PASS, REDIS_HOST, ELASTICSEARCH_URL` и прочие штуки.

                                • 0
                                  Вот по поводу DB_PASS у меня почему-то легкий холодок пробегает по спине когда я думаю о том чтобы такие данные прописывать в окружении. Пока не могу определить для себя позицию по этому вопросу.
                                  • 0

                                    если это докеры и иже с ним, то это ок, т.к приложение одно и оно и так имеет доступ к этим параметрам. если на площадке несколько сервисов, тогда боязно, да

                                    • 0

                                      но эти несколько сервисов могут быть в своих окружениях и не иметь к параметрам друг друга доступа.

                                      • 0
                                        А могут и иметь, мы же рассмативаем не идеальный сферический мир, не так ли? Конечно можно распихать все приложения в индивидуальные контейнеры, я с этого и начал свой тезис.
                              • 0

                                А что делать, если у нас три десятка разработчиков с разными параметрами (персональные окружения)?

                                • 0
                                  У каждого разработчика свое окружение и настройки. При разработке используется компонент symfony/dotenv для эмуляции переменных окружения из файла .env.
                                  Реальные переменные окружения предполагается использовать на stage, prod и т.п. серверах.
                                  • 0

                                    выше уже написали, что дотенв — не панацея

                                  • +1
                                    три десятка разработчиков с разными параметрами

                                    Избавиться от "персонального окружения"? Если у вас 30 человек и у каждого свое окружение, которое отличается от продакшена — у меня возникают большие вопросы.

                                    • 0
                                      Вы меня опередили. Как раз хотел тоже самое сказать. На самом деле нет ничего плохого если у каждого разработчика значения параметров окружения отличаются, для того оно собственно и предназначено. Но если у разработчиков одного проекта разные параметры окружения, то вам не помогут и любые другие решения. В переменных окружения нормально задать порт мемкеша, если у кого-то он отличается от стандартного, но совсем не правильно задавать параметры от которых зависит приложение и которых нет в продакшене или у других разработчиков. По-моему это более чем очевидно.
                                      • 0

                                        у нас есть, например, дебаг тулбар, который управляется через параметры, т.к не всем разработчикам (фронтам, например) он нужен. да и вообще окружение по умолчанию отличается тем, что в разработке используется dev режим по умолчанию, на контурах дальше — прод. но мне казалось, что это логичная разница с продом, иначе зачем вообще тогда dev режим

                                        • 0
                                          т.к не всем разработчикам (фронтам, например) он нужен.

                                          export SYMFONY_ENV=stage


                                          например. У меня к примеру фронтэндеры работают на своих стэйджинг серверах (не хотят они себе докер). Деплоят туда что им нам надо, и норм. Я там отключенным держу дебаг панель, но профайлер доступен на всякий случай.

                                          • 0
                                            Я правильно понимаю, что на каждую группу разработки (бэки, фронты, итд) у вас есть отдельный env в symfony? Сколько у вас их всего, если не секрет? Мы просто используем классический dev,test,prod набор, а кастомизируем именно параметрами.
                                            • 0
                                              у вас есть отдельный env в symfony?

                                              Ну давайте думать какие окружения нам нужны:


                                              • prod — продакшен.
                                              • stage — сервера которые используют QA, фронтэнды, мобильщики, в том числе для e2e тестов. Они же используются для demo внутренних к примеру (а тут уже дизайнеры, аналитики и все желающие). Тут отличие от продакшена только в том что мы врубаем профайлер и частенько используем всякие мэйлкэтчеры. Но это как раз таки через env разруливается на уровне настроек SMTP сервера. А так различия с продакшеном минимальны.
                                              • dev — локальная разработка, много всего накручено поверх stage. И для более удобной отладки, и в целом в силу ограничений инфраструктуры и работы с third-party сервисами.
                                              • test — очень похоже на dev но ориентируется на приемочные тесты.

                                              Как-то так. По итогу с таким подходом можно намного более уверенно говорить фразы вроде "локально работает".


                                              На парочке проектов есть ситуации когда в конфиг заносятся например фича тоглы, которые не привязаны к окружению, это скорее просто часть конфигурации.

                                              • 0
                                                test — очень похоже на dev но ориентируется на приемочные тесты.


                                                А отдельного env для юнитов нет? С моками и пр.

                                                stage — сервера которые используют QA, фронтэнды, мобильщики, в том числе для e2e тестов.


                                                Про фронтов — я правильно понял, что у вас бэк — это чистое API и фронты пишут отдельное приложение, поэтому им не нужны всякие тормозящие фичи типа автоматических перегенераций кэша, статики приложения и пр? Если так, то ваша позиция понятна, у вас немного другой кейс.

                                                У нас более «классическое» приложение с рендером на стороне твига, и фронты технически пишут то же самое приложение что и бэки, поэтому в нашем случае вот такой выделенный stage env не нужен (точней может и нужен, но не в таком варианте). QA, мобильщики и пр. используют просто prod вариант развернутый на внутренних контурах
                                                • 0
                                                  А отдельного env для юнитов нет? С моками и пр.

                                                  А с каких пор юнит тесты хоть как-то привязаны к окружению? Они на то и юнит что тестируют юниты изолировано от любой инфраструктуры. И как раз таки то что проходит границу модуля и предоставляет доступ к инфраструктуре мы и мокаем.


                                                  То есть нам не нужен ни контейнер, ни кернел. А вот интеграционные/приемочные тесты — тут уже нужно.


                                                  у вас бэк — это чистое API

                                                  именно так.

                                                  • 0
                                                    На одном проекте мы юнит тесты в продакшене пускаем после деплоя. Никаких проблем с окружением не испытываем.
                                                    • 0

                                                      было бы еще неплохо если бы юнит тесты запускались ДО деплоя.

                                                      • 0
                                                        Ну конечно :) Было бы глупо пускать тесты только на проде при развороте. Я имел ввиду что на проде тоже пускаются после деплоя.
                                                    • 0

                                                      вот в этом месте я для себя пока не решил для себя. после того, как вы замокали инфраструктуру — ваши тесты не стали изолированными?


                                                      я пишу тесты так, чтобы мокать, но именно эти моки и определены в тестовом енве. эти же тесты без проблем запускаются и в dev, но они попытаются сходить в реальные сервисы, а не в замоканные

                                                      • 0
                                                        после того, как вы замокали инфраструктуру — ваши тесты не стали изолированными?

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


                                                        и определены в тестовом енве.

                                                        если вы контейнер зависимостей юзаете — то это не юнит тесты. Это интеграционные тесты. А интеграционные тесты — это обман (то это не значит что они не нужны).

                                                        • 0
                                                          А на каком из ваших env вы производите интеграционное тестирование?
                                                          • 0

                                                            test, я ж написал выше.

                                                            • 0
                                                              В списке вы упомянули только приемочные, но при этом делите эти тесты, так что я решил уточнить на всякий случай. Спасибо за объяснение.

                                                              test — очень похоже на dev но ориентируется на приемочные тесты.

                                                              А вот интеграционные/приемочные тесты — ...
                                        • 0

                                          любое окружение, которое не продакшен отличается от него по определению. не все параметры, как уже сказано выше, можно переопределить через dotenv, так что parameters.yml остается необходимым инструментом в конфигурировании площадки.


                                          понятно что структура самих конфигов совпадает, вопрос именно в безаппеляционом тезисе "parameters.yml — он не нужен"

                                          • +1
                                            остается необходимым инструментом

                                            я бы сказал не "необходимым" а опциональным. Сам по себе parameters.yml это не отдельный механизм, это часть механизма импортов конфигов который никто не собирается выпиливать.


                                            он не нужен

                                            по умолчанию — не нужен. Но абсолютных правил нет и есть исключения.

                                            • 0
                                              «необходимым» в некоторых кейсах, давайте так. Т.е. есть такие случаи, когда без него не обойтись, в таких случаях его сложно назвать «опциональным»

                                              по умолчанию — не нужен.


                                              Вот с такой формулировкой я соглашусь, можно без него.
                              • 0
                                Есть и критика новой структуры директорий в Symfony 4: http://paul-m-jones.com/archives/6564
                                Инициатива по стандартизации структуры PHP пакетов: https://github.com/php-pds/skeleton
                                • 0
                                  Да, критика вполне справедлива я считаю. По поводу конфига в /config, очень поддерживаю как в прочем и другие доводы.
                                  • 0

                                    навязывать папку src — это имхо что выкидывать psr-4. плохо ложится на концепцию монорепозиториев, которые потом делят на пакеты через subtree. при том, что у нас есть такая крутая вещь, как композер — описывать структуры файлов можно и в нем (где конфиги лежат, где сурсы итд)

                                    • 0
                                      Тут получается неоднозначность. С одной стороны мы можем изменить в composer.json каталог с сорцами на любой, который нам нужен. С другой, все рецепты основываются на предложенной структуре и будут копировать необходимые файлы в строго заданные места. Таким образом при установке каких-то бандлов мы все равно получим что-то в /src
                                      • 0
                                        Стоп, стоп. Фиксированная (каноничная, если угодно) структура приложения — это нормально, приложение не подразумевает быть встроенным в другие пакеты. Предложенный же скелетон хочет, чтобы данная структура была у любых пакетов, что на мой взгляд, весьма неудобно.

                                        Я согласен, что предложенная флексом структура весьма нелогичная, но она навязана рецепту, который будет встраиваться в ваше прилложение, но структура вашего пакета остается полностью произвольной. Т.е. рецепт — это в некотором смысле адаптер между пакетом и флексом
                                        • 0
                                          Как раз таки рецепт — это адаптер между пакетом и вашим приложением, а не флексом. Флекс лишь инструмент который выполняет рецепты. Если вы пишете рецепт сами, то можете написать его конкретно под вашу структуру. Большинство же рецептов написанные core-team Symfony будут следовать структуре о которой написано в статье. Поэтому либо выбирать предложенную структуру, либо писать рецепты под все пакеты, которые собираетесь использовать и отказываться от официального репозитория рецептов.

                                          P.S. Тема очень глубокая с множеством точек зрения. Я согласен с вашими мыслями и сам пока еще не определился с какой я стороны.
                                          • 0
                                            ну да, под флексом я именно понимал приложение, использующее флекс в качестве инструмента конфигурации.

                                            я лично буду просто ждать релиза, т.к. не имею пока проблем с конфигурацией зависимостей в приложении, мы все таки не веб студия, штампующая проекты десятками
                                        • 0

                                          Как вариант, в composer.json можно указывать переменные, переопределяющие стандартные пути, а в рецептах использовать плэйсхолдеры в путях "%app_src% и т. п.

                                      • 0

                                        Как я понимаю, основная проблема в web и etc вместо public и config? Вообще, посмотрев на начальные данные, сложилось впечатление, что Symfony очень сильно влияет на тренды именования и потому посое релиза в текущем виде тренды могут измениться.
                                        А я бы вообще выбрал etc и www

                                        • 0
                                        • +1

                                          Надо обновить пост :)

                                          • 0
                                            Да, сегодня поправлю.

                                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.