1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Asymptix\tools\geo; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Location detection functionality. Provides functionality to detect location |
7
|
|
|
* by IP address with using 3rd party services. |
8
|
|
|
* |
9
|
|
|
* @category Asymptix PHP Framework |
10
|
|
|
* @author Dmytro Zarezenko <[email protected]> |
11
|
|
|
* @copyright (c) 2016, Dmytro Zarezenko |
12
|
|
|
* |
13
|
|
|
* @git https://github.com/Asymptix/Framework |
14
|
|
|
* @license http://opensource.org/licenses/MIT |
15
|
|
|
*/ |
16
|
|
|
class LocationDetector { |
17
|
|
|
|
18
|
|
|
const PROVIDER_IPINFO_IO = "http://ipinfo.io/[IP]/json"; |
19
|
|
|
const PROVIDER_GEOPLUGIN_NET = "http://www.geoplugin.net/json.gp?ip=[IP]"; |
20
|
|
|
const PROVIDER_FREEGEOIP_IO = "http://freegeoip.io/json/[IP]"; |
21
|
|
|
const TYPE_FULL_OBJECT = 0; |
22
|
|
|
const TYPE_FULL_ARRAY = 1; |
23
|
|
|
const TYPE_LOCATION = 2; |
24
|
|
|
const TYPE_ADDRESS = 3; |
25
|
|
|
const TYPE_CITY = 4; |
26
|
|
|
const TYPE_STATE = 5; |
27
|
|
|
const TYPE_REGION = 6; |
28
|
|
|
const TYPE_COUNTRY = 7; |
29
|
|
|
private static $continents = [ |
30
|
|
|
'AF' => "Africa", |
31
|
|
|
'AN' => "Antarctica", |
32
|
|
|
'AS' => "Asia", |
33
|
|
|
'EU' => "Europe", |
34
|
|
|
'OC' => "Australia (Oceania)", |
35
|
|
|
'NA' => "North America", |
36
|
|
|
'SA' => "South America" |
37
|
|
|
]; |
38
|
|
|
|
39
|
|
|
private $provider = null; |
40
|
|
|
|
41
|
|
|
public function __construct($provider = self::PROVIDER_GEOPLUGIN_NET) { |
42
|
|
|
$this->provider = $provider; |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
public function get($ip, $type) { |
46
|
|
|
if (is_null($this->provider)) { |
47
|
|
|
throw new LocationDetectorException("Invalid data provider."); |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
return self::_get($ip, $type, $this->provider); |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
public static function _get($ip, $type, $provider) { |
54
|
|
|
if (!filter_var($ip, FILTER_VALIDATE_IP)) { |
55
|
|
|
throw new LocationDetectorException("Invalid IP address."); |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
$response = file_get_contents(str_replace('[IP]', $ip, $provider)); |
59
|
|
|
if ($response === false) { |
60
|
|
|
throw new LocationDetectorException("Can't receive response from data provider."); |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
$geoData = @json_decode($response); |
64
|
|
|
switch ($provider) { |
65
|
|
|
case (self::PROVIDER_GEOPLUGIN_NET): |
66
|
|
|
if (@strlen(trim($geoData->geoplugin_countryCode)) == 2) { |
67
|
|
|
switch ($type) { |
68
|
|
|
case (self::TYPE_FULL_OBJECT): |
69
|
|
|
return $geoData; |
70
|
|
|
case (self::TYPE_FULL_ARRAY): |
71
|
|
|
return @json_decode($response, true); |
72
|
|
|
case (self::TYPE_LOCATION): |
73
|
|
|
return [ |
74
|
|
|
"city" => @$geoData->geoplugin_city, |
75
|
|
|
"state" => @$geoData->geoplugin_regionName, |
76
|
|
|
"country" => @$geoData->geoplugin_countryName, |
77
|
|
|
"country_code" => @$geoData->geoplugin_countryCode, |
78
|
|
|
"continent" => @self::$continents[strtoupper($geoData->geoplugin_continentCode)], |
79
|
|
|
"continent_code" => @$geoData->geoplugin_continentCode |
80
|
|
|
]; |
81
|
|
|
case (self::TYPE_ADDRESS): |
82
|
|
|
$address = [$geoData->geoplugin_countryName]; |
83
|
|
|
if (@strlen($geoData->geoplugin_regionName) >= 1) { |
84
|
|
|
$address[] = $geoData->geoplugin_regionName; |
85
|
|
|
} |
86
|
|
|
if (@strlen($geoData->geoplugin_city) >= 1) { |
87
|
|
|
$address[] = $geoData->geoplugin_city; |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
return implode(", ", array_reverse($address)); |
91
|
|
|
case (self::TYPE_CITY): |
92
|
|
|
return @$geoData->geoplugin_city; |
93
|
|
|
case (self::TYPE_STATE): |
94
|
|
|
case (self::TYPE_REGION): |
95
|
|
|
return @$geoData->geoplugin_regionName; |
96
|
|
|
case (self::TYPE_COUNTRY): |
97
|
|
|
return [ |
98
|
|
|
'country' => @$geoData->geoplugin_countryName, |
99
|
|
|
'country_code' => @$geoData->geoplugin_countryCode |
100
|
|
|
]; |
101
|
|
|
default: |
102
|
|
|
throw new LocationDetectorException("Invalid return type."); |
103
|
|
|
} |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
return null; |
107
|
|
|
|
108
|
|
View Code Duplication |
case (self::PROVIDER_IPINFO_IO): |
|
|
|
|
109
|
|
|
if (@strlen(trim($geoData->country)) == 2) { |
110
|
|
|
switch ($type) { |
111
|
|
|
case (self::TYPE_FULL_OBJECT): |
112
|
|
|
return $geoData; |
113
|
|
|
case (self::TYPE_FULL_ARRAY): |
114
|
|
|
return @json_decode($response, true); |
115
|
|
|
default: |
116
|
|
|
throw new LocationDetectorException("Invalid return type."); |
117
|
|
|
} |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
return null; |
121
|
|
|
|
122
|
|
View Code Duplication |
case (self::PROVIDER_FREEGEOIP_IO): |
|
|
|
|
123
|
|
|
if (@strlen(trim($geoData->country_code)) == 2) { |
124
|
|
|
switch ($type) { |
125
|
|
|
case (self::TYPE_FULL_OBJECT): |
126
|
|
|
return $geoData; |
127
|
|
|
case (self::TYPE_FULL_ARRAY): |
128
|
|
|
return @json_decode($response, true); |
129
|
|
|
default: |
130
|
|
|
throw new LocationDetectorException("Invalid return type."); |
131
|
|
|
} |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
return null; |
135
|
|
|
} |
136
|
|
|
} |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
class LocationDetectorException extends \Exception {} |
|
|
|
|
140
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.