Микрофреймворк Symfony

Symfony всегда можно было использовать в качестве микрофреймворка. Среди других микрофреймворков, которые позволяют вам добавлять в них все, что пожелаете, можно выделить Empty Edition и MicroFrameworkBundle.

Существует множество критериев, за счет которых фреймворк классификацируется как «микрофреймворк». Вот некоторые из них:

  • небольшой API (использование кода фреймворка в вашем приложении)
  • мало строк кода (LOC)
  • мало зависимостей (количество используемых сторонних библиотек)
  • небольшой объем (время загрузки фреймворка)

Можно ли считать Symfony микрофреймворком? Давайте разберемся.

Примечание: О том, как определить, является ли фреймворк микрофреймворком, почитайте статью Игоря Видлера (Igor Wiedler) Насколько тяжел Silex?

Измерение

Несмотря на то, что примеры типа «Hello World» редко отражают приложения, используемые в реальной жизни, они подойдут для цели этой статьи, а именно: измерить API, строки кода, зависимости и объем Symfony.

Поскольку зависимости и объем измерить легко, мы будем полагаться на эти параметры. Но все эталонные тесты определяются компьютером, на котором они проводятся, поэтому нам понадобится ориентир: простое PHP-приложение Hello World:

<?php
// index.php
echo 'Hello World';

Давайте проведем эталонный тест:

php -S localhost:2501 &
ab -c 10 -t 10 'http://localhost:2501/index.php'
killall php

Результат: 6 915.03 запросов в секунду.

Стандартная версия (Standard Edition)

Чтобы получить Стандартную версию, мы можем использовать composer:

composer create-project symfony/framework-standard-edition
cd framework-standard-edition

Поскольку стандартная версия основывается на концепции «применяй готовые решения для 80% вариантов использования», она практически готова, нам нужно лишь немного настроить наш контроллер:

<?php
// src/AppBundle/Controller/DefaultController.php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class DefaultController extends Controller
{
    /**
     * @Route("/", name="homepage")
     */
    public function indexAction(Request $request)
    {
        return new Response('Hello World');
    }
}

Проведем эталонный тест:

SYMFONY_ENV=prod composer update -o --no-dev
php -S localhost:2502 -t web &
ab -c 10 -t 10 'http://localhost:2502/app.php'
killall php

Результат: 134.23 запросов в секунду. Также мы перечислим зависимости:

tree -d -L 2 vendor/ | grep '   ' | wc -l
tree -d -L 2 vendor/ | grep '    ' | wc -l

Получаем 28 + 1, в которой мы вместо symfony должны указать все пакеты, которые она заменяет (44): 72. Подытожим:

  • API: добавление нового маршрута в 1 шаг
  • объем: в 52 раза медленнее, чем простое PHP-приложение
  • размер: 72 зависимости

Пустая версия (Empty Edition)

Как было сказано выше, Cтандартная версия основывается на концепции «применяй готовые решения для 80% вариантов использования», поэтому она содержит много зависимостей, а это нам не подходит. Микрофреймворк обычно основывается на концепции «добавь все, что тебе нужно», и пустая версия как раз соответствует этой концепции.

Давайте посмотрим, сможем ли мы приблизить фреймворк к статусу «микро»:

composer create-project gnugat/symfony-empty-edition
cd symfony-empty-edition

Сначала создаем контроллер:

<?php
// src/AppBundle/Controller/HelloController.php
namespace AppBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class HelloController
{
    public function world(Request $request)
    {
        return new Response('Hello World');
    }
}

Затем регистрируем его в качестве службы:

# app/config/services/controller.yml
services:
    app.hello_controller:
        class: AppBundle\Controller\HelloController

И наконец, регистрируем маршрут:

# app/config/routings/app.yml
hello_world:
    path: /
    defaults:
        _controller: app.hello_controller:world
    methods:
        - GET

Проведем эталонный тест:

composer update -o --no-dev
php -S localhost:2503 -t web &
ab -c 10 -t 10 'http://localhost:2503/app.php'
killall php

Результат: 524.53 запросов в секунду. Перечислим зависимости:

tree -d -L 2 vendor/ | grep '   ' | wc -l
tree -d -L 2 vendor/ | grep '    ' | wc -l

Получаем 6 + 23 = 29.

В итоге:

  • API: добавление нового маршрута в 3 шага
  • объем: в 13 раз медленнее, чем простое PHP-приложение
  • размер: 29 зависимости

Micro Framework Bundle

Уменьшив число зависимостей, мы значительно сократили объем фреймворка. Это неудивительно, поскольку:

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

Можем сделать больше? Конечно: FrameworkBundle также основывается на концепции «применяй готовые решения для 80% вариантов использования» (включает Формы, Безопасность, Разработку шаблонов, Перевод, Ресурсы, аннотации и т.д).

Используя MicroFrameworkBundle, который предлагает строго ограниченный минимум и основывается на концепции «добавляй все, что тебе нужно», мы можем еще больше сократить число зависимостей. Итак, gnugat/micro-framework-bundle:

composer require 'gnugat/micro-framework-bundle'
composer remove 'symfony/framework-bundle'

Затем нам нужно заменить зарегистрировать бандл:

<?php
// app/AppKernel.php
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;
class AppKernel extends Kernel
{
    public function registerBundles()
    {
        return array(
            new Gnugat\MicroFrameworkBundle\GnugatMicroFrameworkBundle(),
            new AppBundle\AppBundle(),
        );
    }
    public function getRootDir()
    {
        return __DIR__;
    }
    public function getCacheDir()
    {
        return dirname(__DIR__).'/var/cache/'.$this->environment;
    }
    public function getLogDir()
    {
        return dirname(__DIR__).'/var/logs';
    }
    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader->load($this->rootDir.'/config/config_'.$this->environment.'.yml');
    }
}

Наконец, мы можем избавиться от части конфигурации:

# app/config/config.yml
imports:
    - { resource: parameters.yml }
    - { resource: services/ }

Проведем эталонный тест для нашего урезанного приложения:

rm -rf var/*
composer update -o --no-dev
php -S localhost:2504 -t web &
ab -c 10 -t 10 'http://localhost:2504/app.php'
killall php

Результат: 872.83 запроса в секунду. Перечислим зависимости:

tree -d -L 2 vendor/ | grep '   ' | wc -l
tree -d -L 2 vendor/ | grep '    ' | wc -l

Получаем 3 + 13 = 16.

В итоге:

  • API: добавление нового маршрута в 3 шага
  • объем: в 8 раз медленне, чем простое PHP-приложение
  • размер: 13 зависимостей.

Заключение

Symfony всегда можно было использовать в качестве бандла микрофреймворка. Основываясь на концепции «применяй готовые решения для 80% вариантов использования», Стандартная версия Standard Edition и FrameworkBundle предпочтительнее для начинающих.

А подходящей альтернативой для опытных разработчиков, которые ищут концепцию «добавляй все, что тебе нужно» (именно на этой концепции основывается большинство микрофреймворков), может стать использование Пустой версии (Empty Edition) и MicroFrameworkBundle (в них меньше зависимостей и они работают быстрее).