Buzz Words …

20 08 2010

There are a lot of buzz words that a PHP Developer needs to know these days. Here’s a sampling: HTML5, CSS, jQuery, AJAX, JSON, XML, SimpleXML and Zend Framework. Can all of these really work together? And, how would you put them together? As a web developer, one becomes accustomed to working with many different kinds of technologies for any given web application. The challenge is to understand how to get maximum gain from each so that the final result is a cohesive application.

I’ve put together such an example using weather forecast data from wunderground.com. That site has an XML data feed which you may use on condition that you abide by the TOS.

About

My application uses PHP to retrieve the data, encode it as JSON and finally transfer via AJAX to front-end script. PHP is more than able to retrieve the contents of a url; any number of its built-in functions would suffice. In this particular case though, I’m going to use a component of the Zend Framework, Http Client. While there’s plenty of documentation about how to use the framework for an MVC application, we’re going to explore how to use it on a component basis.

After the Http Client fetches data, it needs to be parsed so we can extract what we need. For that purpose, I’m going to employ SimpleXML.   But, rather than return XML to the jQuery script, I will convert the data into  JSON, a much lighter   data  transfer option and as a result it  will  be easier for jQuey to process.

What’s left? On the front end, we’re going to have an HTML page that will contain the jQuery script and the HTML as well as CSS for displaying the forecasted high and low information.

I thought it would be fun to look at the weather in another country and selected Paris, France, using the data feed graciously provided by Weather Underground, Inc, at the following url:

http://api.wunderground.com/auto/wui/geo/ForecastXML/index.xml?query=Paris,%20France”

Feel free to change the query string to that of another city as wunderground.com has weather data on cities throughout the world.

Zend Framework and Its Http Client

To fetch the data, I am employing the default adaptor of the Http Client which is the PHP function stream_socket_client. If we were to use stream_socket_client() directly, the code would resemble the following:

$fp = stream_socket_client("tcp://api.wunderground.com:80", $errno, $errstr, 30);
if (!$fp) {
    echo "$errstr ($errno)
\n";
} else {
    fwrite($fp, "GET /auto/wui/geo/ForecastXML/index.xml?query=Paris,%20France HTTP/1.0\r\nHost:  api.wunderground.com\r\nAccept: */*\r\n\r\n");
    while (!feof($fp)) {
        echo fgets($fp, 1024);
    }
    fclose($fp);
}

The above code only takes a few lines, so why bother with the Http Client? In terms of keeping your code safe, the above snippet is a bit exposed. One could easily interject a typo in the request unintentionally. Also, the code as written neglects to handle redirection; you would need to handle redirection yourself and, doing so, can easily involve writing many more lines of code. Lastly, PHP developers are typically shielded with the fopen() wrapper from the details of working with the http protocol, and how much more so with tcp which is a lower level networking protocol. So, in terms of ease of use, the framework’s Http Client wins on several accounts.

If you work with the Http Client, you will see only what you need, as follows:

$uri = "http://api.wunderground.com/auto/wui/geo/ForecastXML/index.xml?query=Paris,%20France";
$client = new Zend_Http_Client( $uri );
$response = $client->request();

If you compare the two snippets above, clearly the last one using the Http Client is far more pleasant to review.

I’ve found that working with the Zend Framework on a component basis is fairly simple. The only thing to watch out for are dependencies and files paths. The last point is especially important with respect to different operating systems. How you specify the path will vary if one is on Windows or on Linux.

Incidentally, you don’t need to install the full framework to work with the Http Client. Whereas Zend offers various configurations of the framework, there is another alternative. On my Windows box, I discovered that my housekeeping efforts had included inadvertently deleting the framework. But, I had managed to retain GData a library for working with Google‘s Calendar. GData has a pared down version of the Zend Framework. Note in the following code how I specify the path for the Http Client

$path = realpath("/wamp/www/ZendGData/library");
set_include_path($path);
require("Zend/Http/Client.php");

Since my laptop runs on Unix, I specify the path differently. I use the following:

$path = 'Applications/XAMPP/htdocs/ZendFramework/library';
set_include_path($path);
require("Zend/Http?Client.php");

Beyond the fact that I’m using different PHP bundles and different versions of the framework, what is striking are the different ways of specifying the path. In Windows, I needed to use realpath() whereas for the laptop, I merely specify the path information. According to the online manual,

On windows realpath() will change unix style paths to windows style.

So, the above path for Windows would be translated as:

C:\wamp\www\ZendGData\library

Of course, one could hand-code the above path. But, you need to escape your slashes if you use double quotes or you’ll get a fatal error like the following:

Fatal error: Uncaught exception ‘Zend_Http_Client_Adapter_Exception’ with message ‘Unable to Connect to tcp://api.wunderground.com:80. Error #10060: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. ‘

Once I switched to single quotes, then the path worked and so did the code. So, it is a convenience to use real_path() on Windows and I strongly recommend it.

One may argue about the wisdom of employing PHP’s set_include_path($path). (I also could have done the same using ini_set(‘include_path’). Setting this runtime option, will result in :

“… a slight performance penalty as set_include_path is called every time the page runs.”

according to http://www.geeksengine.com/article/php-include-path.html. If performance or scalability are issues of concern, then it’s preferable to alter the setting for include path in the PHP INI file and/or create a .htaccess file. (See discussion at http://bytes.com/topic/php/answers/6102-include-paths-best-practices.)

Returning our attention back to fetching the weather data with the Http Client, consider the following code:

<br />
$path = 'Applications/XAMPP/htdocs/ZendFramework/library';
set_include_path($path);
require("Zend/Http/Client.php");
$uri = "http://api.wunderground.com/auto/wui/geo/ForecastXML/index.xml?query=Paris,%20France";
$client = new Zend_Http_Client( $uri );
$response = $client->request();
if ($response->isError()) {
  		echo "Error transmitting data.\n";
  		echo "Server reply was: " . $response->getStatus() . " " . $response->getMessage() . "\n";
		exit();
}
else
{
                 $xmlstr = $response->getBody();
		 $res = '';
		 if ($sxe = simplexml_load_string( $xmlstr ))
		{
   			  $a = array($sxe->simpleforecast->forecastday->high,
                          $sxe->simpleforecast->forecastday->low);
                          echo json_encode($a);
                }
?>

Experienced web scrapers will note that I neglect to specify a user agent in the above snippet. That’s unnecessary because the Http Client has a default user-agent value of ‘Zend_Http_Client’. In this particular case, the user-agent value works, but you may need to alter that setting depending on the type of application you create.

Navigating with SimpleXML

I’d like to say that getting the data is half the battle. In truth, I think it’s the parsing of it wherein lies the real battle. SimpleXML is supposed to make it easy to work with XML but I usually find working with it is an experience that often belies its name.

To learn how to parse the data, you may wish to browse the following url:

http://wiki.wunderground.com/index.php/API_-_XML

The way the code works, the Http Client retrieves a string of XML formatted weather data. That string I use as the basis for creating a SimpleXML element which should be helpful in navigating the results for the high and low forecasted temperatures. I initially reasoned that I could obtain the high, with the SimpleXML element, as follows:

var_dump($sxe->forecast->simpleforecast->forecastday->high->fahrenheit);

But this snippet yields an error message and no data, owing to a small error. The root element is forecast, so it is needless to specify it. Once I corrected that mistake, I was able to successfully parsse out the high and low dat and return the results as a json encoded array of the following two SimpleXML Elements:

	  $a = array($sxe->simpleforecast->forecastday->high,
                          $sxe->simpleforecast->forecastday->low);
Transporting with JSON

The great thing about JSON is that it is a subset of JavaScript (see http://www.json.org/js.html which makes it easy to work with if you’re dealing with JSON objects; they are good-to-go with JavaScript. The return value in the above script will look similar to the following:

[{"fahrenheit":"86","celsius":"30"},{"fahrenheit":"70","celsius":"21"}];
jQuery: A Real Powerhouse

This PHP script is now ready to be used in an AJAX application. We’re going to make this task less cumbersome by using jQuery’s support for AJAX and JSON. In doing so, we save ourselves from having to deal with AJAX minutia and avoid manually parsing JSON . In another file I create the following code:

  doctype html>
<html>
<head>
	<title>test of getting weather data</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
css" href="./ZFreal.2.css" />
</head>

<body>
<article id="page-content">
<h1>Weather Forecast
<h2> - Paris, France -
<div id="result">
</div>
</article>

<footer>
<div>
<img src="./images/wui.jpg" />

<p><a href="http://www.wunderground.com">www.wunderground.com</a></p>
</div>
</footer>
<script type="text/javascript"><!--mce:0--></script>
<script>
$.getJSON('ZFreal.2.ajax.php', function(data) {
  $('#result').html('<p>High: ' + data[0].fahrenheit + ' and Low: '
    +  data[1].fahrenheit + '</p>').css({
   "background": "#eeeeee", "color": "#ff0099" });
});
</script>
</body>
</html>

I use jQuery’s getJSON() which uses AJAX to call the above PHP script and use the resulting JSON in JavaScript. The results are displayed in the aptly named result DIV element.

The HTML5 Aspect

As far as the HTML code is concerned, it validates as HTML5 (still an evolving standard). If you wish to check your own HTML5, there’s an experimental validator at validator.w3.org. HTML5 eliminates a lot of the coding complexity in other versions of HTML and it adds some other useful tags, too. Since it is still an evolving standard it will be exciting to see how it develops. If you don’t know HTML5, you should start learning it now as it is rapidly becoming the new standard, even though it is still in development. already there are job ads requiring that one know HTML5.  Note: HTML5 does not allow the presentation elements that appear in previous versions of HTML.

Styling with CSS2 and jQuery

The styling of elements must be handled by CSS. For that purpose, I include CSS from a file whose contents are as follows:

body {
margin : auto;
width : 100%;
font : 100% Arial, Helvetica;
}
#result {
margin-left : auto;
margin-right : auto;
width : 50%;
}
p {
text-align : center;
}
h1, h2 {
text-align : center;
}
footer div {
margin : auto;
width : 50%;
padding-top : 5em;
font : 16pt Futara;
}
img {
padding-left : 38.5%;
width : 150px;
padding-right : 33%;
}

The contents may be basic but they do validate as CSS Level 2.1. Observe also that I use jQuery to handle the CSS styling of the displayed data. I set a few CSS properties using a syntax that is already familiar to us, namely the object literal key-value notation.

Closing Thought

And, there you have it; all the above buzz words playing nicely in an application that indicates the current weather forecast for Paris, France. Whew!

This work is licensed under a Creative Commons License
.

Advertisements

Actions

Information

One response

7 09 2010
XML to JSON with PHP « Sharon Lee Levy's Blog

[…] a previous post, Buzz Words, I used PHP to JSON-encode an array of text values — values that I had extracted from XML […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




%d bloggers like this: