Archive for the 'ajax' Category

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.

When Ajax can speed up your site

Many people think that adding Ajax interactions to a web application can cripple a website's performance. Of course, if you add remote periodical executers everywhere, or if you make three Ajax requests to update three parts of a page, the web server will just hate you (servers have feelings, you know). But there can be cases where Ajax can take some burden off the server, where it can be an architecture choice rather than a pure UI choice.

Does it sound familiar?

For instance, take the now classic "add a comment" Ajax form. The user enters data in a form, submits it, and the result is sent to the client in XmlHttpRequest. There is an immediate benefit for the server here: It only has to send back the updated part of the page (in that case, the new comment) rather than the entire page. That represents a notable bandwidth and CPU economy.

Super caching

Another example is an "almost static page", which means that the page contents depend on the user session only for some limited parts. Think of a news website where the only session-dependent part is the name of the connected user displayed on the upper part of the window. If this element wasn't present, the page would be a perfect candidate for super caching.

The super caching is the action to store a copy of the HTML response somewhere under the web root of the server, so that next time the page is requested, the server sends the HTML response without even using PHP. This is very fast, and it can even be done by a lightweight and specialized server like lighthttpd. Symfony has a super caching solution in the form of a plugin, it is called sfSuperCachePlugin.

Ajax comes to the rescue

But, because of the session-dependent element, the page I talk about cannot benefit from the super caching. Can't it, really? What if the session dependent element was removed, and added to the static page afterwards, by the web browser? That's where Ajax comes in. It is a great replacement for iframes, because the Ajax response can be any JavaScript code, used to do some complex DOM modification, and that is more powerful than just replacing an element's innerHTML.

Concretely, that's how you would design your pages to take advantage of the super cache:

  • The page is designed without session dependent element

  • The first time the page is requested, it is stored in the super cache

  • The page contains a static call to another action in Ajax.

For instance, if you use the jQuery Javascript framework, the end of the page can show something like:

<script>
$().ready(function () {
  $.getScript('/path/to/javascript/action');
});
</script>


The /path/to/javascript/action action gets the user's name from the session and database, and sends it back to the browser as a piece of JavaScript modifying the DOM of the static page to include the user's name.

But wait a minute. Modifying a page after it is loaded with JavaScript, isn't that just what unobtrusive behaviours do? That's true, the sfUJSPlugin is designed exactly with this process in mind. Build the static, session-independent, accessible version first, and add the dynamic, session-dependent, highly interactive sugar in JavaScript afterwards. Or, to put it differently, design fast pages first, add the performance penalty afterwards. There is no more limit to the number of pages you can put in cache - even the most session-dependent pages can benefit from super cache.

Pros and cons

The performance advantage is not huge in symfony, because the real cost of a request is the framework initialization. Whether you send one page with cached fragments or two pages with only one using symfony, you will always have to initialize symfony once. But using the solution described here will at least save you the time of deserialization of a complex response from the cache, and a better logic in your design.

One drawback of these techniques is that the load taken off the server ends up being transferred to the client. The web browser has more to do, and the full response to a request takes more exchanges with the server to display - in short, the answer is somehow slower for the end user. Besides, developers tend to forget accessibility when they code Ajax interactions, so the pages have to be though carefully.

Conclusion

To conclude, Ajax can make your website faster because it allows you to use super caching in pages that normally couldn't benefit from it. Symfony has already all the tools to put this idea into practice (namely sfSuperCachePlugin and sfUJSPlugin), so you should never have to buy a new server again.

Unobtrusive JavaScript made possible

Unobtrusive Javascript explained

The usual way of writing JavaScript in templates results in obtrusive code. For instance, the classing link_to_function() helper called in symfony like this:

<?php use_helper('Javascript') ?>
<?php echo link_to_function('see the image', 'showPopup('image.jpg')') ?>


The code appearing in the template is:

<a href="#" onclick="showPopup('image.jpg'); return false;">see the image</a>


While this code effectively provides the correct interaction, it contains an inline even handler (onclick) which will create problems with screen readers. Additionally, the CSS revolution taught us to separate content from presentation, and we soon understood the benefits this could bring (reusability of the presentation layer, better maintainability, etc). Following the same concept would naturally lead to separating behavior from content, and this means putting away JavaScript code. Accessibility and layer separation recently led to a new way of using JavaScript called unobtrusive scripting. The term comes from Stuart Langridge, who started the movement in 2002, rapidly followed by JavaScript gurus like Peter-Paul Koch.

The problem with unobtrusive JavaScript is that it is much longer to design and write. You must first deliver a Javascript-free XHTML content, which must be able to run without further addition, then execute JavaScript code on it to modify some elements, add event handlers, etc. This should sound familiar to those used to styling through CSS, but look at how long it takes:

<head>
  <script type="text/javascript" src="my_behaviors.js"></script>
</head>
<body>
  <a href="image.jpg" id="foobar">see the image</a>


// in my_behaviors.js
function initializePage()
{
  var x=document.getElementById('foobar');
  x.onlick = function () { showPopup('image.jpg'); return false };
}
window.onload = initializePage;


The link works even if JavaScript is disabled. But in order to modify if afterwards, we must add an id attribute to it, then we must register its onclick event handler in JavaScript, and then make sure that the JavaScript is launched when the DOM is ready, therefore executing it when the onload event fires. This is the correct way to achieve the same effect as the first listing in an unobtrusive way.

If you ever tried this, you probably thought just like me: the hell with unobtrusiveness, there is no way I'll spend the rest of my life writing ten lines instead of one just for the sake of accessibility and layer separation!

Fortunately, there are two tools that could make you change your mind.

The first is a JavaScript framework. The most well-known (because it's the official js framework of the most well-known web application framework) is Prototype, but you could as well consider jQuery or others. Doing unobtrusive JavaScript (ok, let's call it UJS until the end of this article) with a JavaScript framework is a lot more easier than with plain JavaScript. See how the previous listing could be reduced by using jQuery:

<head>
  <script type="text/javascript" src="jquery.js"></script>
  <script type="text/javascript" src="my_behaviors.js"></script>
</head>
<body>
  <a href="image.jpg" id="foobar">see the image</a>


// in my_behaviors.js
$().ready(function(){
  $('#foobar').click('showPopup('image.jpg')');
 })


The jQuery dollar function can use a CSS3 selector to find a DOM element, and the addition of a handler is greatly facilitated by the click method. Also, the ready method fires when the document is ready, but you can attach more than one funciton to it.

But that's not enough. This is still way longer to write than the obtrusive version, which was using a single call to a PHP helper. But why not use a helper to add UJS code? This could be very easily done. The syntax could look like this:

<?php use_helper('UJS') ?>
<a href="image.jpg" id="foobar">see the image</a>
<?php UJS_add_behaviour('#foobar', 'click', 'showPopup('image.jpg')') ?>


The UJS helper could very well manage storing the UJS code in the session and writing it in a separate file that would be called by the first template, transforming this listing into the previous one. In fact, I've written a symfony plugin called sfUJSPlugin that does exactly that.

It goes event beyond: If you use the regular symfony helpers, every mention of an event handler property gets automatically transformed into UJS code. So the same effect as before can be achieved with just:

<?php use_helper('UJS') ?>
<?php link_to('see the image', 'image.jpg', 'onclick=showPopup('image.jpg')') ?>


This is not longer than the first example, and that's unobtrusive. At last, we have the right tools to make the web both accessible and usable.

Cross-Domain Ajax with symfony

I recently released a new symfony plug-in, called sfWebBrowser. It's a lightweight HTTP client written in PHP, that doesn't require any additional module to work, and it opens a lot of interesting possibilities. As a matter of fact, it doesn't even require symfony to work - you can use it on any application, with any framework. Refer to its wiki page for installation instructions.

Many web 2.0 applications reuse content from other sites, mostly through REST web services and RSS feeds. The sfWebBrowser plugin already allows you to do that in a very simple way. For instance, to use the symfony blog news feed as a data source, you just need these three lines:

$b = new sfWebBrowser();
$b->get('http://www.symfony-project.com/weblog/rss');
$xml = $b->getResponseXML();


The $xml variable contains a simpleXML version of the online feed, which you can parse easily to retrieve the data you need for your own application.

But haven't you ever thought about a web application that you like in these terms: "Damn, if only they had an API to let me reuse this on my site"? With an HTTP client and the powerful parsing capabilities of PHP, the whole web becomes a giant API.

Take google suggest, for example. They do seem to have a nice API, but as it is not official and not the purpose of this post, we'll voluntarily ignore it. Instead, let's see how to use the publicly available version. When you type characters in a google suggest search box, AJAX requests are sent to:

http://www.google.com/complete/search?hl=en&client=suggest&js=true&qu=WhateverYouTyped

The result is a string meant to be executed in JavaScript:

sendRPCDone(frameElement, "symf", new Array("symphony", "symfony php", "symfony project",
"symfonia", "symfony framework", "symfony tutorial", "symfony-project", "symfw.sys", "cmfce",
"symfony plugins"), new Array("2,540,000 results", "1,700,000 results", "728,000 results",
"1,560,000 results", "979,000 results", "781,000 results", "167,000 results", "817 results",
"967 results", "768,000 results"), new Array(""));


This is quite easy to parse in PHP. So in order to use this output in an AJAX input_auto_complete_tag on your site, you would have to use your server as a proxy to google suggest. Let's see how to do that in symfony. First, create a form with autocompletion on a 'search' input. That's pretty straightforward with a JavaScript helper:

// in mymodule/templates/indexSuccess.php
<?php use_helper('Javascript') ?>
<?php echo form_tag('search/search') ?>
  <label for="search_string">Enter text here:</label>
  <?php echo input_auto_complete_tag('search_string', '',
    'mymodule/autocomplete',
    array('autocomplete' => 'off'),
    array('use_style' => 'true')
  ) ?>
  <?php echo submit_tag('submit') ?>
</form>


Then, in the mymodule/autocomplete action, use the sfWebBrowser to make a request to the google suggest URI, decode it, and prepare data for the template:

// in mymodule/actions/actions.class.php
public function executeAutocomplete()
{
  $service_uri = 'http://www.google.com/complete/search?hl=en&client=suggest&js=true&qu=';
  $b = new sfWebBrowser();
  $b->get($service_uri.urlencode($this->getRequestParameter('search_string')));
  preg_match('/new Array\((.*?)\)/si', $b->getResponseText(), $matches);
  $suggestions = array();   
  foreach(explode(',', $matches[1]) as $suggestion)
  {
    $suggestion = trim($suggestion);
    $suggestions[] = substr($suggestion, 1, strlen($suggestion)-2);
  }
  $this->suggestions = $suggestions;
}


The final part is the autocompleteSuccess.php template. It's as simple as it can be:

// in mymodule/templates/autocompleteSuccess.php
<ul>
<?php foreach($suggestions as $suggestion): ?>
  <li><?php echo $suggestion ?></li>
<?php endforeach; ?>
</ul>



And that's all. The search box of your website now uses the response from the google suggest application to build its own suggestions. Did I say cross-domain AJAX? That's what it is, though. The JavaScript autocomplete control executed in a browser on a page of your domain uses results from another domain, your server (and the sfWebBrowser) being a proxy between the two.

Imagine all you can do with such a tool... "Mashup" is the word, I think.