A small symfony for a fast response

Sometimes, the price of a request when dealing with a symfony application can be overwhelming. But instead of getting back to spaghetti PHP, maybe you can get a handful of symfony features for a share of its initialization time.

The features without the cost

I met this case when designing a feature-rich Content Management System that made a heavy use of the cache - and of the Super Cache. Basically, symfony was able to compute very complex pages and serve them as static page, that means very fast.

The Super Cache was a very efficient performance enhancer, for a small cost. Imagine that you set the lifetime of the super cache to 10 seconds; when a server is under a heavy load, a given page is only calculated once every 10 seconds, even if requested 500 times in between. With these figures, activating the Super Cache roughly multiplies your site's responsiveness by 500.

Design with speed in mind

But "with great power comes great responsibility", or so they say. To be able to use page caching with layout, and therefore the Super Cache plugin, the application had to be designed very carefully.

The most forbidden thing when you want to use page cache with layout are parts of the page that depend on the session - think about a header saying "Hello, John Doe" when you are connected, even if the rest o the page is completely session-independent. Unfortunately, that's always what the client wants on every page. So I had to find a solution to make the page cacheable without losing the basic user customization.

Ajax to the rescue

As explained in a previous post, you can always defer the customization of the page to the next request, by serving a session-independent page that calls an Ajax action in the background to retrieve the data necessary to change the user name in the header.

But symfony does not really fit for the second request. The cost of the initialization of a symfony request is not to neglect, and counts for 90% of the response time in very small requests - such as getting a username from the database based on a key.

Bitter swift symfony

That's when the idea of a "small symfony" comes. Wouldn't it be great if you could get access to the model layer, the configuration, the autoloading, the user object, the helpers, and keep a MVC separation, without initializing the whole framework?

That would indeed give a boost to any application designed according to the principle exposed in the quoted article. But wait a minute, we already know of "lightweight actions". They are called "components" in symfony. The only problem is that they cannot be called from the outside.

Inside out

Can't they? I'm not so sure. Imagine a script lying under your web root folder, a "lightweight front controller", with the following code:

define('SF_ROOT_DIR',    realpath(dirname(__FILE__).'/..'));
define('SF_APP',         'frontend');
define('SF_ENVIRONMENT', 'prod');
define('SF_DEBUG',       false);

require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php');

$module = $_GET['module'];
unset($_GET['module']);
$action = $_GET['action'];
unset($_GET['action']);
sfLoader::loadHelpers('Partial');
include_component($module, $action, $_GET);


It looks very much like a regular symfony front controller script, except that it lacks the final sfController::dispatch() line. And indeed, it does not initialize the filter chain, handle validation nor output escaping. It just initializes the smallest part of symfony required to execute a "lightweight action"

I saved this script under web/component.php. Now, my Ajax calls can be made to the following URI:

http://mysite/component.php?module=foo&action=bar&key1=value1

The server will then return the result of the execution of an

include_component('foo', 'bar', array(
'key1' => 'value1'
));


Does it work?

Now I can execute a component from the client side. The component architecture offers native View/Controller separation, and the configuration initialization brings autoloading, database access, and more. It does work perfectly, but is it fast? Speed tests show that not launching the filter chain saves about 40% to 50% of the cost of a symfony initalization. This means that you can multiply the number of requests that your server can handle by two - for very simple requests.

Be aware that this trick can only be used in some very particular cases, and only for very light requests. It may jeopardize security, and will often prove to be very limited. But for a page split between a session-independent part and a small session-dependent action, it does the trick.

Before we leave

The great thing about this article is that the trick it exposes is not its best part. For the original need of including session-dependent data into a generated CMS page, Ajax is not the only solution. JavaScript alone can do all the job on the client side, and so your server will never need to embed user data in the page. Ok, it will need to do it once, after which the client keeps the data in a cookie, and a JavaScript executed at page load inserts this data into the page - on the client side. And now every CMS page can be cacheable with its layout.

12 Comments so far

  1. Alex on March 8th, 2008

    Great Idea!

    Thanks for the tips :-)

  2. Stefan on March 8th, 2008

    This is an excellent solution, one that I wouldn’t have considered. Of course, I haven’t had any need for this yet, but even if I had, I wonder if I’d found this solution. Excellent!

  3. Kevin on March 8th, 2008

    Nice one!
    Reminds of 2 things :
    1- i unfortunately won’t make it to the 2008 PHP Quebec Conference, but i’m waiting with great interest for Fabien’s slides on creating a “fully featured lightweight framework” based on Symfony… it is somehow related to your point made here.
    http://conf.phpquebec.com/en/session#the_symfony_platform_create_your_very_own_framework
    2- also i check a bit how the gmail contact manager works because i find the application really fast and user friendly… and from what i’ve seen they do exactly what you metion in the last part of your post : load user data once, then the whole application runs on client side js only, and makes ajax calls only when one saves data.

    Thanks a lot for this nice hint/thought.

  4. NiKo on March 8th, 2008

    Nice trick, but you’re right warning on big security issues if not used properly.

  5. […] A small symfony for a fast response […]

  6. […] A small symfony for a fast response […]

  7. DigitalBase on March 10th, 2008

    The “lightweight front controller” is an excellent tip, i wouldn’t come up with this myself…

    thanks for the tips

  8. chris on March 11th, 2008

    What kind of security risks are involved?
    also does this mean the sfUJSPlugin wont work because the filters are not activated?

  9. […] describes in his blog post A small symfony for a fast response how to create a new light-weight front controller that directly serves a […]

  10. devoted on March 24th, 2008

    ?

  11. Matthew on April 1st, 2008

    Nice solution. I’ve recently started writing symfony apps and the main drawback I’ve found using ajax calls is that they’re not responsive enough if the call is to some symfony code which just pulls out a record and dresses it up.
    So the idea of a miniSymfony seems great!

  12. Marc on April 14th, 2008

    Hi All. This is a great tip, but one question comes to my mind. Is it possible to enable only one filter for this front controller? I’m thinking about the security filter, otherwise, I can make an AJAX call an retrieve info that I’m not suposed to read.

    Anyway thanks for the tips, it’s great!

Leave a reply