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.';
}

?>

Tell an amigo:
  • Sphinn
  • Digg
  • Reddit
  • del.icio.us
  • StumbleUpon
  • Facebook



Related posts:
Simple Cloak PHP Library Tell an amigo: ...
Social Bookmarking PHP Library Many of us use social bookmarking sites — del.icio.us, digg,...
A (not so simple) method to add rel="nofollow" to links I wrote this script so that I can run it...
SimpleCloak v2 PHP Implementation Note: I didn't realize WP changes quotes to curly quotes...
Yahoo API Code <?php /* sample usage: $tree = new xmlTreeParser('sample.xml'); echo '<pre>'; print_r($tree->getTree()); */ class xmlTreeParser {     var $_parser;     var $_xmldata;...