Loïc Faugeron Technical Blog

Symfony Micro Framework 23/12/2015

TD;DR: Symfony has always been able to be used as a micro framework. For more "add what you need" micro-framework like spirit, use the Empty Edition and the MicroFrameworkBundle.

There are many definitions out there to qualify a framework as being "micro", among them the following criterias often appear:

Is Symfony a micro framework as well? Let's find out.

Note: To know more about how to determine if a framework is micro, read Igor Wiedler article: How heavy is Silex?.

Measuring

While "Hello World" examples rarely reflect real world applications, it's going to be good enough to serve the purpose of this article: getting a good measure of Symfony's API, LOC, dependencies and footprint.

Since dependencies and footprint are easy to measure, we're going to rely on it. However, all benchmarks are relative to the computer that executes them, so we need a point of reference: a flat PHP "Hello World" application:

<?php
// index.php

echo 'Hello World';

Let's run the benchmark:

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

Result: 6 915.03 Requests per second.

Standard Edition

To get the Standard Edition, we can use composer:

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

Since the standard edition follows a "solve 80% of use cases out of the box" philosohpy, it's almost ready, we just need to tweak the given controller:

<?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');
    }
}

Let's run the benchmark:

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

Result: 134.23 Requests per second.

We're also going to list the dependencies:

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

We get 28 + 1, to which we need to substitute symfony with all the packages it replaces (44): 72.

So to sum up:

Empty Edition

As stated above the Standard Edition has a "solve 80% of use cases out of the box" philosophy, so it comes with many dependencies that might not fit our use. Micro framework usually follow a "add what you need philosophy", which is exactly what the Empty Edition is all about.

Let's see if we can get more micro with it:

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

The first step is to create a controller:

<?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');
    }
}

Then we register it as a service:

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

Finally we register the route:

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

Let's run the benchmark:

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

Result: 524.53 Requests per second.

We're also going to list the dependencies:

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

We get 6 + 23 = 29.

So to sum up:

Micro Framework Bundle

By reducing the number of dependencies, we also drastically reduced the framework footprint. This is not surprising as:

Can we go further? Certainly: the FrameworkBundle also follows a "solve 80% of use cases out of the box" (includes Forms, Security, Templating, Translation, Assets, annotations, etc).

By using a MicroFrameworkBundle that would provide the strict minimum and follow the micro framework philosophy of "add what you need" we can surely reduce further the number of dependencies. Hence gnugat/micro-framework-bundle:

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

Then we need to swap the bundle in the registration:

<?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');
    }
}

Finally we can get rid of some configuration:

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

Let's benchmark our trimmed application:

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

Result: 872.83 Requests per second.

We're also going to list the dependencies:

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

We get 3 + 13 = 16.

So to sum up:

Conclusion

Symfony has always been able to be used as a micro framework bundle.

The Standard Edition and the FrameworkBundle follow a "solve 80% of use cases out of the box" philosohpy, which is better for new comers.

However for experimented developers who're looking for a "add what you need" philosophy, which is what micro-framework usually follow, using the Empty Edition and MicroFrameworkBundle can be a viable alternative (they are slimer in term of dependencies and faster).

Note: At the time of writing, MicroFrameworkBundle is still under development (version 0.1.0). Use it at your own risk and contribute to it :) .