/plugins/UserCountry/LocationProvider/GeoIp.php
PHP | 274 lines | 143 code | 32 blank | 99 comment | 22 complexity | feedad1b40a69be69a3d50e7da4e1a7a MD5 | raw file
Possible License(s): LGPL-3.0, JSON, MIT, GPL-3.0, LGPL-2.1, GPL-2.0, AGPL-1.0, BSD-2-Clause, BSD-3-Clause
- <?php
- /**
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- */
- namespace Piwik\Plugins\UserCountry\LocationProvider;
- use Exception;
- use Piwik\Piwik;
- use Piwik\Plugins\UserCountry\LocationProvider;
- /**
- * Base type for all GeoIP LocationProviders.
- *
- */
- abstract class GeoIp extends LocationProvider
- {
- /* For testing, use: 'http://piwik-team.s3.amazonaws.com/GeoLiteCity.dat.gz' */
- const GEO_LITE_URL = 'http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz';
- const TEST_IP = '194.57.91.215';
- public static $geoIPDatabaseDir = 'misc';
- /**
- * Stores possible database file names categorized by the type of information
- * GeoIP databases hold.
- *
- * @var array
- */
- public static $dbNames = array(
- 'loc' => array('GeoIPCity.dat', 'GeoLiteCity.dat', 'GeoIP.dat'),
- 'isp' => array('GeoIPISP.dat'),
- 'org' => array('GeoIPOrg.dat'),
- );
- /**
- * Cached region name array. Data is from geoipregionvars.php.
- *
- * @var array
- */
- private static $regionNames = null;
- /**
- * Attempts to fill in some missing information in a GeoIP location.
- *
- * This method will call LocationProvider::completeLocationResult and then
- * try to set the region name of the location if the country code & region
- * code are set.
- *
- * @param array $location The location information to modify.
- */
- public function completeLocationResult(&$location)
- {
- $this->fixupLocation($location);
- parent::completeLocationResult($location);
- // set region name if region code is set
- if (empty($location[self::REGION_NAME_KEY])
- && !empty($location[self::REGION_CODE_KEY])
- && !empty($location[self::COUNTRY_CODE_KEY])
- ) {
- $countryCode = $location[self::COUNTRY_CODE_KEY];
- $regionCode = (string)$location[self::REGION_CODE_KEY];
- $location[self::REGION_NAME_KEY] = self::getRegionNameFromCodes($countryCode, $regionCode);
- }
- }
- /**
- * Fix up data to work with our SVG maps which include 'Tib' boundaries
- */
- protected function fixupLocation(&$location)
- {
- if (!empty($location[self::REGION_CODE_KEY])
- && $location[self::REGION_CODE_KEY] == '14'
- && !empty($location[self::COUNTRY_CODE_KEY])
- && strtoupper($location[self::COUNTRY_CODE_KEY]) == 'CN'
- ) {
- $location[self::COUNTRY_CODE_KEY] = 'ti';
- $location[self::REGION_CODE_KEY] = '1';
- }
- }
- /**
- * Returns true if this provider has been setup correctly, the error message if
- * otherwise.
- *
- * @return bool|string
- */
- public function isWorking()
- {
- // test with an example IP to make sure the provider is working
- // NOTE: At the moment only country, region & city info is tested.
- try {
- $supportedInfo = $this->getSupportedLocationInfo();
- list($testIp, $expectedResult) = self::getTestIpAndResult();
- // get location using test IP
- $location = $this->getLocation(array('ip' => $testIp));
- // check that result is the same as expected
- $isResultCorrect = true;
- foreach ($expectedResult as $key => $value) {
- // if this provider is not configured to support this information type, skip it
- if (empty($supportedInfo[$key])) {
- continue;
- }
- if (empty($location[$key])
- || $location[$key] != $value
- ) {
- $isResultCorrect = false;
- }
- }
- if (!$isResultCorrect) {
- $unknown = Piwik::translate('General_Unknown');
- $location = "'"
- . (empty($location[self::CITY_NAME_KEY]) ? $unknown : $location[self::CITY_NAME_KEY])
- . ", "
- . (empty($location[self::REGION_CODE_KEY]) ? $unknown : $location[self::REGION_CODE_KEY])
- . ", "
- . (empty($location[self::COUNTRY_CODE_KEY]) ? $unknown : $location[self::COUNTRY_CODE_KEY])
- . "'";
- $expectedLocation = "'" . $expectedResult[self::CITY_NAME_KEY] . ", "
- . $expectedResult[self::REGION_CODE_KEY] . ", "
- . $expectedResult[self::COUNTRY_CODE_KEY] . "'";
- $bind = array($testIp, $location, $expectedLocation);
- return Piwik::translate('UserCountry_TestIPLocatorFailed', $bind);
- }
- return true;
- } catch (Exception $ex) {
- return $ex->getMessage();
- }
- }
- /**
- * Returns a region name for a country code + region code.
- *
- * @param string $countryCode
- * @param string $regionCode
- * @return string The region name or 'Unknown' (translated).
- */
- public static function getRegionNameFromCodes($countryCode, $regionCode)
- {
- $regionNames = self::getRegionNames();
- $countryCode = strtoupper($countryCode);
- $regionCode = strtoupper($regionCode);
- if (isset($regionNames[$countryCode][$regionCode])) {
- return $regionNames[$countryCode][$regionCode];
- } else {
- return Piwik::translate('General_Unknown');
- }
- }
- /**
- * Returns an array of region names mapped by country code & region code.
- *
- * @return array
- */
- public static function getRegionNames()
- {
- if (is_null(self::$regionNames)) {
- $GEOIP_REGION_NAME = array();
- require_once PIWIK_INCLUDE_PATH . '/libs/MaxMindGeoIP/geoipregionvars.php';
- self::$regionNames = $GEOIP_REGION_NAME;
- }
- return self::$regionNames;
- }
- /**
- * Returns the path of an existing GeoIP database or false if none can be found.
- *
- * @param array $possibleFileNames The list of possible file names for the GeoIP database.
- * @return string|false
- */
- public static function getPathToGeoIpDatabase($possibleFileNames)
- {
- foreach ($possibleFileNames as $filename) {
- $path = self::getPathForGeoIpDatabase($filename);
- if (file_exists($path)) {
- return $path;
- }
- }
- return false;
- }
- /**
- * Returns full path for a GeoIP database managed by Piwik.
- *
- * @param string $filename Name of the .dat file.
- * @return string
- */
- public static function getPathForGeoIpDatabase($filename)
- {
- return PIWIK_INCLUDE_PATH . '/' . self::$geoIPDatabaseDir . '/' . $filename;
- }
- /**
- * Returns test IP used by isWorking and expected result.
- *
- * @return array eg. array('1.2.3.4', array(self::COUNTRY_CODE_KEY => ...))
- */
- private static function getTestIpAndResult()
- {
- static $result = null;
- if (is_null($result)) {
- // TODO: what happens when IP changes? should we get this information from piwik.org?
- $expected = array(self::COUNTRY_CODE_KEY => 'FR',
- self::REGION_CODE_KEY => 'A6',
- self::CITY_NAME_KEY => 'Besanรงon');
- $result = array(self::TEST_IP, $expected);
- }
- return $result;
- }
- /**
- * Returns true if there is a GeoIP database in the 'misc' directory.
- *
- * @return bool
- */
- public static function isDatabaseInstalled()
- {
- return self::getPathToGeoIpDatabase(self::$dbNames['loc'])
- || self::getPathToGeoIpDatabase(self::$dbNames['isp'])
- || self::getPathToGeoIpDatabase(self::$dbNames['org']);
- }
- /**
- * Returns the type of GeoIP database ('loc', 'isp' or 'org') based on the
- * filename (eg, 'GeoLiteCity.dat', 'GeoIPISP.dat', etc).
- *
- * @param string $filename
- * @return string|false 'loc', 'isp', 'org', or false if cannot find a database
- * type.
- */
- public static function getGeoIPDatabaseTypeFromFilename($filename)
- {
- foreach (self::$dbNames as $key => $names) {
- foreach ($names as $name) {
- if ($name === $filename) {
- return $key;
- }
- }
- }
- return false;
- }
- }
- /**
- * @see plugins/UserCountry/LocationProvider/GeoIp/ServerBased.php
- */
- require_once PIWIK_INCLUDE_PATH . '/plugins/UserCountry/LocationProvider/GeoIp/ServerBased.php';
- /**
- * @see plugins/UserCountry/LocationProvider/GeoIp/Php.php
- */
- require_once PIWIK_INCLUDE_PATH . '/plugins/UserCountry/LocationProvider/GeoIp/Php.php';
- /**
- * @see plugins/UserCountry/LocationProvider/GeoIp/Pecl.php
- */
- require_once PIWIK_INCLUDE_PATH . '/plugins/UserCountry/LocationProvider/GeoIp/Pecl.php';