SEO Egghead by Jaimie Sirovich: A blog about SEO, written for nerds, by a nerd.

Choose a Topic:

» Suggest a topic or buzz to cover; if I write about it, you'll get credit with a link in the post!

Fri
21
Jul '06

Simple GeoTarget PHP Library

This class can be used to detect where a site visitor is located and tailor their page content accordingly.  For example, you may want to say "We surrender!" for French users, instead of the usual "Hello."  No offense to Frenchies of course; but I had to poke some fun to make this post less dry :)

To do this, we need to include this class in our application; the code below the class definition implements the aforementioned example.  Many thanks to MaxMind for providing their free database; additionally, they have a better, more accurate version for a fee.  Keep in mind that although this code automatically updates only after 30 days (not every time you call the "updateDB()" function, it downloads a rather large file.  This will cause a delay for that first user who hits your site after 30 days.  You are better off cronning the job instead.  In any case -- happy programming!

<?

/*
// +----------------------------------------------------------------------+
// | SimpleGeoTarget                                                      |
// | Class for targeting content for specific geographical regions        |
// | http://www.SEOEgghead.com/                                           |
// +----------------------------------------------------------------------+
// | Copyright (c) 2004-2006 Jaimie Sirovich                              |
// +----------------------------------------------------------------------+
// | This program is free software; you can redistribute it and/or        |
// | modify it under the terms of the GNU General Public License          |
// | as published by the Free Software Foundation; either version 2       |
// | of the License, or (at your option) any later version.               |
// |                                                                      |
// | This program is distributed in the hope that it will be useful,      |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        |
// | GNU General Public License for more details.                         |
// |                                                                      |
// | You should have received a copy of the GNU General Public License    |
// | along with this program; if not, write to the Free Software          |
// | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA        |
// +----------------------------------------------------------------------+
// | Author: Jaimie Sirovich <jsirovic@gmail.com>                         |
// +----------------------------------------------------------------------+

SCHEMA:
CREATE TABLE `simple_geo_target` (
  `id` int(11) NOT NULL auto_increment,
  `start_ip_text` varchar(15) collate latin1_general_ci NOT NULL default '',
  `end_ip_text` varchar(15) collate latin1_general_ci NOT NULL default '',
  `start_ip_numeric` bigint(20) NOT NULL default '0',
  `end_ip_numeric` bigint(20) NOT NULL default '0',
  `country_code` char(2) collate latin1_general_ci NOT NULL default '',
  `country_name` varchar(50) collate latin1_general_ci NOT NULL default '',
  `date_updated` date default NULL,
  PRIMARY KEY  (`id`),
  KEY `start_ip_numeric` (`start_ip_numeric`,`end_ip_numeric`),
  KEY `country_code` (`country_code`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
*/

class SimpleGeoTarget
{

    function insert($info)
    {
        if (
class_exists('DB')) {
            return 
DB::insert('simple_geo_target'$info);
        } else {
            
$table_name 'simple_geo_target';    
                
            
$names = array();
            
$values = array();
    
            foreach (
$info as $name => $value) {
                
$names[] = $name;
                
$values[] = "'" mysql_real_escape_string($value) . "'";
            }
    
            
$implode_names implode(","$names);
            
$implode_values implode(","$values);
    
            
$q  " INSERT INTO " $table_name " ($implode_names) VALUES ($implode_values) ";
    
            
$tmp mysql_query($q);            
            
            if (
$tmp) {
                return (
$id mysql_insert_id()) ? $id false;
            } else {
                return 
false;
            }            
        }
    }
    
    function 
update($id$info)
    {
        if (
class_exists('DB')) {        
            return 
DB::update('simple_geo_target'$id$info);
        } else {
            
// We don't use update here, so I won't do this one.
        
}
    }
    
    function 
delete($id$key_name 'id')
    {
        if (
class_exists('DB')) {                
            return 
DB::delete('simple_geo_target'$id$key_name);
        } else {
            
$table_name 'simple_geo_target';
            
            if (!
$id) return false;
    
            
$q " DELETE FROM " $table_name " WHERE ";
            
$q .= $key_name ' = ' "'" mysql_real_escape_string($id) . "'";
            
            
$tmp mysql_query($q);
            return 
mysql_affected_rows();
        }
    }

    function purgeWholeTable()
    {
        if (
class_exists('DB')) {                
            return 
DB::purgeWholeTable('simple_geo_target');
        } else {
            
$table_name 'simple_geo_target';            
            
$q " DELETE FROM " $table_name;
            
$tmp mysql_query($q);
            return 
mysql_affected_rows();
        }
    }    
    
    function 
getOldestDate()
    {
        
$q " SELECT MIN(date_updated) AS `0` FROM simple_geo_target ";
        if (
class_exists('DB')) {                
            return 
DB::queryOneVal($q);
        } else {
            
$tmp mysql_fetch_assoc(mysql_query($q));
            return 
$tmp[0];
        }    
    }    
    
    function 
get($id 0$start_ip_text ''$end_ip_text ''$start_ip_numeric 0$end_ip_numeric 0$country_code ''$country_name '',
   
$test_ip_value ''$date_updated_begin ''$date_updated_end ''$order ''$order_asc ''$hash_col '')
    {
        
        if (
class_exists('DB')) {
        
            
$q " SELECT simple_geo_target.* FROM simple_geo_target WHERE TRUE ";
            
            if (
$id)
              
$q .= DB::cond('id'$id);
                              
            if (
$start_ip_text)
              
$q .= DB::cond('start_ip_text'$start_ip_text);
    
            if (
$end_ip_text)  
              
$q .= DB::cond('end_ip_text'$end_ip_text);   
            
            if (
$start_ip_numeric)
              
$q .= DB::cond('start_ip_numeric'$start_ip_numeric);
    
            if (
$end_ip_numeric)  
              
$q .= DB::cond('end_ip_numeric'$end_ip_numeric);   

            if ($country_code)  
              
$q .= DB::cond('country_code'$country_code);   

            if ($country_name)  
              
$q .= DB::cond('country_name'$country_name);   
                
            if (
$test_ip_value) {
              
$test_ip_value ip2long($test_ip_value);
              
$q .= ' AND ( ' ' start_ip_numeric ' ' <= ' $test_ip_value ' AND ' ' end_ip_numeric ' ' >= ' $test_ip_value ' ) ';
            }
              
            
$q .= DB::queryDateRange('date_updated'$date_updated_begin$date_updated_end);
              
            
$q .= DB::orderBy($order$order_asc);
    
            return 
DB::queryRows($q$hash_col); 
            
        } else {
            
            
$q " SELECT simple_geo_target.* FROM simple_geo_target WHERE TRUE ";
            
            if (
$id) {
                
$id = (int) $id;
                
$q .= " AND id = $id ";
            }
            
            if (
$start_ip_text) {
                
$start_ip_text mysql_real_escape_string($start_ip_text);
                
$q .= " AND start_ip_text = '$start_ip_text' ";
            }
            
            if (
$end_ip_text) {
                
$end_ip_text mysql_real_escape_string($end_ip_text);
                
$q .= " AND end_ip_text = '$end_ip_text' ";
            }
              
            if (
$start_ip_numeric) {
                
$start_ip_numeric = (int) $start_ip_numeric;
                
$q .= " AND start_ip_numeric = $start_ip_numeric ";
            }
    
            if (
$end_ip_numeric) {
                
$end_ip_numeric = (int) $end_ip_numeric;
                
$q .= " AND end_ip_numeric = $end_ip_numeric ";
            }

            if ($country_code) {
                
$country_code mysql_real_escape_string($country_code);
                
$q .= " AND country_code = '$country_code' ";
            }

            if ($country_name) {
                
$country_name mysql_real_escape_string($country_name);
                
$q .= " AND country_name = '$country_name' ";
            }
                
            if (
$test_ip_value) {
              
$test_ip_value sprintf("%u"ip2long($test_ip_value));
              
$q .= ' AND ( ' ' start_ip_numeric ' ' <= ' $test_ip_value ' AND ' ' end_ip_numeric ' ' >= ' $test_ip_value ' ) ';
            }
            
            
// the rest is unimplemented, because we don't use it.
            
            
$tmp mysql_query($q);
            
            
$rows = array();
            while (
$_x mysql_fetch_assoc($tmp)) {
                
$rows[] = $_x;
            }
            
            return 
$rows;

        }
    }
    
    function get1($id)
    {
        if (
class_exists('DB')) {
            return 
DB::get1(SimpleGeoTarget::get($id));
        } else {
            
$tmp SimpleGeoTarget::get($id);
            return 
current($tmp);
        }
    }    
                
    function 
updateDB($do_duration_check true$duration '-10 days'$last_check_filename '')
    {
        
// Do not set $do_duration_check to false unless you are cronning it!
        
if ($do_duration_check) {
            
            if (!
$last_check_filename) {
                
$last_check_filename '/tmp/_simple_geo_target_' md5($_SERVER['HTTP_HOST']);    
            }
            
            
$last_check = @file_get_contents($last_check_filename);
            
        }
        
        if (!
$do_duration_check || ($last_check strtotime($duration))) {    
                
            
SimpleGeoTarget::_importDB(SimpleGeoTarget::_getGeoDB());
            
            
$f fopen($last_check_filename'w');
            
            if (!
fputs($ftime())) {
                echo 
'ERROR: Cannot write to SimpleGeoTarget last check file ' $last_check_filename ' in current working directory of ' getcwd();
                exit();
            }
                        
        }
    }
    
    function 
getRegion($ip)
    {
        
$tmp SimpleGeoTarget::get(0''''00''''$ip);
        if (
class_exists('DB')) {
            return 
DB::get1($tmp);
        } else {
            return 
current($tmp);
        }
    }    
    
    function 
isRegion($country_code$current_ip '')
    {
        if (!
$current_ip) {
            
$current_ip $_SERVER['REMOTE_ADDR'];
        }
        
$tmp SimpleGeoTarget::getRegion($current_ip);
        return (
$tmp['country_code'] == $country_code);
    }
    
    function 
_getGeoDB()
    {
        
ini_set("memory_limit""100M");
        
$tmp_filename tempnam('/tmp''GEO_');
        
$tmp_file_handle fopen($tmp_filename'w+');
        
fwrite($tmp_file_handlefile_get_contents('http://www.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip'));
        
$zip zip_open($tmp_filename);
        
        if (
$zip) {
        
            
$zip_entry zip_read($zip);
            if (
zip_entry_open($zip$zip_entry"r")) {
                
$buf zip_entry_read($zip_entryzip_entry_filesize($zip_entry));  
            } else {
                return 
false;
            }
            
        }
        
        
zip_close($zip);
        
fclose($tmp_file_handle);
        
unlink($tmp_filename);
        return 
$buf;
    }
    
    function 
_importDB($buf)
    {        
        
ini_set("memory_limit""100M");
        
$tmp_filename tempnam('/tmp''GEO_');
        
$tmp_file_handle fopen($tmp_filename'w+');
        
fwrite($tmp_file_handle$buf);        
        
rewind($tmp_file_handle);
        
        if (
class_exists('DB')) {
            
DB::query('LOCK TABLES simple_geo_target WRITE');
        } else {
            
mysql_query('LOCK TABLES simple_geo_target WRITE');
        }        
        
        
SimpleGeoTarget::purgeWholeTable();
        
        
$update_time = (class_exists('DB') ? DB::unixToSQL(time()) : date('Y-m-d'time()));
        while ((
$data fgetcsv($tmp_file_handle10000",")) !== false) {
            
SimpleGeoTarget::insert(array('start_ip_text' => $data[0], 'end_ip_text' => $data[1], 'start_ip_numeric' => $data[2],
            
'end_ip_numeric' => $data[3], 'country_code' => $data[4], 'country_name' => $data[5], 'date_updated' => $update_time));
        }
        
        if (
class_exists('DB')) {
            
DB::query('UNLOCK TABLES');
        } else {
            
mysql_query('UNLOCK TABLES');
        }        
        
        
fclose($tmp_file_handle);
        
unlink($tmp_filename);
    }
    
}

$_SERVER['REMOTE_ADDR'] = '160.92.103.98';
SimpleGeoTarget::updateDB();

if (SimpleGeoTarget::isRegion('FR')) {
    echo 
'We surrender!';
} else {
    echo 
'Hello.';
}

?>

These icons link to social bookmarking sites where readers can share and discover new web pages.
  • del.icio.us
  • digg
  • Furl
  • Reddit
  E-Mail This Post/Page

4 Responses to “Simple GeoTarget PHP Library”

  1. rxbbx Says:

    Nice for multilangual things. Thnx.

  2. » Increase AdSense Revenue by 800% in Just 10 Minutes - Part 2 » $250/Day » Blog Archive Says:

    [...] While looking for ways to improve on the Translation Gold script I found a nice little Geo Targeting PHP script that (with a little tweaking), when used in conjunction with Translation Gold, allows you to automatically offer your site in the relevant language of the person viewing your site based on their country of origin [...]

  3. Simple GeoTarget PHP Library at Sandboxing Says:

    [...] SEO Egghead » Blog Archive » Simple GeoTarget PHP Library - This class can be used to detect where a site visitor is located and tailor their page content accordingly. For example, you may want to say “We surrender!” for French users, instead of the usual “Hello.” No offense to Frenchies of course; but I had to poke some fun to make this post less dry [...]

  4. Uwe tippt. » GeoTargeting und so Says:

    [...] Brauch man ja immer mal - etwas Cloaking, um den Engländer vom Deutschen anhand der IP zu unterscheiden. Mit waaas man file_get_contents regelmäsig füttert, bleibt ja jedem selbst überlassen… [...]

Leave a Reply