GOPHERSPACE.DE - P H O X Y
gophering on sdf.org
# A MaxMind GeoIP Country Lookup Function in PHP

Originally published on 14 July 2014 at
http://kohanikin.com/2014/php-maxmind-geoip-country-lookup.html
----------------------------------------------------------------------

I've been using MaxMind GeoIP on my e-commerce website for years now
(almost a decade!) to identify which country my visitors are coming
from. It's been really useful for localizing prices into local
currency, deciding when EU VAT is relevant to a purchase, and even to
block countries with high web-form spam and zero sales. It's been
super affordable too.

MaxMind announced that they're upgrading their GeoIP location web
service, and require everyone to update their code to run on the new
version 2.0 service from 4 August 2014.  Alas, their sample code
recommends using their official code libraries, instead of the
lightweight single function they used to have to grab the country
details for a user.

To fill the gap, I've written a couple of functions that you can use
for the 2-letter Country Code lookup that used to be in MaxMind's
Legacy service.  To use the code below you must replace the User ID
and License Key with your own in each function before use. (You can
also find a list of ISO 2-letter country codes on Wikipedia.)

gopher://gopherpedia.com/0/ISO_3166-1_alpha-2

http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2

First, the simple code. If your server supports file_get_contents,
this is the most elegant solution, but many / most servers disable it
for security reasons:



//////////////////////////////////////////////////////////////
///  Looks up the country associated with a given IP address,
///  according to the MaxMind GeoIP 2.0 lookup service.
///
///  This version may not work on all servers, as
///  file_get_contents is often blocked for security reasons.
///  But it can be more efficient than the standard version.
///
///  @param  ipaddress  the IP address to lookup
///  @return a two letter ISO 3166 Country Code, or ERR
//////////////////////////////////////////////////////////////

function getCountryQuick($ipaddress) {

        $userid = 'XXXX';
        $license_key = 'XXXXXXXXXXXX';
        $query = 'https://geoip.maxmind.com/geoip/v2.0/country/' . $ipaddress;

        if (empty($ipaddress)) return 'ERR';
        $opts = array(
            'http'=>array(
                'method'=>"GET",
                'header'=>"Authorization: Basic " . base64_encode($userid.':'.$license_key) . "\r\n"
            )
        );
        $context = stream_context_create($opts);
        $file = file_get_contents($query, false, $context);
        if ($file === FALSE) return 'ERR';
        $geo = json_decode($file);
        $country = $geo->{"country"}->{"iso_code"};
        return $country;
}



For other servers, you'll probably prefer this code:



//////////////////////////////////////////////////////////////
///  Looks up the country associated with a given IP address,
///  according to the MaxMind GeoIP 2.0 lookup service.
///
///  @param  ipaddress  the IP address to lookup
///  @return a two letter ISO 3166 Country Code, or ERR
//////////////////////////////////////////////////////////////

function getCountry($ipaddress) {

        $userid = 'XXXX';
        $license_key = 'XXXXXXXXXXXX';
        $query = 'https://geoip.maxmind.com/geoip/v2.0/country/' . $ipaddress;
        $data = '';

        if (empty($ipaddress)) return 'ERR';

        $url  = parse_url($query);
        $host = $url["host"];
        $path = $url["path"];
        $timeout = 1;

        $fp = fsockopen('ssl://' . $host, 443, $errno, $errstr, $timeout);
        if ($fp) {
                $buf = '';
                // Use HTTP/1.0 here else server takes forever to hang up.
                fputs($fp, "GET $path HTTP/1.0\r\nHost: " . $host . "\r\nAuthorization: Basic " . base64_en
code($userid.':'.$license_key) . "\r\n\r\n");
                while (!feof($fp)) {
                        $buf .= fgets($fp, 1024);
                }
                $lines = explode("\n", $buf);
                $data = $lines[count($lines)-1];
                fclose($fp);
        }
        else { return 'ERR'; }

        if ($data === FALSE) return 'ERR';
        $geo = json_decode($data);
        $country = $geo->{"country"}->{"iso_code"};
        return $country;
}




I'm sure the code above can be improved (looking at it now, I can see
it could use some more sanity checking if the returned JSON response
doesn't include what you expect). Any suggestions for improvements are
welcome!

----------------------------------------------------------------------

(C) 2014 Kohan Ikin / http://kohanikin.com/

----------------------------------------------------------------------