From 735b8252ead3d1e4d39f3242e6584a1dd4abb92a Mon Sep 17 00:00:00 2001 From: Petr Skoda Date: Tue, 28 Sep 2010 13:19:13 +0000 Subject: [PATCH] fixed incorrect line endings --- lib/pear/Net/GeoIP.php | 1808 +++++++++++++++---------------- lib/pear/Net/GeoIP/DMA.php | 628 +++++------ lib/pear/Net/GeoIP/Location.php | 402 +++---- 3 files changed, 1419 insertions(+), 1419 deletions(-) diff --git a/lib/pear/Net/GeoIP.php b/lib/pear/Net/GeoIP.php index f2cb3e75fe7..f3c48c69f16 100644 --- a/lib/pear/Net/GeoIP.php +++ b/lib/pear/Net/GeoIP.php @@ -1,904 +1,904 @@ - (original Maxmind version) | - * | Hans Lellelid | - * +----------------------------------------------------------------------+ - * - * @category Net - * @package Net_GeoIP - * @author Jim Winstead (original Maxmind PHP API) - * @author Hans Lellelid - * @license LGPL http://www.gnu.org/licenses/lgpl.txt - * @link http://pear.php.net/package/Net_GeoIp - * $Id$ - */ - -require_once 'PEAR/Exception.php'; - -/** - * GeoIP class provides an API for performing geo-location lookups based on IP - * address. - * - * To use this class you must have a [binary version] GeoIP database. There is - * a free GeoIP country database which can be obtained from Maxmind: - * {@link http://www.maxmind.com/app/geoip_country} - * - * - * SIMPLE USE - * - * - * Create an instance: - * - * - * $geoip = Net_GeoIP::getInstance('/path/to/geoipdb.dat', Net_GeoIP::SHARED_MEMORY); - * - * - * Depending on which database you are using (free, or one of paid versions) - * you must use appropriate lookup method: - * - * - * // for free country db: - * $country_name = $geoip->lookupCountryName($_SERVER['REMOTE_ADDR']); - * $country_code = $geoip->lookupCountryCode($_SERVER['REMOTE_ADDR']); - * - * // for [non-free] region db: - * list($ctry_code, $region) = $geoip->lookupRegion($_SERVER['REMOTE_ADDR']); - * - * // for [non-free] city db: - * $location = $geoip->lookupLocation($_SERVER['REMOTE_ADDR']); - * print "city: " . $location->city . ", " . $location->region; - * print "lat: " . $location->latitude . ", long: " . $location->longitude; - * - * // for organization or ISP db: - * $org_or_isp_name = $geoip->lookupOrg($_SERVER['REMOTE_ADDR']); - * - * - * - * MULTIPLE INSTANCES - * - * - * You can have several instances of this class, one for each database file - * you are using. You should use the static getInstance() singleton method - * to save on overhead of setting up database segments. Note that only one - * instance is stored per filename, and any flags will be ignored if an - * instance already exists for the specifiedfilename. - * - * Special note on using SHARED_MEMORY flag - * - * If you are using SHARED_MEMORY (shmop) you can only use SHARED_MEMORY for - * one (1) instance (i.e. for one database). Any subsequent attempts to - * instantiate using SHARED_MEMORY will read the same shared memory block - * already initialized, and therefore will cause problems since the expected - * database format won't match the database in the shared memory block. - * - * Note that there is no easy way to flag "nice errors" to prevent attempts - * to create new instances using SHARED_MEMORY flag and it is also not posible - * (in a safe way) to allow new instances to overwrite the shared memory block. - * - * In short, is you are using multiple databses, use the SHARED_MEMORY flag - * with care. - * - * - * LOOKUPS ON HOSTNAMES - * - * - * Note that this PHP API does NOT support lookups on hostnames. This is so - * that the public API can be kept simple and so that the lookup functions - * don't need to try name lookups if IP lookup fails (which would be the only - * way to keep the API simple and support name-based lookups). - * - * If you do not know the IP address, you can convert an name to IP very - * simply using PHP native functions or other libraries: - * - * - * $geoip->lookupCountryName(gethostbyname('www.sunset.se')); - * - * - * Or, if you don't know whether an address is a name or ip address, use - * application-level logic: - * - * - * if (ip2long($ip_or_name) === false) { - * $ip = gethostbyname($ip_or_name); - * } else { - * $ip = $ip_or_name; - * } - * $ctry = $geoip->lookupCountryName($ip); - * - * - * @category Net - * @package Net_GeoIP - * @author Jim Winstead (original Maxmind PHP API) - * @author Hans Lellelid - * @license LGPL http://www.gnu.org/licenses/lgpl.txt - * @link http://pear.php.net/package/Net_GeoIp - */ -class Net_GeoIP -{ - /** - * Exception error code used for invalid IP address. - */ - const ERR_INVALID_IP = 218624992; // crc32('Net_GeoIP::ERR_INVALID_IP') - - /** - * Exception error code when there is a DB-format-related error. - */ - const ERR_DB_FORMAT = 866184008; // crc32('Net_GeoIP::ERR_DB_FORMAT') - - public static $COUNTRY_CODES = array( - "", "AP", "EU", "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AN", "AO", "AQ", - "AR", "AS", "AT", "AU", "AW", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", - "BI", "BJ", "BM", "BN", "BO", "BR", "BS", "BT", "BV", "BW", "BY", "BZ", "CA", - "CC", "CD", "CF", "CG", "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", "CU", - "CV", "CX", "CY", "CZ", "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", - "EH", "ER", "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR", "FX", "GA", "GB", - "GD", "GE", "GF", "GH", "GI", "GL", "GM", "GN", "GP", "GQ", "GR", "GS", "GT", - "GU", "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", "ID", "IE", "IL", "IN", - "IO", "IQ", "IR", "IS", "IT", "JM", "JO", "JP", "KE", "KG", "KH", "KI", "KM", - "KN", "KP", "KR", "KW", "KY", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", - "LT", "LU", "LV", "LY", "MA", "MC", "MD", "MG", "MH", "MK", "ML", "MM", "MN", - "MO", "MP", "MQ", "MR", "MS", "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", - "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", "NR", "NU", "NZ", "OM", "PA", - "PE", "PF", "PG", "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", "PW", "PY", - "QA", "RE", "RO", "RU", "RW", "SA", "SB", "SC", "SD", "SE", "SG", "SH", "SI", - "SJ", "SK", "SL", "SM", "SN", "SO", "SR", "ST", "SV", "SY", "SZ", "TC", "TD", - "TF", "TG", "TH", "TJ", "TK", "TM", "TN", "TO", "TL", "TR", "TT", "TV", "TW", - "TZ", "UA", "UG", "UM", "US", "UY", "UZ", "VA", "VC", "VE", "VG", "VI", "VN", - "VU", "WF", "WS", "YE", "YT", "RS", "ZA", "ZM", "ME", "ZW", "A1", "A2", "O1", - "AX", "GG", "IM", "JE", "BL", "MF" - ); - - public static $COUNTRY_CODES3 = array( - "","AP","EU","AND","ARE","AFG","ATG","AIA","ALB","ARM","ANT","AGO","AQ","ARG", - "ASM","AUT","AUS","ABW","AZE","BIH","BRB","BGD","BEL","BFA","BGR","BHR","BDI", - "BEN","BMU","BRN","BOL","BRA","BHS","BTN","BV","BWA","BLR","BLZ","CAN","CC", - "COD","CAF","COG","CHE","CIV","COK","CHL","CMR","CHN","COL","CRI","CUB","CPV", - "CX","CYP","CZE","DEU","DJI","DNK","DMA","DOM","DZA","ECU","EST","EGY","ESH", - "ERI","ESP","ETH","FIN","FJI","FLK","FSM","FRO","FRA","FX","GAB","GBR","GRD", - "GEO","GUF","GHA","GIB","GRL","GMB","GIN","GLP","GNQ","GRC","GS","GTM","GUM", - "GNB","GUY","HKG","HM","HND","HRV","HTI","HUN","IDN","IRL","ISR","IND","IO", - "IRQ","IRN","ISL","ITA","JAM","JOR","JPN","KEN","KGZ","KHM","KIR","COM","KNA", - "PRK","KOR","KWT","CYM","KAZ","LAO","LBN","LCA","LIE","LKA","LBR","LSO","LTU", - "LUX","LVA","LBY","MAR","MCO","MDA","MDG","MHL","MKD","MLI","MMR","MNG","MAC", - "MNP","MTQ","MRT","MSR","MLT","MUS","MDV","MWI","MEX","MYS","MOZ","NAM","NCL", - "NER","NFK","NGA","NIC","NLD","NOR","NPL","NRU","NIU","NZL","OMN","PAN","PER", - "PYF","PNG","PHL","PAK","POL","SPM","PCN","PRI","PSE","PRT","PLW","PRY","QAT", - "REU","ROU","RUS","RWA","SAU","SLB","SYC","SDN","SWE","SGP","SHN","SVN","SJM", - "SVK","SLE","SMR","SEN","SOM","SUR","STP","SLV","SYR","SWZ","TCA","TCD","TF", - "TGO","THA","TJK","TKL","TLS","TKM","TUN","TON","TUR","TTO","TUV","TWN","TZA", - "UKR","UGA","UM","USA","URY","UZB","VAT","VCT","VEN","VGB","VIR","VNM","VUT", - "WLF","WSM","YEM","YT","SRB","ZAF","ZMB","MNE","ZWE","A1","A2","O1", - "ALA","GGY","IMN","JEY","BLM","MAF" - ); - - public static $COUNTRY_NAMES = array( - "", "Asia/Pacific Region", "Europe", "Andorra", "United Arab Emirates", - "Afghanistan", "Antigua and Barbuda", "Anguilla", "Albania", "Armenia", - "Netherlands Antilles", "Angola", "Antarctica", "Argentina", "American Samoa", - "Austria", "Australia", "Aruba", "Azerbaijan", "Bosnia and Herzegovina", - "Barbados", "Bangladesh", "Belgium", "Burkina Faso", "Bulgaria", "Bahrain", - "Burundi", "Benin", "Bermuda", "Brunei Darussalam", "Bolivia", "Brazil", - "Bahamas", "Bhutan", "Bouvet Island", "Botswana", "Belarus", "Belize", - "Canada", "Cocos (Keeling) Islands", "Congo, The Democratic Republic of the", - "Central African Republic", "Congo", "Switzerland", "Cote D'Ivoire", "Cook Islands", - "Chile", "Cameroon", "China", "Colombia", "Costa Rica", "Cuba", "Cape Verde", - "Christmas Island", "Cyprus", "Czech Republic", "Germany", "Djibouti", - "Denmark", "Dominica", "Dominican Republic", "Algeria", "Ecuador", "Estonia", - "Egypt", "Western Sahara", "Eritrea", "Spain", "Ethiopia", "Finland", "Fiji", - "Falkland Islands (Malvinas)", "Micronesia, Federated States of", "Faroe Islands", - "France", "France, Metropolitan", "Gabon", "United Kingdom", - "Grenada", "Georgia", "French Guiana", "Ghana", "Gibraltar", "Greenland", - "Gambia", "Guinea", "Guadeloupe", "Equatorial Guinea", "Greece", "South Georgia and the South Sandwich Islands", - "Guatemala", "Guam", "Guinea-Bissau", - "Guyana", "Hong Kong", "Heard Island and McDonald Islands", "Honduras", - "Croatia", "Haiti", "Hungary", "Indonesia", "Ireland", "Israel", "India", - "British Indian Ocean Territory", "Iraq", "Iran, Islamic Republic of", - "Iceland", "Italy", "Jamaica", "Jordan", "Japan", "Kenya", "Kyrgyzstan", - "Cambodia", "Kiribati", "Comoros", "Saint Kitts and Nevis", "Korea, Democratic People's Republic of", - "Korea, Republic of", "Kuwait", "Cayman Islands", - "Kazakstan", "Lao People's Democratic Republic", "Lebanon", "Saint Lucia", - "Liechtenstein", "Sri Lanka", "Liberia", "Lesotho", "Lithuania", "Luxembourg", - "Latvia", "Libyan Arab Jamahiriya", "Morocco", "Monaco", "Moldova, Republic of", - "Madagascar", "Marshall Islands", "Macedonia", - "Mali", "Myanmar", "Mongolia", "Macau", "Northern Mariana Islands", - "Martinique", "Mauritania", "Montserrat", "Malta", "Mauritius", "Maldives", - "Malawi", "Mexico", "Malaysia", "Mozambique", "Namibia", "New Caledonia", - "Niger", "Norfolk Island", "Nigeria", "Nicaragua", "Netherlands", "Norway", - "Nepal", "Nauru", "Niue", "New Zealand", "Oman", "Panama", "Peru", "French Polynesia", - "Papua New Guinea", "Philippines", "Pakistan", "Poland", "Saint Pierre and Miquelon", - "Pitcairn Islands", "Puerto Rico", "Palestinian Territory", - "Portugal", "Palau", "Paraguay", "Qatar", "Reunion", "Romania", - "Russian Federation", "Rwanda", "Saudi Arabia", "Solomon Islands", - "Seychelles", "Sudan", "Sweden", "Singapore", "Saint Helena", "Slovenia", - "Svalbard and Jan Mayen", "Slovakia", "Sierra Leone", "San Marino", "Senegal", - "Somalia", "Suriname", "Sao Tome and Principe", "El Salvador", "Syrian Arab Republic", - "Swaziland", "Turks and Caicos Islands", "Chad", "French Southern Territories", - "Togo", "Thailand", "Tajikistan", "Tokelau", "Turkmenistan", - "Tunisia", "Tonga", "Timor-Leste", "Turkey", "Trinidad and Tobago", "Tuvalu", - "Taiwan", "Tanzania, United Republic of", "Ukraine", - "Uganda", "United States Minor Outlying Islands", "United States", "Uruguay", - "Uzbekistan", "Holy See (Vatican City State)", "Saint Vincent and the Grenadines", - "Venezuela", "Virgin Islands, British", "Virgin Islands, U.S.", - "Vietnam", "Vanuatu", "Wallis and Futuna", "Samoa", "Yemen", "Mayotte", - "Serbia", "South Africa", "Zambia", "Montenegro", "Zimbabwe", - "Anonymous Proxy","Satellite Provider","Other", - "Aland Islands","Guernsey","Isle of Man","Jersey","Saint Barthelemy","Saint Martin" - ); - - // storage / caching flags - const STANDARD = 0; - const MEMORY_CACHE = 1; - const SHARED_MEMORY = 2; - - // Database structure constants - const COUNTRY_BEGIN = 16776960; - const STATE_BEGIN_REV0 = 16700000; - const STATE_BEGIN_REV1 = 16000000; - - const STRUCTURE_INFO_MAX_SIZE = 20; - const DATABASE_INFO_MAX_SIZE = 100; - const COUNTRY_EDITION = 106; - const REGION_EDITION_REV0 = 112; - const REGION_EDITION_REV1 = 3; - const CITY_EDITION_REV0 = 111; - const CITY_EDITION_REV1 = 2; - const ORG_EDITION = 110; - const SEGMENT_RECORD_LENGTH = 3; - const STANDARD_RECORD_LENGTH = 3; - const ORG_RECORD_LENGTH = 4; - const MAX_RECORD_LENGTH = 4; - const MAX_ORG_RECORD_LENGTH = 300; - const FULL_RECORD_LENGTH = 50; - - const US_OFFSET = 1; - const CANADA_OFFSET = 677; - const WORLD_OFFSET = 1353; - const FIPS_RANGE = 360; - - // SHMOP memory address - const SHM_KEY = 0x4f415401; - - /** - * @var int - */ - private $flags = 0; - - /** - * @var resource - */ - private $filehandle; - - /** - * @var string - */ - private $memoryBuffer; - - /** - * @var int - */ - private $databaseType; - - /** - * @var int - */ - private $databaseSegments; - - /** - * @var int - */ - private $recordLength; - - /** - * The memory addr "id" for use with SHMOP. - * @var int - */ - private $shmid; - - /** - * Support for singleton pattern. - * @var array - */ - private static $instances = array(); - - /** - * Construct a Net_GeoIP instance. - * You should use the getInstance() method if you plan to use multiple databases or - * the same database from several different places in your script. - * - * @param string $filename Path to binary geoip database. - * @param int $flags Flags - * - * @see getInstance() - */ - public function __construct($filename = null, $flags = null) - { - if ($filename !== null) { - $this->open($filename, $flags); - } - // store the instance, so that it will be returned by a call to - // getInstance() (with the same db filename). - self::$instances[$filename] = $this; - } - - /** - * Calls the close() function to free any resources. - * @see close() - * - * COMMENTED OUT TO ADDRESS BUG IN PHP 5.0.4, 5.0.5dev. THIS RESOURCE - * SHOULD AUTOMATICALLY BE FREED AT SCRIPT CLOSE, SO A DESTRUCTOR - * IS A GOOD IDEA BUT NOT NECESSARILY A NECESSITY. - public function __destruct() - { - $this->close(); - } - */ - - /** - * Singleton method, use this to get an instance and avoid re-parsing the db. - * - * Unique instances are instantiated based on the filename of the db. The flags - * are ignored -- in that requests to for instance with same filename but different - * flags will return the already-instantiated instance. For example: - * - * // create new instance with memory_cache enabled - * $geoip = Net_GeoIP::getInstance('C:\mydb.dat', Net_GeoIP::MEMORY_CACHE); - * .... - * - * // later in code, request instance with no flags specified. - * $geoip = Net_GeoIP::getInstance('C:\mydb.dat'); - * - * // Normally this means no MEMORY_CACHE but since an instance - * // with memory cache enabled has already been created for 'C:\mydb.dat', the - * // existing instance (with memory cache) will be returned. - * - * - * NOTE: You can only use SHARED_MEMORY flag for one instance! Any subsquent instances - * that attempt to use the SHARED_MEMORY will use the *same* shared memory, which will break - * your script. - * - * @param string $filename Filename - * @param int $flags Flags that control class behavior. - * + Net_GeoIp::SHARED_MEMORY - * Use SHMOP to share a db among multiple PHP instances. - * NOTE: ONLY ONE GEOIP INSTANCE CAN USE SHARED MEMORY!!! - * + Net_GeoIp::MEMORY_CACHE - * Store the full contents of the database in memory for current script. - * This is useful if you access the database several times in a script. - * + Net_GeoIp::STANDARD - * [default] standard no-cache version. - * - * @return Net_GeoIP - */ - public static function getInstance($filename = null, $flags = null) - { - if (!isset(self::$instances[$filename])) { - self::$instances[$filename] = new Net_GeoIP($filename, $flags); - } - return self::$instances[$filename]; - } - - /** - * Opens geoip database at filename and with specified flags. - * - * @param string $filename File to open - * @param int $flags Flags - * - * @return void - * - * @throws PEAR_Exception if unable to open specified file or shared memory. - */ - public function open($filename, $flags = null) - { - if ($flags !== null) { - $this->flags = $flags; - } - if ($this->flags & self::SHARED_MEMORY) { - $this->shmid = @shmop_open(self::SHM_KEY, "a", 0, 0); - if ($this->shmid === false) { - $this->loadSharedMemory($filename); - $this->shmid = @shmop_open(self::SHM_KEY, "a", 0, 0); - if ($this->shmid === false) { // should never be false as loadSharedMemory() will throw Exc if cannot create - throw new PEAR_Exception("Unable to open shared memory at key: " . dechex(self::SHM_KEY)); - } - } - } else { - $this->filehandle = fopen($filename, "rb"); - if (!$this->filehandle) { - throw new PEAR_Exception("Unable to open file: $filename"); - } - if ($this->flags & self::MEMORY_CACHE) { - $s_array = fstat($this->filehandle); - $this->memoryBuffer = fread($this->filehandle, $s_array['size']); - } - } - $this->setupSegments(); - } - - /** - * Loads the database file into shared memory. - * - * @param string $filename Path to database file to read into shared memory. - * - * @return void - * - * @throws PEAR_Exception - if unable to read the db file. - */ - protected function loadSharedMemory($filename) - { - $fp = fopen($filename, "rb"); - if (!$fp) { - throw new PEAR_Exception("Unable to open file: $filename"); - } - $s_array = fstat($fp); - $size = $s_array['size']; - - if ($shmid = @shmop_open(self::SHM_KEY, "w", 0, 0)) { - shmop_delete($shmid); - shmop_close($shmid); - } - - if ($shmid = @shmop_open(self::SHM_KEY, "c", 0644, $size)) { - $offset = 0; - while ($offset < $size) { - $buf = fread($fp, 524288); - shmop_write($shmid, $buf, $offset); - $offset += 524288; - } - shmop_close($shmid); - } - - fclose($fp); - } - - /** - * Parses the database file to determine what kind of database is being used and setup - * segment sizes and start points that will be used by the seek*() methods later. - * - * @return void - */ - protected function setupSegments() - { - - $this->databaseType = self::COUNTRY_EDITION; - $this->recordLength = self::STANDARD_RECORD_LENGTH; - - if ($this->flags & self::SHARED_MEMORY) { - - $offset = shmop_size($this->shmid) - 3; - for ($i = 0; $i < self::STRUCTURE_INFO_MAX_SIZE; $i++) { - $delim = shmop_read($this->shmid, $offset, 3); - $offset += 3; - if ($delim == (chr(255).chr(255).chr(255))) { - $this->databaseType = ord(shmop_read($this->shmid, $offset, 1)); - $offset++; - if ($this->databaseType === self::REGION_EDITION_REV0) { - $this->databaseSegments = self::STATE_BEGIN_REV0; - } elseif ($this->databaseType === self::REGION_EDITION_REV1) { - $this->databaseSegments = self::STATE_BEGIN_REV1; - } elseif (($this->databaseType === self::CITY_EDITION_REV0) - || ($this->databaseType === self::CITY_EDITION_REV1) - || ($this->databaseType === self::ORG_EDITION)) { - $this->databaseSegments = 0; - $buf = shmop_read($this->shmid, $offset, self::SEGMENT_RECORD_LENGTH); - for ($j = 0; $j < self::SEGMENT_RECORD_LENGTH; $j++) { - $this->databaseSegments += (ord($buf[$j]) << ($j * 8)); - } - if ($this->databaseType === self::ORG_EDITION) { - $this->recordLength = self::ORG_RECORD_LENGTH; - } - } - break; - } else { - $offset -= 4; - } - } - if ($this->databaseType == self::COUNTRY_EDITION) { - $this->databaseSegments = self::COUNTRY_BEGIN; - } - - } else { - - $filepos = ftell($this->filehandle); - fseek($this->filehandle, -3, SEEK_END); - for ($i = 0; $i < self::STRUCTURE_INFO_MAX_SIZE; $i++) { - $delim = fread($this->filehandle, 3); - if ($delim == (chr(255).chr(255).chr(255))) { - $this->databaseType = ord(fread($this->filehandle, 1)); - if ($this->databaseType === self::REGION_EDITION_REV0) { - $this->databaseSegments = self::STATE_BEGIN_REV0; - } elseif ($this->databaseType === self::REGION_EDITION_REV1) { - $this->databaseSegments = self::STATE_BEGIN_REV1; - } elseif ($this->databaseType === self::CITY_EDITION_REV0 - || $this->databaseType === self::CITY_EDITION_REV1 - || $this->databaseType === self::ORG_EDITION) { - $this->databaseSegments = 0; - $buf = fread($this->filehandle, self::SEGMENT_RECORD_LENGTH); - for ($j = 0; $j < self::SEGMENT_RECORD_LENGTH; $j++) { - $this->databaseSegments += (ord($buf[$j]) << ($j * 8)); - } - if ($this->databaseType === self::ORG_EDITION) { - $this->recordLength = self::ORG_RECORD_LENGTH; - } - } - break; - } else { - fseek($this->filehandle, -4, SEEK_CUR); - } - } - if ($this->databaseType === self::COUNTRY_EDITION) { - $this->databaseSegments = self::COUNTRY_BEGIN; - } - fseek($this->filehandle, $filepos, SEEK_SET); - - } - } - - /** - * Closes the geoip database. - * - * @return int Status of close command. - */ - public function close() - { - if ($this->flags & self::SHARED_MEMORY) { - return shmop_close($this->shmid); - } else { - // right now even if file was cached in RAM the file was not closed - // so it's safe to expect no error w/ fclose() - return fclose($this->filehandle); - } - } - - /** - * Get the country index. - * - * This method is called by the lookupCountryCode() and lookupCountryName() - * methods. It lookups up the index ('id') for the country which is the key - * for the code and name. - * - * @param string $addr IP address (hostname not allowed) - * - * @throws PEAR_Exception - if IP address is invalid. - * - if database type is incorrect - * - * @return string ID for the country - */ - protected function lookupCountryId($addr) - { - $ipnum = ip2long($addr); - if ($ipnum === false) { - throw new PEAR_Exception("Invalid IP address: " . var_export($addr, true), self::ERR_INVALID_IP); - } - if ($this->databaseType !== self::COUNTRY_EDITION) { - throw new PEAR_Exception("Invalid database type; lookupCountry*() methods expect Country database."); - } - return $this->seekCountry($ipnum) - self::COUNTRY_BEGIN; - } - - /** - * Returns 2-letter country code (e.g. 'CA') for specified IP address. - * Use this method if you have a Country database. - * - * @param string $addr IP address (hostname not allowed). - * - * @return string 2-letter country code - * - * @throws PEAR_Exception (see lookupCountryId()) - * @see lookupCountryId() - */ - public function lookupCountryCode($addr) - { - return self::$COUNTRY_CODES[$this->lookupCountryId($addr)]; - } - - /** - * Returns full country name for specified IP address. - * Use this method if you have a Country database. - * - * @param string $addr IP address (hostname not allowed). - * - * @return string Country name - * @throws PEAR_Exception (see lookupCountryId()) - * @see lookupCountryId() - */ - public function lookupCountryName($addr) - { - return self::$COUNTRY_NAMES[$this->lookupCountryId($addr)]; - } - - /** - * Using the record length and appropriate start points, seek to the country that corresponds - * to the converted IP address integer. - * - * @param int $ipnum Result of ip2long() conversion. - * - * @return int Offset of start of record. - * @throws PEAR_Exception - if fseek() fails on the file or no results after traversing the database (indicating corrupt db). - */ - protected function seekCountry($ipnum) - { - $offset = 0; - for ($depth = 31; $depth >= 0; --$depth) { - if ($this->flags & self::MEMORY_CACHE) { - $buf = substr($this->memoryBuffer, 2 * $this->recordLength * $offset, 2 * $this->recordLength); - } elseif ($this->flags & self::SHARED_MEMORY) { - $buf = shmop_read($this->shmid, 2 * $this->recordLength * $offset, 2 * $this->recordLength); - } else { - if (fseek($this->filehandle, 2 * $this->recordLength * $offset, SEEK_SET) !== 0) { - throw new PEAR_Exception("fseek failed"); - } - $buf = fread($this->filehandle, 2 * $this->recordLength); - } - $x = array(0,0); - for ($i = 0; $i < 2; ++$i) { - for ($j = 0; $j < $this->recordLength; ++$j) { - $x[$i] += ord($buf[$this->recordLength * $i + $j]) << ($j * 8); - } - } - if ($ipnum & (1 << $depth)) { - if ($x[1] >= $this->databaseSegments) { - return $x[1]; - } - $offset = $x[1]; - } else { - if ($x[0] >= $this->databaseSegments) { - return $x[0]; - } - $offset = $x[0]; - } - } - throw new PEAR_Exception("Error traversing database - perhaps it is corrupt?"); - } - - /** - * Lookup the organization (or ISP) for given IP address. - * Use this method if you have an Organization/ISP database. - * - * @param string $addr IP address (hostname not allowed). - * - * @throws PEAR_Exception - if IP address is invalid. - * - if database is of wrong type - * - * @return string The organization - */ - public function lookupOrg($addr) - { - $ipnum = ip2long($addr); - if ($ipnum === false) { - throw new PEAR_Exception("Invalid IP address: " . var_export($addr, true), self::ERR_INVALID_IP); - } - if ($this->databaseType !== self::ORG_EDITION) { - throw new PEAR_Exception("Invalid database type; lookupOrg() method expects Org/ISP database.", self::ERR_DB_FORMAT); - } - return $this->getOrg($ipnum); - } - - /** - * Lookup the region for given IP address. - * Use this method if you have a Region database. - * - * @param string $addr IP address (hostname not allowed). - * - * @return array Array containing country code and region: array($country_code, $region) - * - * @throws PEAR_Exception - if IP address is invalid. - */ - public function lookupRegion($addr) - { - $ipnum = ip2long($addr); - if ($ipnum === false) { - throw new PEAR_Exception("Invalid IP address: " . var_export($addr, true), self::ERR_INVALID_IP); - } - if ($this->databaseType !== self::REGION_EDITION_REV0 && $this->databaseType !== self::REGION_EDITION_REV1) { - throw new PEAR_Exception("Invalid database type; lookupRegion() method expects Region database.", self::ERR_DB_FORMAT); - } - return $this->getRegion($ipnum); - } - - /** - * Lookup the location record for given IP address. - * Use this method if you have a City database. - * - * @param string $addr IP address (hostname not allowed). - * - * @return Net_GeoIP_Location The full location record. - * - * @throws PEAR_Exception - if IP address is invalid. - */ - public function lookupLocation($addr) - { - include_once 'Net/GeoIP/Location.php'; - $ipnum = ip2long($addr); - if ($ipnum === false) { - throw new PEAR_Exception("Invalid IP address: " . var_export($addr, true), self::ERR_INVALID_IP); - } - if ($this->databaseType !== self::CITY_EDITION_REV0 && $this->databaseType !== self::CITY_EDITION_REV1) { - throw new PEAR_Exception("Invalid database type; lookupLocation() method expects City database."); - } - return $this->getRecord($ipnum); - } - - /** - * Seek and return organization (or ISP) name for converted IP addr. - * - * @param int $ipnum Converted IP address. - * - * @return string The organization - */ - protected function getOrg($ipnum) - { - $seek_org = $this->seekCountry($ipnum); - if ($seek_org == $this->databaseSegments) { - return null; - } - $record_pointer = $seek_org + (2 * $this->recordLength - 1) * $this->databaseSegments; - if ($this->flags & self::SHARED_MEMORY) { - $org_buf = shmop_read($this->shmid, $record_pointer, self::MAX_ORG_RECORD_LENGTH); - } else { - fseek($this->filehandle, $record_pointer, SEEK_SET); - $org_buf = fread($this->filehandle, self::MAX_ORG_RECORD_LENGTH); - } - $org_buf = substr($org_buf, 0, strpos($org_buf, 0)); - return $org_buf; - } - - /** - * Seek and return the region info (array containing country code and region name) for converted IP addr. - * - * @param int $ipnum Converted IP address. - * - * @return array Array containing country code and region: array($country_code, $region) - */ - protected function getRegion($ipnum) - { - if ($this->databaseType == self::REGION_EDITION_REV0) { - $seek_region = $this->seekCountry($ipnum) - self::STATE_BEGIN_REV0; - if ($seek_region >= 1000) { - $country_code = "US"; - $region = chr(($seek_region - 1000)/26 + 65) . chr(($seek_region - 1000)%26 + 65); - } else { - $country_code = self::$COUNTRY_CODES[$seek_region]; - $region = ""; - } - return array($country_code, $region); - } elseif ($this->databaseType == self::REGION_EDITION_REV1) { - $seek_region = $this->seekCountry($ipnum) - self::STATE_BEGIN_REV1; - //print $seek_region; - if ($seek_region < self::US_OFFSET) { - $country_code = ""; - $region = ""; - } elseif ($seek_region < self::CANADA_OFFSET) { - $country_code = "US"; - $region = chr(($seek_region - self::US_OFFSET)/26 + 65) . chr(($seek_region - self::US_OFFSET)%26 + 65); - } elseif ($seek_region < self::WORLD_OFFSET) { - $country_code = "CA"; - $region = chr(($seek_region - self::CANADA_OFFSET)/26 + 65) . chr(($seek_region - self::CANADA_OFFSET)%26 + 65); - } else { - $country_code = self::$COUNTRY_CODES[($seek_region - self::WORLD_OFFSET) / self::FIPS_RANGE]; - $region = ""; - } - return array ($country_code,$region); - } - } - - /** - * Seek and populate Net_GeoIP_Location object for converted IP addr. - * Note: this - * - * @param int $ipnum Converted IP address. - * - * @return Net_GeoIP_Location - */ - protected function getRecord($ipnum) - { - $seek_country = $this->seekCountry($ipnum); - if ($seek_country == $this->databaseSegments) { - return null; - } - - $record_pointer = $seek_country + (2 * $this->recordLength - 1) * $this->databaseSegments; - - if ($this->flags & self::SHARED_MEMORY) { - $record_buf = shmop_read($this->shmid, $record_pointer, self::FULL_RECORD_LENGTH); - } else { - fseek($this->filehandle, $record_pointer, SEEK_SET); - $record_buf = fread($this->filehandle, self::FULL_RECORD_LENGTH); - } - - $record = new Net_GeoIP_Location(); - - $record_buf_pos = 0; - $char = ord(substr($record_buf, $record_buf_pos, 1)); - - $record->countryCode = self::$COUNTRY_CODES[$char]; - $record->countryCode3 = self::$COUNTRY_CODES3[$char]; - $record->countryName = self::$COUNTRY_NAMES[$char]; - $record_buf_pos++; - $str_length = 0; - - //get region - $char = ord(substr($record_buf, $record_buf_pos+$str_length, 1)); - while ($char != 0) { - $str_length++; - $char = ord(substr($record_buf, $record_buf_pos+$str_length, 1)); - } - if ($str_length > 0) { - $record->region = substr($record_buf, $record_buf_pos, $str_length); - } - $record_buf_pos += $str_length + 1; - $str_length = 0; - - //get city - $char = ord(substr($record_buf, $record_buf_pos+$str_length, 1)); - while ($char != 0) { - $str_length++; - $char = ord(substr($record_buf, $record_buf_pos+$str_length, 1)); - } - if ($str_length > 0) { - $record->city = substr($record_buf, $record_buf_pos, $str_length); - } - $record_buf_pos += $str_length + 1; - $str_length = 0; - - //get postal code - $char = ord(substr($record_buf, $record_buf_pos+$str_length, 1)); - while ($char != 0) { - $str_length++; - $char = ord(substr($record_buf, $record_buf_pos+$str_length, 1)); - } - if ($str_length > 0) { - $record->postalCode = substr($record_buf, $record_buf_pos, $str_length); - } - $record_buf_pos += $str_length + 1; - $str_length = 0; - $latitude = 0; - $longitude = 0; - for ($j = 0;$j < 3; ++$j) { - $char = ord(substr($record_buf, $record_buf_pos++, 1)); - $latitude += ($char << ($j * 8)); - } - $record->latitude = ($latitude/10000) - 180; - - for ($j = 0;$j < 3; ++$j) { - $char = ord(substr($record_buf, $record_buf_pos++, 1)); - $longitude += ($char << ($j * 8)); - } - $record->longitude = ($longitude/10000) - 180; - - if ($this->databaseType === self::CITY_EDITION_REV1) { - $dmaarea_combo = 0; - if ($record->countryCode == "US") { - for ($j = 0;$j < 3;++$j) { - $char = ord(substr($record_buf, $record_buf_pos++, 1)); - $dmaarea_combo += ($char << ($j * 8)); - } - $record->dmaCode = floor($dmaarea_combo/1000); - $record->areaCode = $dmaarea_combo%1000; - } - } - - return $record; - } - -} - + (original Maxmind version) | + * | Hans Lellelid | + * +----------------------------------------------------------------------+ + * + * @category Net + * @package Net_GeoIP + * @author Jim Winstead (original Maxmind PHP API) + * @author Hans Lellelid + * @license LGPL http://www.gnu.org/licenses/lgpl.txt + * @link http://pear.php.net/package/Net_GeoIp + * $Id$ + */ + +require_once 'PEAR/Exception.php'; + +/** + * GeoIP class provides an API for performing geo-location lookups based on IP + * address. + * + * To use this class you must have a [binary version] GeoIP database. There is + * a free GeoIP country database which can be obtained from Maxmind: + * {@link http://www.maxmind.com/app/geoip_country} + * + * + * SIMPLE USE + * + * + * Create an instance: + * + * + * $geoip = Net_GeoIP::getInstance('/path/to/geoipdb.dat', Net_GeoIP::SHARED_MEMORY); + * + * + * Depending on which database you are using (free, or one of paid versions) + * you must use appropriate lookup method: + * + * + * // for free country db: + * $country_name = $geoip->lookupCountryName($_SERVER['REMOTE_ADDR']); + * $country_code = $geoip->lookupCountryCode($_SERVER['REMOTE_ADDR']); + * + * // for [non-free] region db: + * list($ctry_code, $region) = $geoip->lookupRegion($_SERVER['REMOTE_ADDR']); + * + * // for [non-free] city db: + * $location = $geoip->lookupLocation($_SERVER['REMOTE_ADDR']); + * print "city: " . $location->city . ", " . $location->region; + * print "lat: " . $location->latitude . ", long: " . $location->longitude; + * + * // for organization or ISP db: + * $org_or_isp_name = $geoip->lookupOrg($_SERVER['REMOTE_ADDR']); + * + * + * + * MULTIPLE INSTANCES + * + * + * You can have several instances of this class, one for each database file + * you are using. You should use the static getInstance() singleton method + * to save on overhead of setting up database segments. Note that only one + * instance is stored per filename, and any flags will be ignored if an + * instance already exists for the specifiedfilename. + * + * Special note on using SHARED_MEMORY flag + * + * If you are using SHARED_MEMORY (shmop) you can only use SHARED_MEMORY for + * one (1) instance (i.e. for one database). Any subsequent attempts to + * instantiate using SHARED_MEMORY will read the same shared memory block + * already initialized, and therefore will cause problems since the expected + * database format won't match the database in the shared memory block. + * + * Note that there is no easy way to flag "nice errors" to prevent attempts + * to create new instances using SHARED_MEMORY flag and it is also not posible + * (in a safe way) to allow new instances to overwrite the shared memory block. + * + * In short, is you are using multiple databses, use the SHARED_MEMORY flag + * with care. + * + * + * LOOKUPS ON HOSTNAMES + * + * + * Note that this PHP API does NOT support lookups on hostnames. This is so + * that the public API can be kept simple and so that the lookup functions + * don't need to try name lookups if IP lookup fails (which would be the only + * way to keep the API simple and support name-based lookups). + * + * If you do not know the IP address, you can convert an name to IP very + * simply using PHP native functions or other libraries: + * + * + * $geoip->lookupCountryName(gethostbyname('www.sunset.se')); + * + * + * Or, if you don't know whether an address is a name or ip address, use + * application-level logic: + * + * + * if (ip2long($ip_or_name) === false) { + * $ip = gethostbyname($ip_or_name); + * } else { + * $ip = $ip_or_name; + * } + * $ctry = $geoip->lookupCountryName($ip); + * + * + * @category Net + * @package Net_GeoIP + * @author Jim Winstead (original Maxmind PHP API) + * @author Hans Lellelid + * @license LGPL http://www.gnu.org/licenses/lgpl.txt + * @link http://pear.php.net/package/Net_GeoIp + */ +class Net_GeoIP +{ + /** + * Exception error code used for invalid IP address. + */ + const ERR_INVALID_IP = 218624992; // crc32('Net_GeoIP::ERR_INVALID_IP') + + /** + * Exception error code when there is a DB-format-related error. + */ + const ERR_DB_FORMAT = 866184008; // crc32('Net_GeoIP::ERR_DB_FORMAT') + + public static $COUNTRY_CODES = array( + "", "AP", "EU", "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AN", "AO", "AQ", + "AR", "AS", "AT", "AU", "AW", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", + "BI", "BJ", "BM", "BN", "BO", "BR", "BS", "BT", "BV", "BW", "BY", "BZ", "CA", + "CC", "CD", "CF", "CG", "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", "CU", + "CV", "CX", "CY", "CZ", "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", + "EH", "ER", "ES", "ET", "FI", "FJ", "FK", "FM", "FO", "FR", "FX", "GA", "GB", + "GD", "GE", "GF", "GH", "GI", "GL", "GM", "GN", "GP", "GQ", "GR", "GS", "GT", + "GU", "GW", "GY", "HK", "HM", "HN", "HR", "HT", "HU", "ID", "IE", "IL", "IN", + "IO", "IQ", "IR", "IS", "IT", "JM", "JO", "JP", "KE", "KG", "KH", "KI", "KM", + "KN", "KP", "KR", "KW", "KY", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", + "LT", "LU", "LV", "LY", "MA", "MC", "MD", "MG", "MH", "MK", "ML", "MM", "MN", + "MO", "MP", "MQ", "MR", "MS", "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", + "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", "NR", "NU", "NZ", "OM", "PA", + "PE", "PF", "PG", "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", "PW", "PY", + "QA", "RE", "RO", "RU", "RW", "SA", "SB", "SC", "SD", "SE", "SG", "SH", "SI", + "SJ", "SK", "SL", "SM", "SN", "SO", "SR", "ST", "SV", "SY", "SZ", "TC", "TD", + "TF", "TG", "TH", "TJ", "TK", "TM", "TN", "TO", "TL", "TR", "TT", "TV", "TW", + "TZ", "UA", "UG", "UM", "US", "UY", "UZ", "VA", "VC", "VE", "VG", "VI", "VN", + "VU", "WF", "WS", "YE", "YT", "RS", "ZA", "ZM", "ME", "ZW", "A1", "A2", "O1", + "AX", "GG", "IM", "JE", "BL", "MF" + ); + + public static $COUNTRY_CODES3 = array( + "","AP","EU","AND","ARE","AFG","ATG","AIA","ALB","ARM","ANT","AGO","AQ","ARG", + "ASM","AUT","AUS","ABW","AZE","BIH","BRB","BGD","BEL","BFA","BGR","BHR","BDI", + "BEN","BMU","BRN","BOL","BRA","BHS","BTN","BV","BWA","BLR","BLZ","CAN","CC", + "COD","CAF","COG","CHE","CIV","COK","CHL","CMR","CHN","COL","CRI","CUB","CPV", + "CX","CYP","CZE","DEU","DJI","DNK","DMA","DOM","DZA","ECU","EST","EGY","ESH", + "ERI","ESP","ETH","FIN","FJI","FLK","FSM","FRO","FRA","FX","GAB","GBR","GRD", + "GEO","GUF","GHA","GIB","GRL","GMB","GIN","GLP","GNQ","GRC","GS","GTM","GUM", + "GNB","GUY","HKG","HM","HND","HRV","HTI","HUN","IDN","IRL","ISR","IND","IO", + "IRQ","IRN","ISL","ITA","JAM","JOR","JPN","KEN","KGZ","KHM","KIR","COM","KNA", + "PRK","KOR","KWT","CYM","KAZ","LAO","LBN","LCA","LIE","LKA","LBR","LSO","LTU", + "LUX","LVA","LBY","MAR","MCO","MDA","MDG","MHL","MKD","MLI","MMR","MNG","MAC", + "MNP","MTQ","MRT","MSR","MLT","MUS","MDV","MWI","MEX","MYS","MOZ","NAM","NCL", + "NER","NFK","NGA","NIC","NLD","NOR","NPL","NRU","NIU","NZL","OMN","PAN","PER", + "PYF","PNG","PHL","PAK","POL","SPM","PCN","PRI","PSE","PRT","PLW","PRY","QAT", + "REU","ROU","RUS","RWA","SAU","SLB","SYC","SDN","SWE","SGP","SHN","SVN","SJM", + "SVK","SLE","SMR","SEN","SOM","SUR","STP","SLV","SYR","SWZ","TCA","TCD","TF", + "TGO","THA","TJK","TKL","TLS","TKM","TUN","TON","TUR","TTO","TUV","TWN","TZA", + "UKR","UGA","UM","USA","URY","UZB","VAT","VCT","VEN","VGB","VIR","VNM","VUT", + "WLF","WSM","YEM","YT","SRB","ZAF","ZMB","MNE","ZWE","A1","A2","O1", + "ALA","GGY","IMN","JEY","BLM","MAF" + ); + + public static $COUNTRY_NAMES = array( + "", "Asia/Pacific Region", "Europe", "Andorra", "United Arab Emirates", + "Afghanistan", "Antigua and Barbuda", "Anguilla", "Albania", "Armenia", + "Netherlands Antilles", "Angola", "Antarctica", "Argentina", "American Samoa", + "Austria", "Australia", "Aruba", "Azerbaijan", "Bosnia and Herzegovina", + "Barbados", "Bangladesh", "Belgium", "Burkina Faso", "Bulgaria", "Bahrain", + "Burundi", "Benin", "Bermuda", "Brunei Darussalam", "Bolivia", "Brazil", + "Bahamas", "Bhutan", "Bouvet Island", "Botswana", "Belarus", "Belize", + "Canada", "Cocos (Keeling) Islands", "Congo, The Democratic Republic of the", + "Central African Republic", "Congo", "Switzerland", "Cote D'Ivoire", "Cook Islands", + "Chile", "Cameroon", "China", "Colombia", "Costa Rica", "Cuba", "Cape Verde", + "Christmas Island", "Cyprus", "Czech Republic", "Germany", "Djibouti", + "Denmark", "Dominica", "Dominican Republic", "Algeria", "Ecuador", "Estonia", + "Egypt", "Western Sahara", "Eritrea", "Spain", "Ethiopia", "Finland", "Fiji", + "Falkland Islands (Malvinas)", "Micronesia, Federated States of", "Faroe Islands", + "France", "France, Metropolitan", "Gabon", "United Kingdom", + "Grenada", "Georgia", "French Guiana", "Ghana", "Gibraltar", "Greenland", + "Gambia", "Guinea", "Guadeloupe", "Equatorial Guinea", "Greece", "South Georgia and the South Sandwich Islands", + "Guatemala", "Guam", "Guinea-Bissau", + "Guyana", "Hong Kong", "Heard Island and McDonald Islands", "Honduras", + "Croatia", "Haiti", "Hungary", "Indonesia", "Ireland", "Israel", "India", + "British Indian Ocean Territory", "Iraq", "Iran, Islamic Republic of", + "Iceland", "Italy", "Jamaica", "Jordan", "Japan", "Kenya", "Kyrgyzstan", + "Cambodia", "Kiribati", "Comoros", "Saint Kitts and Nevis", "Korea, Democratic People's Republic of", + "Korea, Republic of", "Kuwait", "Cayman Islands", + "Kazakstan", "Lao People's Democratic Republic", "Lebanon", "Saint Lucia", + "Liechtenstein", "Sri Lanka", "Liberia", "Lesotho", "Lithuania", "Luxembourg", + "Latvia", "Libyan Arab Jamahiriya", "Morocco", "Monaco", "Moldova, Republic of", + "Madagascar", "Marshall Islands", "Macedonia", + "Mali", "Myanmar", "Mongolia", "Macau", "Northern Mariana Islands", + "Martinique", "Mauritania", "Montserrat", "Malta", "Mauritius", "Maldives", + "Malawi", "Mexico", "Malaysia", "Mozambique", "Namibia", "New Caledonia", + "Niger", "Norfolk Island", "Nigeria", "Nicaragua", "Netherlands", "Norway", + "Nepal", "Nauru", "Niue", "New Zealand", "Oman", "Panama", "Peru", "French Polynesia", + "Papua New Guinea", "Philippines", "Pakistan", "Poland", "Saint Pierre and Miquelon", + "Pitcairn Islands", "Puerto Rico", "Palestinian Territory", + "Portugal", "Palau", "Paraguay", "Qatar", "Reunion", "Romania", + "Russian Federation", "Rwanda", "Saudi Arabia", "Solomon Islands", + "Seychelles", "Sudan", "Sweden", "Singapore", "Saint Helena", "Slovenia", + "Svalbard and Jan Mayen", "Slovakia", "Sierra Leone", "San Marino", "Senegal", + "Somalia", "Suriname", "Sao Tome and Principe", "El Salvador", "Syrian Arab Republic", + "Swaziland", "Turks and Caicos Islands", "Chad", "French Southern Territories", + "Togo", "Thailand", "Tajikistan", "Tokelau", "Turkmenistan", + "Tunisia", "Tonga", "Timor-Leste", "Turkey", "Trinidad and Tobago", "Tuvalu", + "Taiwan", "Tanzania, United Republic of", "Ukraine", + "Uganda", "United States Minor Outlying Islands", "United States", "Uruguay", + "Uzbekistan", "Holy See (Vatican City State)", "Saint Vincent and the Grenadines", + "Venezuela", "Virgin Islands, British", "Virgin Islands, U.S.", + "Vietnam", "Vanuatu", "Wallis and Futuna", "Samoa", "Yemen", "Mayotte", + "Serbia", "South Africa", "Zambia", "Montenegro", "Zimbabwe", + "Anonymous Proxy","Satellite Provider","Other", + "Aland Islands","Guernsey","Isle of Man","Jersey","Saint Barthelemy","Saint Martin" + ); + + // storage / caching flags + const STANDARD = 0; + const MEMORY_CACHE = 1; + const SHARED_MEMORY = 2; + + // Database structure constants + const COUNTRY_BEGIN = 16776960; + const STATE_BEGIN_REV0 = 16700000; + const STATE_BEGIN_REV1 = 16000000; + + const STRUCTURE_INFO_MAX_SIZE = 20; + const DATABASE_INFO_MAX_SIZE = 100; + const COUNTRY_EDITION = 106; + const REGION_EDITION_REV0 = 112; + const REGION_EDITION_REV1 = 3; + const CITY_EDITION_REV0 = 111; + const CITY_EDITION_REV1 = 2; + const ORG_EDITION = 110; + const SEGMENT_RECORD_LENGTH = 3; + const STANDARD_RECORD_LENGTH = 3; + const ORG_RECORD_LENGTH = 4; + const MAX_RECORD_LENGTH = 4; + const MAX_ORG_RECORD_LENGTH = 300; + const FULL_RECORD_LENGTH = 50; + + const US_OFFSET = 1; + const CANADA_OFFSET = 677; + const WORLD_OFFSET = 1353; + const FIPS_RANGE = 360; + + // SHMOP memory address + const SHM_KEY = 0x4f415401; + + /** + * @var int + */ + private $flags = 0; + + /** + * @var resource + */ + private $filehandle; + + /** + * @var string + */ + private $memoryBuffer; + + /** + * @var int + */ + private $databaseType; + + /** + * @var int + */ + private $databaseSegments; + + /** + * @var int + */ + private $recordLength; + + /** + * The memory addr "id" for use with SHMOP. + * @var int + */ + private $shmid; + + /** + * Support for singleton pattern. + * @var array + */ + private static $instances = array(); + + /** + * Construct a Net_GeoIP instance. + * You should use the getInstance() method if you plan to use multiple databases or + * the same database from several different places in your script. + * + * @param string $filename Path to binary geoip database. + * @param int $flags Flags + * + * @see getInstance() + */ + public function __construct($filename = null, $flags = null) + { + if ($filename !== null) { + $this->open($filename, $flags); + } + // store the instance, so that it will be returned by a call to + // getInstance() (with the same db filename). + self::$instances[$filename] = $this; + } + + /** + * Calls the close() function to free any resources. + * @see close() + * + * COMMENTED OUT TO ADDRESS BUG IN PHP 5.0.4, 5.0.5dev. THIS RESOURCE + * SHOULD AUTOMATICALLY BE FREED AT SCRIPT CLOSE, SO A DESTRUCTOR + * IS A GOOD IDEA BUT NOT NECESSARILY A NECESSITY. + public function __destruct() + { + $this->close(); + } + */ + + /** + * Singleton method, use this to get an instance and avoid re-parsing the db. + * + * Unique instances are instantiated based on the filename of the db. The flags + * are ignored -- in that requests to for instance with same filename but different + * flags will return the already-instantiated instance. For example: + * + * // create new instance with memory_cache enabled + * $geoip = Net_GeoIP::getInstance('C:\mydb.dat', Net_GeoIP::MEMORY_CACHE); + * .... + * + * // later in code, request instance with no flags specified. + * $geoip = Net_GeoIP::getInstance('C:\mydb.dat'); + * + * // Normally this means no MEMORY_CACHE but since an instance + * // with memory cache enabled has already been created for 'C:\mydb.dat', the + * // existing instance (with memory cache) will be returned. + * + * + * NOTE: You can only use SHARED_MEMORY flag for one instance! Any subsquent instances + * that attempt to use the SHARED_MEMORY will use the *same* shared memory, which will break + * your script. + * + * @param string $filename Filename + * @param int $flags Flags that control class behavior. + * + Net_GeoIp::SHARED_MEMORY + * Use SHMOP to share a db among multiple PHP instances. + * NOTE: ONLY ONE GEOIP INSTANCE CAN USE SHARED MEMORY!!! + * + Net_GeoIp::MEMORY_CACHE + * Store the full contents of the database in memory for current script. + * This is useful if you access the database several times in a script. + * + Net_GeoIp::STANDARD + * [default] standard no-cache version. + * + * @return Net_GeoIP + */ + public static function getInstance($filename = null, $flags = null) + { + if (!isset(self::$instances[$filename])) { + self::$instances[$filename] = new Net_GeoIP($filename, $flags); + } + return self::$instances[$filename]; + } + + /** + * Opens geoip database at filename and with specified flags. + * + * @param string $filename File to open + * @param int $flags Flags + * + * @return void + * + * @throws PEAR_Exception if unable to open specified file or shared memory. + */ + public function open($filename, $flags = null) + { + if ($flags !== null) { + $this->flags = $flags; + } + if ($this->flags & self::SHARED_MEMORY) { + $this->shmid = @shmop_open(self::SHM_KEY, "a", 0, 0); + if ($this->shmid === false) { + $this->loadSharedMemory($filename); + $this->shmid = @shmop_open(self::SHM_KEY, "a", 0, 0); + if ($this->shmid === false) { // should never be false as loadSharedMemory() will throw Exc if cannot create + throw new PEAR_Exception("Unable to open shared memory at key: " . dechex(self::SHM_KEY)); + } + } + } else { + $this->filehandle = fopen($filename, "rb"); + if (!$this->filehandle) { + throw new PEAR_Exception("Unable to open file: $filename"); + } + if ($this->flags & self::MEMORY_CACHE) { + $s_array = fstat($this->filehandle); + $this->memoryBuffer = fread($this->filehandle, $s_array['size']); + } + } + $this->setupSegments(); + } + + /** + * Loads the database file into shared memory. + * + * @param string $filename Path to database file to read into shared memory. + * + * @return void + * + * @throws PEAR_Exception - if unable to read the db file. + */ + protected function loadSharedMemory($filename) + { + $fp = fopen($filename, "rb"); + if (!$fp) { + throw new PEAR_Exception("Unable to open file: $filename"); + } + $s_array = fstat($fp); + $size = $s_array['size']; + + if ($shmid = @shmop_open(self::SHM_KEY, "w", 0, 0)) { + shmop_delete($shmid); + shmop_close($shmid); + } + + if ($shmid = @shmop_open(self::SHM_KEY, "c", 0644, $size)) { + $offset = 0; + while ($offset < $size) { + $buf = fread($fp, 524288); + shmop_write($shmid, $buf, $offset); + $offset += 524288; + } + shmop_close($shmid); + } + + fclose($fp); + } + + /** + * Parses the database file to determine what kind of database is being used and setup + * segment sizes and start points that will be used by the seek*() methods later. + * + * @return void + */ + protected function setupSegments() + { + + $this->databaseType = self::COUNTRY_EDITION; + $this->recordLength = self::STANDARD_RECORD_LENGTH; + + if ($this->flags & self::SHARED_MEMORY) { + + $offset = shmop_size($this->shmid) - 3; + for ($i = 0; $i < self::STRUCTURE_INFO_MAX_SIZE; $i++) { + $delim = shmop_read($this->shmid, $offset, 3); + $offset += 3; + if ($delim == (chr(255).chr(255).chr(255))) { + $this->databaseType = ord(shmop_read($this->shmid, $offset, 1)); + $offset++; + if ($this->databaseType === self::REGION_EDITION_REV0) { + $this->databaseSegments = self::STATE_BEGIN_REV0; + } elseif ($this->databaseType === self::REGION_EDITION_REV1) { + $this->databaseSegments = self::STATE_BEGIN_REV1; + } elseif (($this->databaseType === self::CITY_EDITION_REV0) + || ($this->databaseType === self::CITY_EDITION_REV1) + || ($this->databaseType === self::ORG_EDITION)) { + $this->databaseSegments = 0; + $buf = shmop_read($this->shmid, $offset, self::SEGMENT_RECORD_LENGTH); + for ($j = 0; $j < self::SEGMENT_RECORD_LENGTH; $j++) { + $this->databaseSegments += (ord($buf[$j]) << ($j * 8)); + } + if ($this->databaseType === self::ORG_EDITION) { + $this->recordLength = self::ORG_RECORD_LENGTH; + } + } + break; + } else { + $offset -= 4; + } + } + if ($this->databaseType == self::COUNTRY_EDITION) { + $this->databaseSegments = self::COUNTRY_BEGIN; + } + + } else { + + $filepos = ftell($this->filehandle); + fseek($this->filehandle, -3, SEEK_END); + for ($i = 0; $i < self::STRUCTURE_INFO_MAX_SIZE; $i++) { + $delim = fread($this->filehandle, 3); + if ($delim == (chr(255).chr(255).chr(255))) { + $this->databaseType = ord(fread($this->filehandle, 1)); + if ($this->databaseType === self::REGION_EDITION_REV0) { + $this->databaseSegments = self::STATE_BEGIN_REV0; + } elseif ($this->databaseType === self::REGION_EDITION_REV1) { + $this->databaseSegments = self::STATE_BEGIN_REV1; + } elseif ($this->databaseType === self::CITY_EDITION_REV0 + || $this->databaseType === self::CITY_EDITION_REV1 + || $this->databaseType === self::ORG_EDITION) { + $this->databaseSegments = 0; + $buf = fread($this->filehandle, self::SEGMENT_RECORD_LENGTH); + for ($j = 0; $j < self::SEGMENT_RECORD_LENGTH; $j++) { + $this->databaseSegments += (ord($buf[$j]) << ($j * 8)); + } + if ($this->databaseType === self::ORG_EDITION) { + $this->recordLength = self::ORG_RECORD_LENGTH; + } + } + break; + } else { + fseek($this->filehandle, -4, SEEK_CUR); + } + } + if ($this->databaseType === self::COUNTRY_EDITION) { + $this->databaseSegments = self::COUNTRY_BEGIN; + } + fseek($this->filehandle, $filepos, SEEK_SET); + + } + } + + /** + * Closes the geoip database. + * + * @return int Status of close command. + */ + public function close() + { + if ($this->flags & self::SHARED_MEMORY) { + return shmop_close($this->shmid); + } else { + // right now even if file was cached in RAM the file was not closed + // so it's safe to expect no error w/ fclose() + return fclose($this->filehandle); + } + } + + /** + * Get the country index. + * + * This method is called by the lookupCountryCode() and lookupCountryName() + * methods. It lookups up the index ('id') for the country which is the key + * for the code and name. + * + * @param string $addr IP address (hostname not allowed) + * + * @throws PEAR_Exception - if IP address is invalid. + * - if database type is incorrect + * + * @return string ID for the country + */ + protected function lookupCountryId($addr) + { + $ipnum = ip2long($addr); + if ($ipnum === false) { + throw new PEAR_Exception("Invalid IP address: " . var_export($addr, true), self::ERR_INVALID_IP); + } + if ($this->databaseType !== self::COUNTRY_EDITION) { + throw new PEAR_Exception("Invalid database type; lookupCountry*() methods expect Country database."); + } + return $this->seekCountry($ipnum) - self::COUNTRY_BEGIN; + } + + /** + * Returns 2-letter country code (e.g. 'CA') for specified IP address. + * Use this method if you have a Country database. + * + * @param string $addr IP address (hostname not allowed). + * + * @return string 2-letter country code + * + * @throws PEAR_Exception (see lookupCountryId()) + * @see lookupCountryId() + */ + public function lookupCountryCode($addr) + { + return self::$COUNTRY_CODES[$this->lookupCountryId($addr)]; + } + + /** + * Returns full country name for specified IP address. + * Use this method if you have a Country database. + * + * @param string $addr IP address (hostname not allowed). + * + * @return string Country name + * @throws PEAR_Exception (see lookupCountryId()) + * @see lookupCountryId() + */ + public function lookupCountryName($addr) + { + return self::$COUNTRY_NAMES[$this->lookupCountryId($addr)]; + } + + /** + * Using the record length and appropriate start points, seek to the country that corresponds + * to the converted IP address integer. + * + * @param int $ipnum Result of ip2long() conversion. + * + * @return int Offset of start of record. + * @throws PEAR_Exception - if fseek() fails on the file or no results after traversing the database (indicating corrupt db). + */ + protected function seekCountry($ipnum) + { + $offset = 0; + for ($depth = 31; $depth >= 0; --$depth) { + if ($this->flags & self::MEMORY_CACHE) { + $buf = substr($this->memoryBuffer, 2 * $this->recordLength * $offset, 2 * $this->recordLength); + } elseif ($this->flags & self::SHARED_MEMORY) { + $buf = shmop_read($this->shmid, 2 * $this->recordLength * $offset, 2 * $this->recordLength); + } else { + if (fseek($this->filehandle, 2 * $this->recordLength * $offset, SEEK_SET) !== 0) { + throw new PEAR_Exception("fseek failed"); + } + $buf = fread($this->filehandle, 2 * $this->recordLength); + } + $x = array(0,0); + for ($i = 0; $i < 2; ++$i) { + for ($j = 0; $j < $this->recordLength; ++$j) { + $x[$i] += ord($buf[$this->recordLength * $i + $j]) << ($j * 8); + } + } + if ($ipnum & (1 << $depth)) { + if ($x[1] >= $this->databaseSegments) { + return $x[1]; + } + $offset = $x[1]; + } else { + if ($x[0] >= $this->databaseSegments) { + return $x[0]; + } + $offset = $x[0]; + } + } + throw new PEAR_Exception("Error traversing database - perhaps it is corrupt?"); + } + + /** + * Lookup the organization (or ISP) for given IP address. + * Use this method if you have an Organization/ISP database. + * + * @param string $addr IP address (hostname not allowed). + * + * @throws PEAR_Exception - if IP address is invalid. + * - if database is of wrong type + * + * @return string The organization + */ + public function lookupOrg($addr) + { + $ipnum = ip2long($addr); + if ($ipnum === false) { + throw new PEAR_Exception("Invalid IP address: " . var_export($addr, true), self::ERR_INVALID_IP); + } + if ($this->databaseType !== self::ORG_EDITION) { + throw new PEAR_Exception("Invalid database type; lookupOrg() method expects Org/ISP database.", self::ERR_DB_FORMAT); + } + return $this->getOrg($ipnum); + } + + /** + * Lookup the region for given IP address. + * Use this method if you have a Region database. + * + * @param string $addr IP address (hostname not allowed). + * + * @return array Array containing country code and region: array($country_code, $region) + * + * @throws PEAR_Exception - if IP address is invalid. + */ + public function lookupRegion($addr) + { + $ipnum = ip2long($addr); + if ($ipnum === false) { + throw new PEAR_Exception("Invalid IP address: " . var_export($addr, true), self::ERR_INVALID_IP); + } + if ($this->databaseType !== self::REGION_EDITION_REV0 && $this->databaseType !== self::REGION_EDITION_REV1) { + throw new PEAR_Exception("Invalid database type; lookupRegion() method expects Region database.", self::ERR_DB_FORMAT); + } + return $this->getRegion($ipnum); + } + + /** + * Lookup the location record for given IP address. + * Use this method if you have a City database. + * + * @param string $addr IP address (hostname not allowed). + * + * @return Net_GeoIP_Location The full location record. + * + * @throws PEAR_Exception - if IP address is invalid. + */ + public function lookupLocation($addr) + { + include_once 'Net/GeoIP/Location.php'; + $ipnum = ip2long($addr); + if ($ipnum === false) { + throw new PEAR_Exception("Invalid IP address: " . var_export($addr, true), self::ERR_INVALID_IP); + } + if ($this->databaseType !== self::CITY_EDITION_REV0 && $this->databaseType !== self::CITY_EDITION_REV1) { + throw new PEAR_Exception("Invalid database type; lookupLocation() method expects City database."); + } + return $this->getRecord($ipnum); + } + + /** + * Seek and return organization (or ISP) name for converted IP addr. + * + * @param int $ipnum Converted IP address. + * + * @return string The organization + */ + protected function getOrg($ipnum) + { + $seek_org = $this->seekCountry($ipnum); + if ($seek_org == $this->databaseSegments) { + return null; + } + $record_pointer = $seek_org + (2 * $this->recordLength - 1) * $this->databaseSegments; + if ($this->flags & self::SHARED_MEMORY) { + $org_buf = shmop_read($this->shmid, $record_pointer, self::MAX_ORG_RECORD_LENGTH); + } else { + fseek($this->filehandle, $record_pointer, SEEK_SET); + $org_buf = fread($this->filehandle, self::MAX_ORG_RECORD_LENGTH); + } + $org_buf = substr($org_buf, 0, strpos($org_buf, 0)); + return $org_buf; + } + + /** + * Seek and return the region info (array containing country code and region name) for converted IP addr. + * + * @param int $ipnum Converted IP address. + * + * @return array Array containing country code and region: array($country_code, $region) + */ + protected function getRegion($ipnum) + { + if ($this->databaseType == self::REGION_EDITION_REV0) { + $seek_region = $this->seekCountry($ipnum) - self::STATE_BEGIN_REV0; + if ($seek_region >= 1000) { + $country_code = "US"; + $region = chr(($seek_region - 1000)/26 + 65) . chr(($seek_region - 1000)%26 + 65); + } else { + $country_code = self::$COUNTRY_CODES[$seek_region]; + $region = ""; + } + return array($country_code, $region); + } elseif ($this->databaseType == self::REGION_EDITION_REV1) { + $seek_region = $this->seekCountry($ipnum) - self::STATE_BEGIN_REV1; + //print $seek_region; + if ($seek_region < self::US_OFFSET) { + $country_code = ""; + $region = ""; + } elseif ($seek_region < self::CANADA_OFFSET) { + $country_code = "US"; + $region = chr(($seek_region - self::US_OFFSET)/26 + 65) . chr(($seek_region - self::US_OFFSET)%26 + 65); + } elseif ($seek_region < self::WORLD_OFFSET) { + $country_code = "CA"; + $region = chr(($seek_region - self::CANADA_OFFSET)/26 + 65) . chr(($seek_region - self::CANADA_OFFSET)%26 + 65); + } else { + $country_code = self::$COUNTRY_CODES[($seek_region - self::WORLD_OFFSET) / self::FIPS_RANGE]; + $region = ""; + } + return array ($country_code,$region); + } + } + + /** + * Seek and populate Net_GeoIP_Location object for converted IP addr. + * Note: this + * + * @param int $ipnum Converted IP address. + * + * @return Net_GeoIP_Location + */ + protected function getRecord($ipnum) + { + $seek_country = $this->seekCountry($ipnum); + if ($seek_country == $this->databaseSegments) { + return null; + } + + $record_pointer = $seek_country + (2 * $this->recordLength - 1) * $this->databaseSegments; + + if ($this->flags & self::SHARED_MEMORY) { + $record_buf = shmop_read($this->shmid, $record_pointer, self::FULL_RECORD_LENGTH); + } else { + fseek($this->filehandle, $record_pointer, SEEK_SET); + $record_buf = fread($this->filehandle, self::FULL_RECORD_LENGTH); + } + + $record = new Net_GeoIP_Location(); + + $record_buf_pos = 0; + $char = ord(substr($record_buf, $record_buf_pos, 1)); + + $record->countryCode = self::$COUNTRY_CODES[$char]; + $record->countryCode3 = self::$COUNTRY_CODES3[$char]; + $record->countryName = self::$COUNTRY_NAMES[$char]; + $record_buf_pos++; + $str_length = 0; + + //get region + $char = ord(substr($record_buf, $record_buf_pos+$str_length, 1)); + while ($char != 0) { + $str_length++; + $char = ord(substr($record_buf, $record_buf_pos+$str_length, 1)); + } + if ($str_length > 0) { + $record->region = substr($record_buf, $record_buf_pos, $str_length); + } + $record_buf_pos += $str_length + 1; + $str_length = 0; + + //get city + $char = ord(substr($record_buf, $record_buf_pos+$str_length, 1)); + while ($char != 0) { + $str_length++; + $char = ord(substr($record_buf, $record_buf_pos+$str_length, 1)); + } + if ($str_length > 0) { + $record->city = substr($record_buf, $record_buf_pos, $str_length); + } + $record_buf_pos += $str_length + 1; + $str_length = 0; + + //get postal code + $char = ord(substr($record_buf, $record_buf_pos+$str_length, 1)); + while ($char != 0) { + $str_length++; + $char = ord(substr($record_buf, $record_buf_pos+$str_length, 1)); + } + if ($str_length > 0) { + $record->postalCode = substr($record_buf, $record_buf_pos, $str_length); + } + $record_buf_pos += $str_length + 1; + $str_length = 0; + $latitude = 0; + $longitude = 0; + for ($j = 0;$j < 3; ++$j) { + $char = ord(substr($record_buf, $record_buf_pos++, 1)); + $latitude += ($char << ($j * 8)); + } + $record->latitude = ($latitude/10000) - 180; + + for ($j = 0;$j < 3; ++$j) { + $char = ord(substr($record_buf, $record_buf_pos++, 1)); + $longitude += ($char << ($j * 8)); + } + $record->longitude = ($longitude/10000) - 180; + + if ($this->databaseType === self::CITY_EDITION_REV1) { + $dmaarea_combo = 0; + if ($record->countryCode == "US") { + for ($j = 0;$j < 3;++$j) { + $char = ord(substr($record_buf, $record_buf_pos++, 1)); + $dmaarea_combo += ($char << ($j * 8)); + } + $record->dmaCode = floor($dmaarea_combo/1000); + $record->areaCode = $dmaarea_combo%1000; + } + } + + return $record; + } + +} + diff --git a/lib/pear/Net/GeoIP/DMA.php b/lib/pear/Net/GeoIP/DMA.php index 412254df088..253f7b573c2 100644 --- a/lib/pear/Net/GeoIP/DMA.php +++ b/lib/pear/Net/GeoIP/DMA.php @@ -1,315 +1,315 @@ - (original Maxmind version) | - * | Hans Lellelid | - * +----------------------------------------------------------------------+ - * - * @category Net - * @package Net_GeoIP - * @author Hans Lellelid - * @license LGPL http://www.gnu.org/licenses/lgpl.txt - * @link http://pear.php.net/package/Net_GeoIp - * $Id$ - */ - -/** - * Static class to handle mapping of DMA codes to metro regions. - * - * Use this class with the dmaCode property of the Net_GeoIpLocation object. - * - * - * $region = Net_GeoIPDMA::getMetroRegion($record->dmaCode); - * - * - * @category Net - * @package Net_GeoIP - * @author Hans Lellelid - * @author Dmitri Snytkine - * @license LGPL http://www.gnu.org/licenses/lgpl.txt - * @version $Revision$ - * @link http://pear.php.net/package/Net_GeoIp - */ -class Net_GeoIP_DMA -{ - /** - * Holds DMA -> Metro mapping. - * @var array - */ - private static $dmaMap; - - /** - * Initialize - * - * @return void - */ - public static function initialize() - { - self::$dmaMap = array( - 500 => 'Portland-Auburn, ME', - 501 => 'New York, NY', - 502 => 'Binghamton, NY', - 503 => 'Macon, GA', - 504 => 'Philadelphia, PA', - 505 => 'Detroit, MI', - 506 => 'Boston, MA', - 507 => 'Savannah, GA', - 508 => 'Pittsburgh, PA', - 509 => 'Ft Wayne, IN', - 510 => 'Cleveland, OH', - 511 => 'Washington, DC', - 512 => 'Baltimore, MD', - 513 => 'Flint, MI', - 514 => 'Buffalo, NY', - 515 => 'Cincinnati, OH', - 516 => 'Erie, PA', - 517 => 'Charlotte, NC', - 518 => 'Greensboro, NC', - 519 => 'Charleston, SC', - 520 => 'Augusta, GA', - 521 => 'Providence, RI', - 522 => 'Columbus, GA', - 523 => 'Burlington, VT', - 524 => 'Atlanta, GA', - 525 => 'Albany, GA', - 526 => 'Utica-Rome, NY', - 527 => 'Indianapolis, IN', - 528 => 'Miami, FL', - 529 => 'Louisville, KY', - 530 => 'Tallahassee, FL', - 531 => 'Tri-Cities, TN', - 532 => 'Albany-Schenectady-Troy, NY', - 533 => 'Hartford, CT', - 534 => 'Orlando, FL', - 535 => 'Columbus, OH', - 536 => 'Youngstown-Warren, OH', - 537 => 'Bangor, ME', - 538 => 'Rochester, NY', - 539 => 'Tampa, FL', - 540 => 'Traverse City-Cadillac, MI', - 541 => 'Lexington, KY', - 542 => 'Dayton, OH', - 543 => 'Springfield-Holyoke, MA', - 544 => 'Norfolk-Portsmouth, VA', - 545 => 'Greenville-New Bern-Washington, NC', - 546 => 'Columbia, SC', - 547 => 'Toledo, OH', - 548 => 'West Palm Beach, FL', - 549 => 'Watertown, NY', - 550 => 'Wilmington, NC', - 551 => 'Lansing, MI', - 552 => 'Presque Isle, ME', - 553 => 'Marquette, MI', - 554 => 'Wheeling, WV', - 555 => 'Syracuse, NY', - 556 => 'Richmond-Petersburg, VA', - 557 => 'Knoxville, TN', - 558 => 'Lima, OH', - 559 => 'Bluefield-Beckley-Oak Hill, WV', - 560 => 'Raleigh-Durham, NC', - 561 => 'Jacksonville, FL', - 563 => 'Grand Rapids, MI', - 564 => 'Charleston-Huntington, WV', - 565 => 'Elmira, NY', - 566 => 'Harrisburg-Lancaster-Lebanon-York, PA', - 567 => 'Greenville-Spartenburg, SC', - 569 => 'Harrisonburg, VA', - 570 => 'Florence-Myrtle Beach, SC', - 571 => 'Ft Myers, FL', - 573 => 'Roanoke-Lynchburg, VA', - 574 => 'Johnstown-Altoona, PA', - 575 => 'Chattanooga, TN', - 576 => 'Salisbury, MD', - 577 => 'Wilkes Barre-Scranton, PA', - 581 => 'Terre Haute, IN', - 582 => 'Lafayette, IN', - 583 => 'Alpena, MI', - 584 => 'Charlottesville, VA', - 588 => 'South Bend, IN', - 592 => 'Gainesville, FL', - 596 => 'Zanesville, OH', - 597 => 'Parkersburg, WV', - 598 => 'Clarksburg-Weston, WV', - 600 => 'Corpus Christi, TX', - 602 => 'Chicago, IL', - 603 => 'Joplin-Pittsburg, MO', - 604 => 'Columbia-Jefferson City, MO', - 605 => 'Topeka, KS', - 606 => 'Dothan, AL', - 609 => 'St Louis, MO', - 610 => 'Rockford, IL', - 611 => 'Rochester-Mason City-Austin, MN', - 612 => 'Shreveport, LA', - 613 => 'Minneapolis-St Paul, MN', - 616 => 'Kansas City, MO', - 617 => 'Milwaukee, WI', - 618 => 'Houston, TX', - 619 => 'Springfield, MO', - 620 => 'Tuscaloosa, AL', - 622 => 'New Orleans, LA', - 623 => 'Dallas-Fort Worth, TX', - 624 => 'Sioux City, IA', - 625 => 'Waco-Temple-Bryan, TX', - 626 => 'Victoria, TX', - 627 => 'Wichita Falls, TX', - 628 => 'Monroe, LA', - 630 => 'Birmingham, AL', - 631 => 'Ottumwa-Kirksville, IA', - 632 => 'Paducah, KY', - 633 => 'Odessa-Midland, TX', - 634 => 'Amarillo, TX', - 635 => 'Austin, TX', - 636 => 'Harlingen, TX', - 637 => 'Cedar Rapids-Waterloo, IA', - 638 => 'St Joseph, MO', - 639 => 'Jackson, TN', - 640 => 'Memphis, TN', - 641 => 'San Antonio, TX', - 642 => 'Lafayette, LA', - 643 => 'Lake Charles, LA', - 644 => 'Alexandria, LA', - 646 => 'Anniston, AL', - 647 => 'Greenwood-Greenville, MS', - 648 => 'Champaign-Springfield-Decatur, IL', - 649 => 'Evansville, IN', - 650 => 'Oklahoma City, OK', - 651 => 'Lubbock, TX', - 652 => 'Omaha, NE', - 656 => 'Panama City, FL', - 657 => 'Sherman, TX', - 658 => 'Green Bay-Appleton, WI', - 659 => 'Nashville, TN', - 661 => 'San Angelo, TX', - 662 => 'Abilene-Sweetwater, TX', - 669 => 'Madison, WI', - 670 => 'Ft Smith-Fay-Springfield, AR', - 671 => 'Tulsa, OK', - 673 => 'Columbus-Tupelo-West Point, MS', - 675 => 'Peoria-Bloomington, IL', - 676 => 'Duluth, MN', - 678 => 'Wichita, KS', - 679 => 'Des Moines, IA', - 682 => 'Davenport-Rock Island-Moline, IL', - 686 => 'Mobile, AL', - 687 => 'Minot-Bismarck-Dickinson, ND', - 691 => 'Huntsville, AL', - 692 => 'Beaumont-Port Author, TX', - 693 => 'Little Rock-Pine Bluff, AR', - 698 => 'Montgomery, AL', - 702 => 'La Crosse-Eau Claire, WI', - 705 => 'Wausau-Rhinelander, WI', - 709 => 'Tyler-Longview, TX', - 710 => 'Hattiesburg-Laurel, MS', - 711 => 'Meridian, MS', - 716 => 'Baton Rouge, LA', - 717 => 'Quincy, IL', - 718 => 'Jackson, MS', - 722 => 'Lincoln-Hastings, NE', - 724 => 'Fargo-Valley City, ND', - 725 => 'Sioux Falls, SD', - 734 => 'Jonesboro, AR', - 736 => 'Bowling Green, KY', - 737 => 'Mankato, MN', - 740 => 'North Platte, NE', - 743 => 'Anchorage, AK', - 744 => 'Honolulu, HI', - 745 => 'Fairbanks, AK', - 746 => 'Biloxi-Gulfport, MS', - 747 => 'Juneau, AK', - 749 => 'Laredo, TX', - 751 => 'Denver, CO', - 752 => 'Colorado Springs, CO', - 753 => 'Phoenix, AZ', - 754 => 'Butte-Bozeman, MT', - 755 => 'Great Falls, MT', - 756 => 'Billings, MT', - 757 => 'Boise, ID', - 758 => 'Idaho Falls-Pocatello, ID', - 759 => 'Cheyenne, WY', - 760 => 'Twin Falls, ID', - 762 => 'Missoula, MT', - 764 => 'Rapid City, SD', - 765 => 'El Paso, TX', - 766 => 'Helena, MT', - 767 => 'Casper-Riverton, WY', - 770 => 'Salt Lake City, UT', - 771 => 'Yuma, AZ', - 773 => 'Grand Junction, CO', - 789 => 'Tucson, AZ', - 790 => 'Albuquerque, NM', - 798 => 'Glendive, MT', - 800 => 'Bakersfield, CA', - 801 => 'Eugene, OR', - 802 => 'Eureka, CA', - 803 => 'Los Angeles, CA', - 804 => 'Palm Springs, CA', - 807 => 'San Francisco, CA', - 810 => 'Yakima-Pasco, WA', - 811 => 'Reno, NV', - 813 => 'Medford-Klamath Falls, OR', - 819 => 'Seattle-Tacoma, WA', - 820 => 'Portland, OR', - 821 => 'Bend, OR', - 825 => 'San Diego, CA', - 828 => 'Monterey-Salinas, CA', - 839 => 'Las Vegas, NV', - 855 => 'Santa Barbara, CA', - 862 => 'Sacramento, CA', - 866 => 'Fresno, CA', - 868 => 'Chico-Redding, CA', - 881 => 'Spokane, WA'); - } - - /** - * Lookup the metro region based on the provided DMA code. - * - * @param int $dmaCode The DMA code - * - * @return string Metro region name. - */ - public static function getMetroRegion($dmaCode) - { - if ($dmaCode === null) { - return null; - } - if (self::$dmaMap === null) { - self::initialize(); - } - return self::$dmaMap[$dmaCode]; - } - - /** - * Reverse lookup of DMA code if [exact] metro region name is known. - * - * @param string $metro Metro region name. - * - * @return int DMA code, or false if not found. - */ - public static function getDMACode($metro) - { - if (self::$dmaMap === null) { - self::initialize(); - } - return array_search($metro, self::$dmaMap); - } - + (original Maxmind version) | + * | Hans Lellelid | + * +----------------------------------------------------------------------+ + * + * @category Net + * @package Net_GeoIP + * @author Hans Lellelid + * @license LGPL http://www.gnu.org/licenses/lgpl.txt + * @link http://pear.php.net/package/Net_GeoIp + * $Id$ + */ + +/** + * Static class to handle mapping of DMA codes to metro regions. + * + * Use this class with the dmaCode property of the Net_GeoIpLocation object. + * + * + * $region = Net_GeoIPDMA::getMetroRegion($record->dmaCode); + * + * + * @category Net + * @package Net_GeoIP + * @author Hans Lellelid + * @author Dmitri Snytkine + * @license LGPL http://www.gnu.org/licenses/lgpl.txt + * @version $Revision$ + * @link http://pear.php.net/package/Net_GeoIp + */ +class Net_GeoIP_DMA +{ + /** + * Holds DMA -> Metro mapping. + * @var array + */ + private static $dmaMap; + + /** + * Initialize + * + * @return void + */ + public static function initialize() + { + self::$dmaMap = array( + 500 => 'Portland-Auburn, ME', + 501 => 'New York, NY', + 502 => 'Binghamton, NY', + 503 => 'Macon, GA', + 504 => 'Philadelphia, PA', + 505 => 'Detroit, MI', + 506 => 'Boston, MA', + 507 => 'Savannah, GA', + 508 => 'Pittsburgh, PA', + 509 => 'Ft Wayne, IN', + 510 => 'Cleveland, OH', + 511 => 'Washington, DC', + 512 => 'Baltimore, MD', + 513 => 'Flint, MI', + 514 => 'Buffalo, NY', + 515 => 'Cincinnati, OH', + 516 => 'Erie, PA', + 517 => 'Charlotte, NC', + 518 => 'Greensboro, NC', + 519 => 'Charleston, SC', + 520 => 'Augusta, GA', + 521 => 'Providence, RI', + 522 => 'Columbus, GA', + 523 => 'Burlington, VT', + 524 => 'Atlanta, GA', + 525 => 'Albany, GA', + 526 => 'Utica-Rome, NY', + 527 => 'Indianapolis, IN', + 528 => 'Miami, FL', + 529 => 'Louisville, KY', + 530 => 'Tallahassee, FL', + 531 => 'Tri-Cities, TN', + 532 => 'Albany-Schenectady-Troy, NY', + 533 => 'Hartford, CT', + 534 => 'Orlando, FL', + 535 => 'Columbus, OH', + 536 => 'Youngstown-Warren, OH', + 537 => 'Bangor, ME', + 538 => 'Rochester, NY', + 539 => 'Tampa, FL', + 540 => 'Traverse City-Cadillac, MI', + 541 => 'Lexington, KY', + 542 => 'Dayton, OH', + 543 => 'Springfield-Holyoke, MA', + 544 => 'Norfolk-Portsmouth, VA', + 545 => 'Greenville-New Bern-Washington, NC', + 546 => 'Columbia, SC', + 547 => 'Toledo, OH', + 548 => 'West Palm Beach, FL', + 549 => 'Watertown, NY', + 550 => 'Wilmington, NC', + 551 => 'Lansing, MI', + 552 => 'Presque Isle, ME', + 553 => 'Marquette, MI', + 554 => 'Wheeling, WV', + 555 => 'Syracuse, NY', + 556 => 'Richmond-Petersburg, VA', + 557 => 'Knoxville, TN', + 558 => 'Lima, OH', + 559 => 'Bluefield-Beckley-Oak Hill, WV', + 560 => 'Raleigh-Durham, NC', + 561 => 'Jacksonville, FL', + 563 => 'Grand Rapids, MI', + 564 => 'Charleston-Huntington, WV', + 565 => 'Elmira, NY', + 566 => 'Harrisburg-Lancaster-Lebanon-York, PA', + 567 => 'Greenville-Spartenburg, SC', + 569 => 'Harrisonburg, VA', + 570 => 'Florence-Myrtle Beach, SC', + 571 => 'Ft Myers, FL', + 573 => 'Roanoke-Lynchburg, VA', + 574 => 'Johnstown-Altoona, PA', + 575 => 'Chattanooga, TN', + 576 => 'Salisbury, MD', + 577 => 'Wilkes Barre-Scranton, PA', + 581 => 'Terre Haute, IN', + 582 => 'Lafayette, IN', + 583 => 'Alpena, MI', + 584 => 'Charlottesville, VA', + 588 => 'South Bend, IN', + 592 => 'Gainesville, FL', + 596 => 'Zanesville, OH', + 597 => 'Parkersburg, WV', + 598 => 'Clarksburg-Weston, WV', + 600 => 'Corpus Christi, TX', + 602 => 'Chicago, IL', + 603 => 'Joplin-Pittsburg, MO', + 604 => 'Columbia-Jefferson City, MO', + 605 => 'Topeka, KS', + 606 => 'Dothan, AL', + 609 => 'St Louis, MO', + 610 => 'Rockford, IL', + 611 => 'Rochester-Mason City-Austin, MN', + 612 => 'Shreveport, LA', + 613 => 'Minneapolis-St Paul, MN', + 616 => 'Kansas City, MO', + 617 => 'Milwaukee, WI', + 618 => 'Houston, TX', + 619 => 'Springfield, MO', + 620 => 'Tuscaloosa, AL', + 622 => 'New Orleans, LA', + 623 => 'Dallas-Fort Worth, TX', + 624 => 'Sioux City, IA', + 625 => 'Waco-Temple-Bryan, TX', + 626 => 'Victoria, TX', + 627 => 'Wichita Falls, TX', + 628 => 'Monroe, LA', + 630 => 'Birmingham, AL', + 631 => 'Ottumwa-Kirksville, IA', + 632 => 'Paducah, KY', + 633 => 'Odessa-Midland, TX', + 634 => 'Amarillo, TX', + 635 => 'Austin, TX', + 636 => 'Harlingen, TX', + 637 => 'Cedar Rapids-Waterloo, IA', + 638 => 'St Joseph, MO', + 639 => 'Jackson, TN', + 640 => 'Memphis, TN', + 641 => 'San Antonio, TX', + 642 => 'Lafayette, LA', + 643 => 'Lake Charles, LA', + 644 => 'Alexandria, LA', + 646 => 'Anniston, AL', + 647 => 'Greenwood-Greenville, MS', + 648 => 'Champaign-Springfield-Decatur, IL', + 649 => 'Evansville, IN', + 650 => 'Oklahoma City, OK', + 651 => 'Lubbock, TX', + 652 => 'Omaha, NE', + 656 => 'Panama City, FL', + 657 => 'Sherman, TX', + 658 => 'Green Bay-Appleton, WI', + 659 => 'Nashville, TN', + 661 => 'San Angelo, TX', + 662 => 'Abilene-Sweetwater, TX', + 669 => 'Madison, WI', + 670 => 'Ft Smith-Fay-Springfield, AR', + 671 => 'Tulsa, OK', + 673 => 'Columbus-Tupelo-West Point, MS', + 675 => 'Peoria-Bloomington, IL', + 676 => 'Duluth, MN', + 678 => 'Wichita, KS', + 679 => 'Des Moines, IA', + 682 => 'Davenport-Rock Island-Moline, IL', + 686 => 'Mobile, AL', + 687 => 'Minot-Bismarck-Dickinson, ND', + 691 => 'Huntsville, AL', + 692 => 'Beaumont-Port Author, TX', + 693 => 'Little Rock-Pine Bluff, AR', + 698 => 'Montgomery, AL', + 702 => 'La Crosse-Eau Claire, WI', + 705 => 'Wausau-Rhinelander, WI', + 709 => 'Tyler-Longview, TX', + 710 => 'Hattiesburg-Laurel, MS', + 711 => 'Meridian, MS', + 716 => 'Baton Rouge, LA', + 717 => 'Quincy, IL', + 718 => 'Jackson, MS', + 722 => 'Lincoln-Hastings, NE', + 724 => 'Fargo-Valley City, ND', + 725 => 'Sioux Falls, SD', + 734 => 'Jonesboro, AR', + 736 => 'Bowling Green, KY', + 737 => 'Mankato, MN', + 740 => 'North Platte, NE', + 743 => 'Anchorage, AK', + 744 => 'Honolulu, HI', + 745 => 'Fairbanks, AK', + 746 => 'Biloxi-Gulfport, MS', + 747 => 'Juneau, AK', + 749 => 'Laredo, TX', + 751 => 'Denver, CO', + 752 => 'Colorado Springs, CO', + 753 => 'Phoenix, AZ', + 754 => 'Butte-Bozeman, MT', + 755 => 'Great Falls, MT', + 756 => 'Billings, MT', + 757 => 'Boise, ID', + 758 => 'Idaho Falls-Pocatello, ID', + 759 => 'Cheyenne, WY', + 760 => 'Twin Falls, ID', + 762 => 'Missoula, MT', + 764 => 'Rapid City, SD', + 765 => 'El Paso, TX', + 766 => 'Helena, MT', + 767 => 'Casper-Riverton, WY', + 770 => 'Salt Lake City, UT', + 771 => 'Yuma, AZ', + 773 => 'Grand Junction, CO', + 789 => 'Tucson, AZ', + 790 => 'Albuquerque, NM', + 798 => 'Glendive, MT', + 800 => 'Bakersfield, CA', + 801 => 'Eugene, OR', + 802 => 'Eureka, CA', + 803 => 'Los Angeles, CA', + 804 => 'Palm Springs, CA', + 807 => 'San Francisco, CA', + 810 => 'Yakima-Pasco, WA', + 811 => 'Reno, NV', + 813 => 'Medford-Klamath Falls, OR', + 819 => 'Seattle-Tacoma, WA', + 820 => 'Portland, OR', + 821 => 'Bend, OR', + 825 => 'San Diego, CA', + 828 => 'Monterey-Salinas, CA', + 839 => 'Las Vegas, NV', + 855 => 'Santa Barbara, CA', + 862 => 'Sacramento, CA', + 866 => 'Fresno, CA', + 868 => 'Chico-Redding, CA', + 881 => 'Spokane, WA'); + } + + /** + * Lookup the metro region based on the provided DMA code. + * + * @param int $dmaCode The DMA code + * + * @return string Metro region name. + */ + public static function getMetroRegion($dmaCode) + { + if ($dmaCode === null) { + return null; + } + if (self::$dmaMap === null) { + self::initialize(); + } + return self::$dmaMap[$dmaCode]; + } + + /** + * Reverse lookup of DMA code if [exact] metro region name is known. + * + * @param string $metro Metro region name. + * + * @return int DMA code, or false if not found. + */ + public static function getDMACode($metro) + { + if (self::$dmaMap === null) { + self::initialize(); + } + return array_search($metro, self::$dmaMap); + } + } \ No newline at end of file diff --git a/lib/pear/Net/GeoIP/Location.php b/lib/pear/Net/GeoIP/Location.php index 2b70f6d3a57..c02598b487a 100644 --- a/lib/pear/Net/GeoIP/Location.php +++ b/lib/pear/Net/GeoIP/Location.php @@ -1,201 +1,201 @@ - (original Maxmind version) | - * | Hans Lellelid | - * +----------------------------------------------------------------------+ - * - * @category Net - * @package Net_GeoIP - * @author Hans Lellelid - * @license LGPL http://www.gnu.org/licenses/lgpl.txt - * @link http://pear.php.net/package/Net_GeoIp - * $Id$ - */ - -/** - * This class represents a location record as returned by Net_GeoIP::lookupLocation(). - * - * This class is primarily a collection of values (the public properties of the class), but - * there is also a distance() method to calculate the km distance between two points. - * - * @category Net - * @package Net_GeoIP - * @author Hans Lellelid - * @author Dmitri Snytkine - * @license LGPL http://www.gnu.org/licenses/lgpl.txt - * @version $Revision$ - * @link http://pear.php.net/package/Net_GeoIp - * @see Net_GeoIP::lookupLocation() - */ -class Net_GeoIP_Location implements Serializable -{ - protected $aData = array( - 'countryCode' => null, - 'countryCode3' => null, - 'countryName' => null, - 'region' => null, - 'city' => null, - 'postalCode' => null, - 'latitude' => null, - 'longitude' => null, - 'areaCode' => null, - 'dmaCode' => null - ); - - - /** - * Calculate the distance in km between two points. - * - * @param Net_GeoIP_Location $loc The other point to which distance will be calculated. - * - * @return float The number of km between two points on the globe. - */ - public function distance(Net_GeoIP_Location $loc) - { - // ideally these should be class constants, but class constants - // can't be operations. - $RAD_CONVERT = M_PI / 180; - $EARTH_DIAMETER = 2 * 6378.2; - - $lat1 = $this->latitude; - $lon1 = $this->longitude; - $lat2 = $loc->latitude; - $lon2 = $loc->longitude; - - // convert degrees to radians - $lat1 *= $RAD_CONVERT; - $lat2 *= $RAD_CONVERT; - - // find the deltas - $delta_lat = $lat2 - $lat1; - $delta_lon = ($lon2 - $lon1) * $RAD_CONVERT; - - // Find the great circle distance - $temp = pow(sin($delta_lat/2), 2) + cos($lat1) * cos($lat2) * pow(sin($delta_lon/2), 2); - return $EARTH_DIAMETER * atan2(sqrt($temp), sqrt(1-$temp)); - } - - /** - * magic method to make it possible - * to store this object in cache when - * automatic serialization is on - * Specifically it makes it possible to store - * this object in memcache - * - * @return array - */ - public function serialize() - { - return serialize($this->aData); - } - - /** - * unserialize a representation of the object - * - * @param array $serialized The serialized representation of the location - * - * @return void - */ - public function unserialize($serialized) - { - $this->aData = unserialize($serialized); - } - - - /** - * Setter for elements of $this->aData array - * - * @param string $name The variable to set - * @param string $val The value - * - * @return object $this object - */ - public function set($name, $val) - { - if (array_key_exists($name, $this->aData)) { - $this->aData[$name] = $val; - } - - return $this; - } - - public function __set($name, $val) - { - return $this->set($name, $val); - } - - /** - * Getter for $this->aData array - * - * @return array - */ - public function getData() - { - return $this->aData; - } - - - /** - * Magic method to get value from $this->aData array - * - * @param string $name The var to get - * - * @return mixed string if value exists or null if it is empty of - * just does not exist - */ - public function __get($name) - { - if (array_key_exists($name, $this->aData)) { - return $this->aData[$name]; - } - - return null; - } - - - /** - * String representation of the object - * - * @return string text and result of print_r of $this->aData array - */ - public function __toString() - { - return 'object of type '.__CLASS__.'. data: '.implode(',', $this->aData); - } - - - /** - * Magic method - * makes it possible to check if specific record exists - * and also makes it possible to use empty() on any property - * - * @param strign $name The name of the var to check - * - * @return bool - */ - public function __isset($name) - { - return (null !== $this->__get($name)); - } - -} + (original Maxmind version) | + * | Hans Lellelid | + * +----------------------------------------------------------------------+ + * + * @category Net + * @package Net_GeoIP + * @author Hans Lellelid + * @license LGPL http://www.gnu.org/licenses/lgpl.txt + * @link http://pear.php.net/package/Net_GeoIp + * $Id$ + */ + +/** + * This class represents a location record as returned by Net_GeoIP::lookupLocation(). + * + * This class is primarily a collection of values (the public properties of the class), but + * there is also a distance() method to calculate the km distance between two points. + * + * @category Net + * @package Net_GeoIP + * @author Hans Lellelid + * @author Dmitri Snytkine + * @license LGPL http://www.gnu.org/licenses/lgpl.txt + * @version $Revision$ + * @link http://pear.php.net/package/Net_GeoIp + * @see Net_GeoIP::lookupLocation() + */ +class Net_GeoIP_Location implements Serializable +{ + protected $aData = array( + 'countryCode' => null, + 'countryCode3' => null, + 'countryName' => null, + 'region' => null, + 'city' => null, + 'postalCode' => null, + 'latitude' => null, + 'longitude' => null, + 'areaCode' => null, + 'dmaCode' => null + ); + + + /** + * Calculate the distance in km between two points. + * + * @param Net_GeoIP_Location $loc The other point to which distance will be calculated. + * + * @return float The number of km between two points on the globe. + */ + public function distance(Net_GeoIP_Location $loc) + { + // ideally these should be class constants, but class constants + // can't be operations. + $RAD_CONVERT = M_PI / 180; + $EARTH_DIAMETER = 2 * 6378.2; + + $lat1 = $this->latitude; + $lon1 = $this->longitude; + $lat2 = $loc->latitude; + $lon2 = $loc->longitude; + + // convert degrees to radians + $lat1 *= $RAD_CONVERT; + $lat2 *= $RAD_CONVERT; + + // find the deltas + $delta_lat = $lat2 - $lat1; + $delta_lon = ($lon2 - $lon1) * $RAD_CONVERT; + + // Find the great circle distance + $temp = pow(sin($delta_lat/2), 2) + cos($lat1) * cos($lat2) * pow(sin($delta_lon/2), 2); + return $EARTH_DIAMETER * atan2(sqrt($temp), sqrt(1-$temp)); + } + + /** + * magic method to make it possible + * to store this object in cache when + * automatic serialization is on + * Specifically it makes it possible to store + * this object in memcache + * + * @return array + */ + public function serialize() + { + return serialize($this->aData); + } + + /** + * unserialize a representation of the object + * + * @param array $serialized The serialized representation of the location + * + * @return void + */ + public function unserialize($serialized) + { + $this->aData = unserialize($serialized); + } + + + /** + * Setter for elements of $this->aData array + * + * @param string $name The variable to set + * @param string $val The value + * + * @return object $this object + */ + public function set($name, $val) + { + if (array_key_exists($name, $this->aData)) { + $this->aData[$name] = $val; + } + + return $this; + } + + public function __set($name, $val) + { + return $this->set($name, $val); + } + + /** + * Getter for $this->aData array + * + * @return array + */ + public function getData() + { + return $this->aData; + } + + + /** + * Magic method to get value from $this->aData array + * + * @param string $name The var to get + * + * @return mixed string if value exists or null if it is empty of + * just does not exist + */ + public function __get($name) + { + if (array_key_exists($name, $this->aData)) { + return $this->aData[$name]; + } + + return null; + } + + + /** + * String representation of the object + * + * @return string text and result of print_r of $this->aData array + */ + public function __toString() + { + return 'object of type '.__CLASS__.'. data: '.implode(',', $this->aData); + } + + + /** + * Magic method + * makes it possible to check if specific record exists + * and also makes it possible to use empty() on any property + * + * @param strign $name The name of the var to check + * + * @return bool + */ + public function __isset($name) + { + return (null !== $this->__get($name)); + } + +}