Skip to main content

Introduction to GeoIP

In case you’ve ever used Google Analytics or any simmilar tool where you can see exact location from where visitors of your site came from, you’ve probably wondered how they were able to dig that info. Is it magic or what? Of course, it’s not kind of magic, exact location of visitor is defined by visitor’s IP address, and technology used to locate user by his IP is well known as GeoIP.

Today we are going to look how to locate visitor of your site with PHP and Max Mind’s GeoIP database. In examples bellow we used free(lite) versions of GeoIP databases, because fully supported GeoIP databases are not free(you’d have to pay $50USD setup + $12USD update for GeoIP Country and $370USD + $90USD for GeoIP City base). Drawback of lite version is that it is not as accurate as fully supported GeoIP databases, but it is still very usefull and probably good enough for great majority of live projects.

MaxMind offer API for dozen of programming languages (full list is available here), details about PHP API are available here. This tutorial deal with so called “Pure PHP API”, there are also PECL extensions and apache mod_geoip modul available. Apache modul provide better perfomance, but Pure PHP API is easier to set up.

Just for a start let’s download all PHP API files from http://www.maxmind.com/download/geoip/api/php/, and save them somewhere inside of your Web tree(let say /htdocs/geoip). To use GeoIP Country you need to download lite database from here, and for GeoLiteCity download database from here. Just for the sake of simplicity, we are going to unpack both bases to the same dir where we saved our PHP API’s files (/htods/geoip in our example).

GeoIP Country
——————————–
Now, let’s see how country detection works:

<?php
/**
 * GeoIP Country Database Example
 *
 * @version $Id$
 * @package geoip
 * @copyright © 2006 Lampix.net
 * @author Dragan Dinic <dinke@lampix.net>
 */

require_once("geoip.inc");

$gi = geoip_open("GeoIP.dat", GEOIP_STANDARD);

$ip = $_SERVER['REMOTE_ADDR'];
//if you test on localhost use IP bellow for test
//since $_SERVER['REMOTE_ADDR'] would be 127.0.0.1
//$ip = "89.216.226.174";

$country_name = geoip_country_name_by_addr($gi, $ip);
$country_code = geoip_country_code_by_addr($gi, $ip);
if($country_name)
{
	echo "Your country is: $country_name <br />";
	echo "Country Code is: $country_code <br />";
}
else
{
	echo "Sorry, we weren't able to locate you.";
}

geoip_close($gi);
?>

So, at the beggining we’ve included geoip.inc which contains all functions needed to use GeoIP country database, then we’ve created new instance of GeoIP class with geoip_open function, and at the end we called proper functions(geoip_country_name_by_addr and geoip_country_code_by_addr) to get country name/code in which detected IP address reside. Again, in case you test localy, don’t use $_SERVER[‘REMOTE_ADDR’].

When you run script above you should get something like this as output:

Your country is: Serbia and Montenegro
Country Code is: CS

GeoIP City
—————————-
Now, let’s extend visitor’s country data with exact location(like city, postal code etc.)

<?php
/**
 * GeoIP City Database Example
 *
 * @version $Id$
 * @package geoip
 * @copyright © 2006 Lampix.net
 * @author Dragan Dinic <dinke@lampix.net>
 */

require_once("geoipcity.inc");

$gi = geoip_open("GeoLiteCity.dat", GEOIP_STANDARD);

$ip = $_SERVER['REMOTE_ADDR'];
//if you test on localhost use IP bellow for test
//since $_SERVER['REMOTE_ADDR'] would be 127.0.0.1
//$ip = "89.216.226.174";

$record = geoip_record_by_addr($gi, $ip);

if(!$record)
{
	echo "Sorry, we weren't able to locate you.";
}
else
{
	echo "Country: " .$record->country_name . "<br />";
	echo "Country Code: " . $record->country_code . "<br />";
	echo "Country Code 2: " . $record->country_code3 . "<br />";
	echo "Region: " .$record->region . "<br />";
	echo "City: " .$record->city . "<br />";
	echo "Postal Code: " .$record->postal_code . "<br />";
	echo "Latitude: " .$record->latitude . "<br />";
	echo "Longitude: " .$record->longitude . "<br />";
	echo "DMA Code: " .$record->dma_code . "<br />";
	echo "Area Code: " .$record->area_code . "<br />";
}

geoip_close($gi);
?>

As you see, PHP code is simmilar as in our country detection example, with exception that we used geoipcity.inc and GeoLiteCity.dat database. Function geoip_record_by_addr($gi, $ip) return instance of ‘geoiprecord’ class which contains in it’s properties location’s data we used in our example. After you run script you should get output like this one:

Country: Serbia and Montenegro
Country Code: CS
Country Code 2: SCG
Region: 02
City: Beograd
Postal Code: 11000
Latitude: 44.8186
Longitude: 20.4681
DMA Code:
Area Code:

CaseStudy – Redirection depending of Country
————————————————————–
At the end, we are going to see some real GeoIP usage. Our goal is to redirect users on multy language site(blog) to proper language section on the site depending of their location. Here is how code looks like on my own blog:

<?php
/**
 * Case Study - GeoIP Redirection
 *
 * @version $Id$
 * @package geoip
 * @copyright © 2006 Lampix.net
 * @author Dragan Dinic <dinke@lampix.net>
 */

require_once("geoip/geoip.inc");

$gi = geoip_open("geoip/GeoIP.dat",GEOIP_STANDARD);

$country_code = geoip_country_code_by_addr($gi, $_SERVER['REMOTE_ADDR']);

geoip_close($gi);

if($country_code == 'CS')
{
        header("HTTP/1.1 301 Moved Permanently");
        header('Location: http://www.dinke.net/blog/sr/');
}
else
{
        header("HTTP/1.1 301 Moved Permanently");
        header('Location: https://www.dinke.net/blog/en/');
}
?>

Above example is used on this blog in order to redirect all users located out of Serbia to english version of the blog. Sending custom 301 redirection headers is important so bots(like google etc. google) are able to index blog pages without problems.

PHP 5.2 upload progress meter

Yesterday I’ve spent considerable amount of time in order to find out more about the most interesting new PHP 5.2 feature – hook for upload progress meter. Except for this link I haven’t found anything else, no php source code example how to make one.

However, after I looked at internal php mailing list archive, I’ve found this thread, still no php data found, but Rasmus mentioned link with an example at http://progphp.com/progress.php. I immediately tried upload and it looked very cool. Then I looked at html source code and noticed one unusual thing there: APC_UPLOAD_PROGRESS hidden field inside of html source. I knew it must be important so I’ve googled for it, and insteresting enough first result was source code of Rasmus example above 🙂

So, I took complete source (I figured out later it is upload meter example made by Rasmus Lerdorf) and quickly tried to make it working under my fresh new installed PHP 5.2.0. Unfortunately, it didn’t work since it needed apc stuff installed. After I looked at apc documentation, I found that I need to grab it from list of pecl dll’s for windows php 5.2 version. Unfortunately, after I’ve downloaded it from here I’ve noticed apc dll is missing there ?!!

Again, I had to google for php_apc.dll and after a while found needed dll available at http://pecl4win.php.net/ext.php/php_apc.dll. In order to make it working, you have to save dll file under php/ext dir (i.e. c:\php\ext on windows) and put this to php.ini:

extension=php_apc.dll

Unfortunately, it still didn’t work, so I’ve looked at apc docs further. Finally on this page I’ve found apc have new “special feature” which is directly related to our new upload feature.

    apc.rfc1867             RFC1867 File Upload Progress hook handler is only available
                            if you compiled APC against PHP 5.2.0 or later.  When enabled
                            any file uploads which includes a field called
                            APC_UPLOAD_PROGRESS before the file field in an upload form
                            will cause APC to automatically create an upload_
                            user cache entry where  is the value of the
                            APC_UPLOAD_PROGRESS form entry.
                            (Default: 0)

After I figured out on my phpinfo page apc.rfc1867 setting is turned off, I’ve added

apc.rfc1867 = on 

in php.ini, and after restart was finally able to enjoy new fancy upload progress meter 🙂

upload progress meter

Btw, upload also depend of json turned on as well, but it was already turned on so I didn’t have any more problems.

About the code Rasmus used in his example, I am tired to analyze it more now, but obviously it use Yahoo! User Interface Library to create progress bar and json/apc to control it from php during file upload.

I hope this will be helpful for someone. Enjoy 😉

Firefox 2.0 Final

Almost one year after launch of version 1.5, and just a few days after very buggy RC 3, final version of Firefox 2.0 is finally available.

Firefox 2.0

According to release notes, new Firefox contains usability improvements, built-in phishing protection, spell checking, improved tabbed browsing and new JavaScript 1.7 under the hood.

After download (windows installation is 5.6MB) it install smoothly, without any problem with extensions etc.

However, my first impressions are not very good. I simple don’t like new default theme, something simmilar as “Go” button is added next to location bar and search bar, there is no longer close button to the far right side of tab bar, now you have close button added for each tab (like in Opera).

About new features, now firefox is able to remember your session (reopen all tabs you had before you’ve closed it). On my machine it is not enabled by default, but it can be easily turned on in ff options. Also, there is a new spell checker which is enabled by default with english dictionary. Very annoying if you mostly write in non english language, and don’t have dictionary support. Search bar is now able to list you suggestions soon after you star typing your keyword (simmilar as Google Suggest tool).

Regarding new options in menu, I haven’t noticed many changes. In History part there is a “Recently closed tabs” option, very usefull especially if you’ve closed some tab by mistake. Also themes and extensions are now reorganized under “Add-ons” menu, which is part of Tools menu. In Options menu there is a “feeds” tab where you can define how would you like to handle rss feeds, and in case you don’t have any feed reader installed, you can pick some of online readers (like Bloglines, Google Reader etc.).

All in all, I really liked ff 1.5 look and feel much more, but FF 2.0 have some interesting new features. Will give it a chance for a few days and see if I am going to keep it, or revert to goof old FF 1.5.x.x 🙂

Netvibes 2.0

My favorite Web 2.0 application Netvibes just came to second big revision with code name “Cinnamon”.

At first I was disappointed with changes. Although they announced tons of new features, I saw only small changes in user interface and few new modules. However, more changes are about to come, because Cinnamon is actually “a three-month release cycle, where you will see new features and upgrades throughout that time”.

So far I’ve noticed new splash screen, slightly changed interface, and couple of new modules like “Blog Search”, Video Search etc.

Blog Search
Blog Search

Netvibes u Akciji
Netvibes in Action

Why 09/11 Happened to USA

Few days ago I watched on history channel an documentary about Berlin Blockade in 1948. On Jun 24, 1948 Soviets started total blockade of Western part of Berlin, with goal to spread their control over whole city. All communitations were blocked, and citizens of West Berlin were faced with lack of food, medicine and fuel for upcoming winter.

American response was quick, and starting from Jun 25 they launched a massive airlift using both civil and military aircrafts that flew supplies into the Western Berlin. Aircrafts were fly by volunteers, mostly by experienced WWII pilots who flew day and night, sometimes without sleep. Landing on improvised Berlin airport was very hard (especially during bad weather) – many brave pilots died during this mission.

One of the pilots involved in this mission was Gail Halvorsen. One day during pause between two flights he gave a few sticks of chewing gum to some children watching the planes from outside the base. Wanting to give them more, he promised to drop more candy from his plane the next day. And really, during next flight, soon before landing Halvorsen dropped candy attached to parachutes to children below.

His actions were soon noticed by the press and gained widespread attention. A wave of public support led to donations which enabled Halvorsen and his crew to drop 850 pounds of candy. By the end of the airlift, around 25 plane crews had dropped 23 tons of chocolate, chewing gum, and other candies over various places in Berlin. The action may have had a substantial impact on the postwar perception of Americans in Germany, and it is still pointed to as a symbol of German-American relations.

Yesterday was 09/11, 5 years after terrorist attack on United States. I see many people are looking for cause, why that happend to USA, why USA was picked as a target and why USA is no longer an icon of freedom in modern world.

Not sure about all causes, but I am sure that one of the reasons is that US advocated their freedom in Vietnam, Cambodia, Iraq, Serbia etc. with bombs and not with candies.

Spam Carma 2

If you run Word Press based blog, and getting a lot of spam comments on it, this plugin is right for you.

Unlike other spam killers (like Akismet plugin which behave badly with non english comments), Spam Carma 2 kills 100% of spam, and never have a problems with regular comments.

Spam Carma 2 contains several spam filters, where restriction for each of them can be set to higher or lower level. For example, there is a Stopwatch which make sure commenter has been on page for a certain number of seconds before commenting, Javascript Payload which embed a few Javascript commands in comment form (users with JS turned off only receives a small penalty) and plenty of other stuff like captcha check which is displayed only if commenter is not recognized as normal user.

Since I’ve installed it, Spam Carma killed more than 300 spam commenters without actually stopping any regular comment!

Curl HTTP Client

Frequently, in my daytime job I have to fetch data from various url’s, either by sending get or post request, binding to different IP address etc. Long time I used my own socket based HTTP class, although I wasn’t quite happy with perfomances and various other things with it. I already used curl cli tool (mostly for debugging purposes), but didn’t really liked it’s php api, so I’ve decided to take some spare time and make some kind of oop wrapper for it, which should be easier to use for easy stuff like sending get/post request etc.

I use this class several months since then, and it evolved over time whenever I needed some new feature. Since various of my colleagues found it very usefull (some of them even sent me new methods for it), I’ve decided to put it out for public. Recently I submitted a code to phpclasses.org, and today got confirmation that class is officially approved.

Update 03/01/2007
New version 1.1 released with new features: fetch into file, upload, proxy etc.

Update 15/02/2008
New version 1.2 released with few bug fixes. New features are ability to send post string as string argument in send_post_data method, ability to accept gzipped content, close curl session etc.

Update 25/03/2013
Version 2.0 released. Pretty much the same features as old versions, but with updated code in order to match latest PHP changes. Code will be regularly updated on github.

You can download class and example files directly from gitgub https://github.com/dinke/curl_http_client

Here are few usage examples.

<?php
/**
 * @version 2.0
 * @package dinke.net
 * @copyright © 2013 Lampix.net
 * @author Dragan Dinic 
 */

require_once("curl_http_client.php");

$curl = new Curl_HTTP_Client();

//pretend to be Firefox 19.0 on Mac
$useragent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0";
$curl->set_user_agent($useragent);

//uncomment next two lines if you want to automatically manage cookies
//which will store them to file and send them on each http request
//$cookies_file = "/tmp/cookies.txt";
//$curl->store_cookies($cookies_file);

//Uncomment next line if you want to set credentials for basic http_auth
//$curl->set_credentials($username, $password);

//Uncomment next line if you want to set specific referrer
//$curl->set_referer('http://www.foo.com/referer_url/');

//if you want to send some post data
//form post data array like this one
$post_data = array('login' => 'pera', 'password' => 'joe', 'other_foo_field' => 'foo_value');
//or like a string: $post_data = 'login=pera&password=joe&other_foo_field=foo_value';

//and send request to http://www.foo.com/login.php. Result page is stored in $html_data string
$html_data = $curl->send_post_data("http://www.foo.com/login.php", $post_data);

//You can also fetch data from somewhere using get method!
//Fetch html from url
$html_data = $curl->fetch_url("http://www.foo.com/foobar.php?login=pera&password=joe&other_foo_field=foo_value");

//if you have more than one IP on your server,
//you can also bind to specific IP address like ...
//$bind_ip = "192.168.0.1";
//$curl->fetch_url("http://www.foo.com/login.php", $bind_ip);
//$html_data = $curl->send_post_data("http://www.foo.com/login.php", $post_data, $bind_ip);

//and there are many other things you can do like

//use proxy
//$curl->set_proxy('http://www.proxyurl.com');

//get http response code for last request
//$http_code = $curl->get_http_response_code();

//get last http request duration in sec
//$duration = $curl->get_request_duration();
?>

Google Page Creator

Don’t know about you, but I have difficulties to follow up with all services Google announce almost each day. GMail, GMap, GTalk, GCal, GSpreadsheets, … and lately we got Google Page Creator.

Google Page Creator is another one Web 2.0 application, which allow us to create Web Pages in a snap. Like with others simmilar tools, you don’t have to know html or any other technology, all you need is your browser and little creativity. Unlike other tools Page Creator is very easy to use (thanks to very good Ajax implementation), so in as little as 5 minutes you can come up with your new Web Site. Such site will be indexed by google shortly, and will be hosted free on address like: http://yourgoogleaccount.googlepages.com.

You can see results of my 5 minute Page Creatoru session here 🙂