Combining HTTP and JavaScript APIs with php
The implementation of the ip to geo location script started as a quick hack in php. Sometimes I use php scripts because everyone can include it in his own website account (but I am not an active php programmer). It's much easier to write a simple php script instead of deploying a more complex web application. The quick result was this implementation as an example. After getting the php script to work I rewrote it in python, transformed it into a google appengine application and deployed it. Both worked fine and now there are two examples for the API integration available.
- the tutorial how to use ip to geolocation provider api
- the free usage ip to geolocation aggregator script
- the php stand alone implementation
- the python google appengine implementation
php and http API / RESTfull web services
To fetch http resources use the the curl module. It's robust and there aren't too many possibilities to break it. If you want to use more than one service provider's API you can access them in a parallel fashion. So the total execution time for the request will be equal to the slowest provider's roundtrip time instead of the sum of all roundtrip times together.
- function readUrls($urls) {
- $req = array();
- $mh = curl_multi_init();
- for($i = 0; $i<count($urls); $i++) {
- $req[$i] = curl_init();
- curl_setopt($req[$i], CURLOPT_URL, $urls[$i]);
- curl_setopt($req[$i], CURLOPT_RETURNTRANSFER, 1);
- curl_multi_add_handle($mh, $req[$i]);
- }
- $running=null;
- do {
- curl_multi_exec($mh,$running);
- } while ($running > 0);
- //read the content
- $ret = array();
- for($i = 0; $i<count($urls); $i++) {
- $ret[$i] = curl_multi_getcontent($req[$i]);
- curl_multi_remove_handle($mh, $req[$i]);
- }
- curl_multi_close($mh);
- return $ret;
- }
This simple function gets an array of urls and returns an array of the content. Ok - only the optimistic way without error detection!
Basic XML parsing with PHP
First you have to check if the Content-encoding is set correctly. If not you'll have to convert the http-content to the encoding declared within the xml declaration, which is UTF-8 in the example below.
- <?xml version="1.0" encoding="UTF-8"?>
- <Response>
- <Ip>74.125.45.100</Ip>
- <Status>OK</Status>
- <CountryCode>US</CountryCode>
- <CountryName>United States</CountryName>
- <RegionCode>06</RegionCode>
- <RegionName>California</RegionName>
- <City>Mountain View</City>
- <ZipPostalCode>94043</ZipPostalCode>
- <Latitude>37.4192</Latitude>
- <Longitude>-122.057</Longitude>
- </Response>
And now the simple php code for xml parsing:
- if (substr($content,1,5)=='?xml') {
- $xml = simplexml_load_string(utf8_encode($content));
- if ($xml && $xml->City) {
- //Add the geo data to the result set
- $result = array("name" => "ipinfodb",
- "country" => strval($xml->CountryName),
- "city" => strval($xml->City),
- "long" => floatval($xml->Longitude),
- "lat" => floatval($xml->Latitude) );
- }
php and parsing non-structured data
For non-structured data regular expressions are the best solution if you can beat the beast. Otherwise try it with several split-actions.
- Country: UNITED STATES (US)
- City: Sugar Grove, IL
- Latitude: 41.7696
- Longitude: -88.4588
And now the php code for text parsing:
- if (preg_match("/Country:\s+(.*?)\(\w+\)\nCity:\s+(.*?)\nLatitude: (-*\d+\.\d+)\nLongitude: (-*\d+\.\d+)/", $content, $ma)==1) {
- $result = array("name" => "hostip",
- "country" => $ma[1],
- "city" => $ma[2],
- "long" => floatval($ma[4]),
- "lat" => floatval($ma[3])
- };
php and parsing JSON data
Json is defined as the JavaScript object notation and is an universal key-value transfer format, especially in web applications. So many API (like the IPInfoDB from the xml example) offer these formats and php has the build-in function to parse it. The encoding is defined as utf-8!
- if (substr($content,0,1)=='{') {
- $json = json_decode($content);
- if ($json && $json->{'City'}) {
- $result = array("name" => "ipinfodb",
- "country" => $json->{'CountryName'},
- "city" => $json->{'City'},
- "long" => floatval($json->{'Longitude'}),
- "lat" => floatval($json->{'Latitude'})));
- }
- }
Getting the javascripts data together
The described php code should produce a simple javascript (after running the above script) containing the fetched data and including the other javascripts via document.write function like this output:
- var com = com||{};
- com.unitedCoders = com.unitedCoders||{};
- com.unitedCoders.geo = com.unitedCoders.geo||{};
- <?php
- //build your result
- echo "com.unitedCoders.geo.ll = [".json_encode($result)."];\n";
- ?>
- document.write('<script type="text/javascript" src="http://j.maxmind.com/app/geoip.js"></script>');
- document.write('<script type="text/javascript" src="http://api.wipmania.com/wip.js"></script>');
- document.write('<script type="text/javascript" src="geo.js"></script>');
After the other javascript sources are loaded (sequential) the geo_funcs.js javascript can map from the internal structures to the global com.unitedCoders.geo.ll list and add some helper functions. For example the "get the middle position" of all lat/long values.
add the external javascript data to the result
The maxmind javascript api has as result a set of global functions. Here the maxmind part of geo_funcs.js:
- if (window['geoip_country_name']) {
- com.unitedCoders.geo.ll.push({
- "name": "maxmind",
- "country": geoip_country_name(),
- "city": geoip_city(),
- "long": parseFloat(geoip_longitude()),
- "lat": parseFloat(geoip_latitude()) });
- };
- //calculate the middle point of all position where city is set
- (function() {
- var lat = 0.0;
- var long = 0.0;
- var count = 0;
- for (var i=0; i<com.unitedCoders.geo.ll.length;i++) {
- var service=com.unitedCoders.geo.ll[i]
- if (service.long && service.lat && service.city) {
- lat += parseFloat(service.lat);
- long += parseFloat(service.long);
- count ++;
- }
- }
- com.unitedCoders.geo.lat = lat/count;
- com.unitedCoders.geo.long = long/count;
- })();
The function wrapper chooses all positions with a set city, hoping these lat/long value pairs are the most accurate positions and calculate the middle point. After testing I noticed the google positions are accurat (but no city information is available).
In the next article I will describe the same functionality but implemented in python for the google appengine.
Attachment | Size |
---|---|
geo_php.txt | 3.06 KB |
- Login to post comments
Comments
Anonymous - Fri, 08/21/2009 - 04:00
very cool & good tip, thank you very much for sharing.
Can I share this snippet on my JavaScript library?
Awaiting your response. Thank
Combining HTTP and JavaScript APIs with python on google app (not verified) - Wed, 06/16/2010 - 21:46
[...] the php stand alone implementation [...]
3 caching steps to boost your webservice by x10 | united-cod (not verified) - Mon, 07/26/2010 - 18:46
[...] the php stand alone implementation [...]