Wednesday, September 14, 2016

Using Silex and Symfony together in same application

I am testing the idea of having an application using both Silex and Symfony.

Why ?

Maybe because I want for some parts of the application to be really fast, and for the most of the application to enjoy the productivity of using a full stack framework and have access to all the third party bundles.

Of course having 2 application may introduce other kind of problems.

How my application should work:

- receive request
- fast Silex kernel handles the request.
     - if the URL request is not matching any of the routes from Silex, it will return a response with status 404
    -  if the URL is matching a route, execute controller and get response
- check what is the response from Silex
    - if the response has status 404 pass it to the Symfony kernel
   -  otherwise return response

Using the 2 frameworks together is possible because they both use the same abstraction for HTTP Request and Response and the HttpKernel.
There is a project called StackPHP that promotes interoperability between applications based on the HttpKernelInterface.

Implementation:


Create a probject folder under /var/www/public called double: /var/www/public/double
I will be using Symfony Standard Edition and Silex Skeleton project by Fabien Potencier: https://packagist.org/packages/fabpot/silex-skeleton

Install using Composer in two different subfolders:

   composer create-project fabpot/silex-skeleton  silex  ~2.0@dev
   composer create-project symfony/framework-standard-edition symf


Create a virtual host that will point to /var/www/public/double/symf/web
You may want to first save Symfony app.php and app_dev.php before editing them:
app_dev.php :

<?php

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Debug\Debug;
use Silex\Application;



/**
 * @var Composer\Autoload\ClassLoader $loader
 */
$loader = require __DIR__.'/../app/autoload.php';
require_once __DIR__.'/../../silex/vendor/autoload.php';

Debug::enable();

//create request object
$request = Request::createFromGlobals();

//initialize Silex app
$app = require __DIR__.'/../../silex/src/app.php';
require __DIR__.'/../../silex/config/dev.php';
require __DIR__.'/../../silex/src/controllers.php';
//handle request with Silex app
$response = $app->handle($request); if ($response->getStatusCode() === 404) {
    //initialize Symfony app and handle request
    $kernel = new AppKernel('dev', true);
    $kernel->loadClassCache();
    $response = $kernel->handle($request);
    $response->send();
    $kernel->terminate($request, $response);

} else {
    $response->send();
    $app->terminate($request, $response);
}


Into app.php I've added microtime() function to get the loading time in production as I do not have access to the Web Profiler.


<?php

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Silex\Application;

$time_start = microtime(true);
/**
 * @var Composer\Autoload\ClassLoader $loader
 */
$loader = require_once __DIR__.'/../app/autoload.php';
require_once __DIR__.'/../../silex/vendor/autoload.php';


//create request object
$request = Request::createFromGlobals();

//initialize Silex app
$app = require __DIR__.'/../../silex/src/app.php';
require __DIR__.'/../../silex/config/prod.php';
require __DIR__.'/../../silex/src/controllers.php';

$response = $app->handle($request);

if ($response->getStatusCode() === 404) {


    $kernel = new AppKernel('prod', false);
    $kernel->loadClassCache();
    $response = $kernel->handle($request);
    $response->send();
    $kernel->terminate($request, $response);

    $time_end = microtime(true);
    $time = $time_end - $time_start;

    echo "Execution time: $time seconds\n";

} else {
    $response->send();
    $app->terminate($request, $response);

    $time_end = microtime(true);
    $time = $time_end - $time_start;

    echo "Execution time: $time seconds\n";
}


I did some simple tests using the production env (app.php) and generally Silex is 3 times faster than Symfony, which is no surprise as it is lighter.
In Silex I used Doctrine DBAL (not ORM), the provider it is included in the Silex Skeleton project, just needs to be configured in src/app.php:



$app->register(new DoctrineServiceProvider(), array(
    'dbs.options' => array (
        'localhost' => array(
            'driver'    => 'pdo_mysql',
            'host'      => 'localhost',
            'dbname'    => 'symfony',
            'user'      => 'root',
            'password'  => 'root',
            'charset'   => 'utf8',
        )
    ),
));

No comments:

Post a Comment