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->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:
"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:
<?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:
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:
<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.

actually, there's a (tiny) dependency on Symfony's sfToolkit class : http://www.symfony-project.com/trac/browser/plugins/sfWebBrowserPlugin/lib/sfWebBrowser.class.php#L335 (and i can't patch it from where i am at the moment
hi,
nice article...
little typo:
"The final part is the autocompleteSuccess.php template. It's as simple as it can be:"
is followed by
"// in mymodule/templates/indexSuccess.php"
..
Thank you,
Markus
Thanks Markus, I just fixed the typo.