A MaxMind GeoIP Country Lookup Function in PHP

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.

Screenshot of some PHP code

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.)

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_encode($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!

14 July 2014 // ©2014 Kohan Ikin.