Jul 21

Simple GeoTarget PHP Library

Posted by Jaimie Sirovich on Jul. 21st, 2006. 4 comments — voice your opinion.

NEED A GREAT WEB SITE? NEED IT TO BE SEARCH-ENGINE-FRIENDLY?

SEO Egghead is a web development firm dedicated to creating custom, search engine optimized web site applications. We specialize in eCommerce and content management web sites that not only render information beautifully to the human, but also satisfy the "third browser" — the search engine. To us, search engines are people too. Click here to talk to us. We'd love to help!
X

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: ...
A (not so simple) method to add rel="nofollow" to links I wrote this script so that I can run it...
Social Bookmarking PHP Library Many of us use social bookmarking sites — del.icio.us, digg,...
Google Spiders (Very) Simple Forms I used to assume that content behind forms was never...
The Proper Procedure for Changing Hosting Companies (It's really not always so simple … ) Should the need exist to change hosting companies, the process...




"4 Wise Comments Banged Out Somewhere On The Internet ..."


rxbbx

Nice for multilangual things. Thnx.

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

[...] 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 [...]

Simple GeoTarget PHP Library at Sandboxing

[...] 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 [...]

Uwe tippt. » GeoTargeting und so

[...] 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… [...]



Care To Bang On The Keys ... ?

BECOME AN EGGHEAD. SUBSCRIBE TO OUR RSS FEED!

Learn to be as nerdy as we are by never missing our latest blog entries. Receive great tips, tricks, and ideas on improving your web site every day! Subscribe via our RSS Feed or use the chicklets in the sidebar.