By: Brian Dobberteen

I will start by saying that my experience with PHP is somewhat limited, so this is more of an exercise for my benefit. I’d like to take ASP.NET out of the formula for a bit here, and instead resort to a simple PHP page to answer AJAX requests from jQuery.

Earlier, we used ASP.NET PageMethods, but let’s see a possible PHP solution.

Again, we’ll just be performing the trivial task of retrieving a city/state location based on a zip code entered on our form.

...
<form id="form1">
  <input type="text" name="ZipCodeTextBox" id="ZipCodeTextBox" />
</form>
...

Since we’ll use jQuery to bind this textbox to the onchange event, there are no additional attributes needed in our markup here.

<script type="text/javascript">
$(function() {
});
</script>

A quick note on jQuery for those new to it: in line 2 above, the rather terse statement $(function() is shorthand for “wait until the page’s document object model (DOM) is loaded so that we can bind event handlers, set CSS properties, etc. to page elements.” The DOM loads before the images on a page are done downloading, so this allows for our jQuery to work as soon as it is able to. $ is essentially shorthand for jQuery itself and is central to its operation. Wrapping an element in $() bestows upon that element with all the power of jQuery, allowing for extensive manipulation through chaining of functions.

jQuery also allows for us to create new DOM elements by using the syntax:

$("<div id='div1'></div>")

In this example we have created a div with an id attribute of ’1′. Even though this element now “exists,” it is not actually in the DOM. Rather than include an <input> in our XHTML, let’s instead create it with jQuery. With chaining, we can handle not only the creation, but the binding of the onchange event, some simple CSS styling, and finally, the insertion of the new element into the page.

Because ASP.NET is pretty specific about a PageMethod can respond to a client-side reqest, we used the $.ajax() function in our earlier example, as it allows for the most granular approach to sending an AJAX request. But now, since we are going to build a simple PHP script to respond to a GET request, there is a very handy jQuery function, namely $.getJSON():

$(function() {
  $("<input type='text' id='ZipCodeTextBox' name='ZipCodeTextBox' />")
    .bind('change', function(event) {
      if (/^\d{5}(-\d{4})?$/.test($(this).valI())) {
        $.getJSON("ZipCodeService.php", {zip: $(this).val()}, function(msg) {
          $("<label />").text(msg.City + ", " + msg.State).appendTo("#form1");
        });
      }
    })
    .appendTo("#form1");
});

Lines 2 and 6 demonstrate jQuery’s ability to generate new DOM elements on the fly – in this case a new textbox and a new label – and perform various operations on them, including inserting them into the page. Here we use appendTo() to place our new elements after the last child element of what we’ve selected, in this case #form1.

Line 5 is the call to $.getJSON, whose signature looks like:

$.getJSON(url, parameters, callback)

In our example, we use ZipCodeService.php as our URL, which refers to the script we’re about to create that will reside in the same location as our XHTML file. For parameters, I chose to create a simple object using JSON notation, {zip: $(this).val()}, which will be turned into proper query string parameters by jQuery. We could also have set our parameters as they would appear in a query string, i.e. “zip=” + $(this).val() – in this simple GET request, this is a pretty trivial point, but if you were working with a more complicated javascript object, it would be a lot easier to simply pass it as a whole rather than trying to manually serialize it. Finally, the callback parameter to $.getJSON() is the name of a function that will fire following completion of the AJAX request – it features two parameters, the first containing the javascript object returned and the second the status of the request. We are going to ignore the status parameter, so our anonymous callback function looks like:

function(msg) { $("<label />").text(msg.City + ", " + msg.State).appendTo("#form1"); }

Doesn’t get much easier! All we had to do was return a JSON encoded object from our PHP function and in two lines of javascript, we have direct access to the City and State properties that we requested.

Now, let’s check out ZipCodeService.php – please note: this is my first foray into the world of PHP and XML, so if I’ve badly butchered anything, please let me know!

<?php
// Check to make sure 'zip' was sent in the GET request
if (!empty($_GET["zip"])) {

  // Set a couple of constants
  define("MAP_URL", "http://local.yahooapis.com/MapsService/V1/geocode?appid=");
  define("MAP_APP_ID", "<YOUR APP ID>");

  // Retrieve the query string parameter 'zip' and store it in $zip
  $zip = $_GET["zip"];
  
  // Run *another* Regular Expression test on the input
  if (preg_match("/^\d{5}(-\d{4})?$/", $zip)) {

    // Use simplexml_load_file to grab our XML response from Yahoo
    // This works much the same as when we loaded the XML in our ASP.NET
    $xml = @simplexml_load_file(MAP_URL . MAP_APP_ID . "&zip=" . $zip);

    // Make sure that our request actually succeeded!
    if ($xml)
    
      // Because the Yahoo Maps API will give some funky responses to some
      // strings that *look* like US zip codes but are somehow classified as foreign
      // we are going to check that the zipcode is indeed a US postal code and that
      // the Zip element is set in the response - something which is absent on the
      // postal codes deemed to be located outside the US by Yahoo Maps
      if (!empty($xml->Result->Zip) && $xml->Result->Country == "US")
        $ret = array('City' => (string)$xml->Result->City, 'State' => (string)$xml->Result->State);
      else
        return false;
    else
      return false;


  echo json_encode($ret);
}
	} 
?>

On line 28, we are using array() to construct a new associative array (aka hash) with the keys ‘City’ and ‘State’ (big surprise, right?!) and their respective values. I found that our simplexml object exhibits some interesting behavior when it comes to casting. It seems that it is smart enough to know to cast to a string automatically when used in some scenarios, such as in a comparison:

if ($xml->Result->City == "Anytown")

But if we try and assign $xml->Result->City to a key in our associative array, it instead assigns the entire simplexml object rather than its string contents. So, in our call to array() we need to cast the two elements as such:

$ret = array('City' => (string)$xml->Result->City, 'State' => (string)$xml->Result->State);

To cast our objects, we simply prepend them with the type to cast to enclosed in parentheses – a lot less verbose than VB’s clunky CType() function!

Now, all we need to do for our client-side $.getJSON() call to be happy is to write back a string representing a JSON object, which is where the handy json_encode() function comes into play. We just pass it our hash $ret and voila! Out pops a JSON string that we simply echo to our client, and that’s it on the server side!

All that’s left now is for the callback function we specified in our call to $.getJSON() to fire, in which a new <label /> is created, has its text set to City, State and is finally appended to our form.

Was this easier that doing it with ASP.NET? Was it lighter-weight? Faster? For something as trivial as this, I think that performance is not much of a consideration at all. Especially since the entire thing bottlenecks on our request to Yahoo Maps. In terms of ease of coding, I think I slightly prefer the ASP.NET PageMethod, though that is probably because I am such a Visual Studio fanboy. The .NET solution still requires two files as does the jQuery/PHP that we’ve concocted here. The XML handling routines in PHP seem just as capable as those in .NET. One thing I can say I like more about the PHP solution is that I feel much more in control of exactly what is going on in terms of generated code (there is none for our PHP solution!). I am not sure how much weight is added to a page when ASP.NET AJAX builds proxies for our client code to talk to a PageMethod, and I do know that jQuery, in minified and gzipped form, only takes 19K – which is awfully small… how close does ASP.NET AJAX come to that number?

I think we’ve pretty much exhausted this whole zipcode lookup thing, so next time I want to examine using nested ASP.NET data controls such as the Repeater, FormView, etc.