Symfony2 deployment checklist

Symfony2 – гибкий и мощный фреймворк... что часто ведет к достаточно долгому времени выполнения. Конечно, production-окружение значительно быстрее, чем development, но этого недостаточно.

Чтобы ускорить приложение в production-окружении, крайне рекомендуется установить PHP-акселератор, например, APC.

На выделенном сервере

На Linux

Для установки APC на debian-подобных linux дистрибутивах достаточно выполнить:

apt-get install php-apc

Команду следует изменить в зависимости от дистрибутива.

На Windows

Раскомментируйте соответствующую строку в php.ini:

extension=php_apc.dll

Во всех случаях

После установки, расширение необходимо активировать. Для этого необходимо добавить строчку в конец php.ini:

[APC]
apc.enabled=1

На shared хостинге

На shared-хостинге доступ по SSH как правило закрыт. В этом случае, лучше всего связаться с администратором хостинга, сообщив, что для их серверов будет лучше поставить PHP-акселератор.

Убедитесь, что на production-сервере задан уникальный секретный ключ. Проверить параметр можно в файле app/config/parameters.yml:

secret:  please_use_a_real_secret_here

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

Перед тем как развертывать ваше приложение, вы должны проверить, что ваш production-сервер правильно настроен.

Есть три метода, чтобы проверить сервер:

  1. Выполнить php app/check.php в консоли и посмотреть все необходимое;
  2. Через браузер открыть config.php, находящийся в директории web;
  3. Если доступа на сервер пока нет (например, вы только планируете его купить), вы можете посмотреть требования на соответствующей странице в документации

Вам не нужно, чтобы логотип Symfony появлялся в браузере ваших посетителей. Поэтому favicon по умолчанию должен быть заменен на ваш собственный перед развертыванием.

Просто замените файл web/favicon.ico.

Чтобы сделать свой favicon, можно:

  • Использовать онлайн сервисы типа favicon.cc для генерации .ico файла;
  • Использовать PNG-иконку. В этом случае нужно указать link в HTML-коде страниц: <link rel="icon" type="image/png" href="yourFavIcon.png">

Компонент, отвечающий за генерацию логов — неотъемлемая часть вашего приложения. В Symfony2 эту задачу выполняет Monolog.

Настройки по-умолчанию подходят для development-окружения, но недостаточны для production-окружения. Необходимые изменения решают две задачи:

  • Отправка ошибок администратору сайта по электронной почте (логи с уровнем "error")
  • Запись всех аутентификаций, так как по-умолчанию они не логгируются в записях уровня "info"

Сконфигурировать Monolog можно в config_prod.yml:

monolog:
    handlers:
        main:
            type:               fingers_crossed
            action_level:       error
            handler:            grouped
        grouped:
            type:               group
            members:            [streamed, swift]
        streamed:
            type:               stream
            path:               "%kernel.logs_dir%/%kernel.environment%.log"
            level:              debug
        swift:
            type:               swift_mailer
            from_email:         FQN@foo.com
            to_email:           webmaster@company.com
            subject:            "OOps"
            level:              debug
        login:
            type:               stream
            path:               "%kernel.logs_dir%/auth.log"
            level:              info
            channels:           security

И всё готово!

В production-окружении крайне рекомендуется использовать кэш для Doctrine. Есть два типа кэширования.

Кэш запросов и метаданных (Query and Metadata Cache)

  • Query Cache отвечает за кэширование результата трансфорамции DQL запроса в соответствующий SQL. В production-окружении запросы вряд ли будут меняться, поэтому логично их закэшировать.
  • Metadata Cache отвечает за кэширование разобранных метаданных из нескольких источников, например: YAML, XML, аннотации и т.п.

Кэширование этой информации включается с помощью следующих параметров в файле config_prod.yml:

doctrine:
    orm:
        auto_mapping: true
        query_cache_driver:    apc
        metadata_cache_driver: apc

Кэш результатов

Результаты запросов могут быть закэшированы, чтобы не обращаться к базе снова и снова. Так как это более точная настройка, нельзя задать это параметром на все приложение. Можно только задать драйвер:

doctrine:
    orm:
        auto_mapping: true
        result_cache_driver: apc

Затем необходимо явно указывать, использовать кэш или нет во всех важных запросах. Это делается установкой имени и времени жизни для каждого запроса. Посмотреть подробнее можно в документации по кэшированию результатов Doctrine.

Убедиться, что Doctrine на самом деле использует APC-кэш

Задать APC в качестве драйвера кэширования для Doctrine – это отлично. Но проблема в том, что Dependency Injection контейнер генерируется через интерфейс командной строки, и кэш создается так же. И если в php.ini не указан параметр apc.enable_cli = 1, то DIC будет использовать FileCacheReader. Это не то поведение, которое хочется получить.

Чтобы проверить, что APC-кэш действительно используется, можно посмотреть в app/cache/prod/appProdProjectContainer.php. Он должен содержать следующее:

protected function getDoctrine_Orm_DefaultEntityManagerService()
{
    $a = $this->get('annotation_reader');
    $b = new \Doctrine\Common\Cache\ApcCache();
    $b->setNamespace('sf2orm_default_5cdc3404d84577b226d7772ca9818908');
    $c = new \Doctrine\Common\Cache\ApcCache();
    $c->setNamespace('sf2orm_default_5cdc3404d84577b226d7772ca9818908');

    // ...

    $g = new \Doctrine\ORM\Configuration();
    $g->setMetadataCacheImpl($b);
    $g->setQueryCacheImpl($c);

    // ...
}

Если вы не можете найти \Doctrine\Common\Cache\ApcCache, то это значит, что APC не используется.

По-умолчанию, выгрузка автозагрузчика классов, которую делает Composer, оптимизирована не полностью. Когда в проекте множество классов, работа автозагрузчика может занимать солидное время.

В production-окружении автозагрузчик должен быть быстрым. Composer может сделать оптимизированную выгрузку для автозагрузчика, сконвертировав PSR-0 пакеты в classmap, что улучшит производительность.

Для использования оптимизированной автозагрукзи нужно добавить опцию --optimize для команды Composer dump-autoload:

php composer.phar dump-autoload --optimize

Конечно, с этой опцией команда может выполняться немного дольше. Поэтому это не делается по-умолчанию.

Компонент Symfony2 Form автоматически добавляет и валидирует CSRF токены за вас.

Убедитесь, что секретный ключ, который используется для генерации токенов, кастомизирован в файле app/config/parameters.yml:

secret:  please_use_a_real_secret_here

Кроме того, токен можно кастомизировать на уровне каждой отдельной формы, что даже лучше. Это можно сделать, добавив опцию intention в форме:

namespace Acme\DemoBundle\Form;

use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class TaskType extends AbstractType
{
    // ...

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            // a unique key to help generate the secret token
            'intention'  => 'task_form',
        ));
    }

    // ...
}

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

Кастомизировать страницы ошибок действительно просто. Их шаблоны находятся в бандле TwigBundle, что позволяет их переопределить. Для этого нужно создать шаблоны типа Exception/errorXXX.html.twig, где XXX — это код ошибки. Рекомендуется кастомизировать по крайней мере шаблоны для 404 и 500 кодов ошибок.

Есть два способа использовать свои собственные шаблоны:

  1. Создать новый бандл, который наследуется от TwigBundle, и положить шаблоны в директорию Resources/views/Exception/errorXXX.html.twig. Это позволит переиспользовать их в разных проектах.
  2. Просто положить шаблоны в директорию app/Resources/TwigBundle/views/Exception/errorXXX.html.twig. Если шаблоны уникальны для текущего проекта, это позволит избежать создание нового бандла только для их переопределения.