22
33namespace OpenCage \Geocoder ;
44
5- use OpenCage \Geocoder \AbstractGeocoder ;
5+ use GuzzleHttp \Client ;
6+ use GuzzleHttp \Exception \ConnectException ;
67
7- class Geocoder extends AbstractGeocoder
8+ class Geocoder
89{
10+ public const VERSION = '3.4.0 ' ; // if changing this => remember to match everything with the git tag
11+
12+ public const TIMEOUT = 10 ;
13+ public const URL = 'https://api.opencagedata.com/geocode/v1/json/? ' ;
14+ public const PROXY = null ;
15+
16+ protected $ key ;
17+ protected $ timeout ;
18+ protected $ url ;
19+ protected $ proxy ;
20+ protected $ user_agent ;
21+ protected $ client ;
22+
23+ public function __construct ($ key = null )
24+ {
25+ if (isset ($ key ) && !empty ($ key )) {
26+ $ this ->setKey ($ key );
27+ }
28+ $ this ->setTimeout (self ::TIMEOUT );
29+ $ this ->url = self ::URL ;
30+ $ this ->user_agent = 'opencage-php/ ' . self ::VERSION . ' (PHP ' . phpversion () . '; ' . php_uname ('s ' ) . ' ' . php_uname ('r ' ) . ') ' ;
31+ }
32+
33+ public function setKey ($ key )
34+ {
35+ $ this ->key = $ key ;
36+ }
37+
38+ public function setTimeout ($ timeout )
39+ {
40+ $ this ->timeout = $ timeout ;
41+ $ this ->client = null ;
42+ }
43+
44+ public function setProxy ($ proxy )
45+ {
46+ $ parsed = parse_url ($ proxy );
47+ if (
48+ $ parsed === false
49+ || !isset ($ parsed ['scheme ' ])
50+ || !in_array ($ parsed ['scheme ' ], ['http ' , 'https ' , 'socks5 ' ], true )
51+ || !isset ($ parsed ['host ' ])
52+ ) {
53+ throw new \Exception ('Invalid proxy URL: must include a valid scheme (http, https, or socks5) and host ' );
54+ }
55+ $ this ->proxy = $ proxy ;
56+ $ this ->client = null ;
57+ }
58+
59+ public function setHost ($ host )
60+ {
61+ if (!$ this ->isValidHost ($ host )) {
62+ throw new \Exception ('Invalid host: must be localhost or an opencagedata.com subdomain ' );
63+ }
64+ $ this ->url = str_replace ('api.opencagedata.com ' , $ host , self ::URL );
65+ $ this ->client = null ;
66+ }
67+
968 public function geocode ($ query , $ optParams = [])
1069 {
1170 $ url = $ this ->url . 'q= ' . urlencode ($ query );
@@ -24,4 +83,67 @@ public function geocode($query, $optParams = [])
2483 $ ret = json_decode ($ this ->getJSON ($ url ), true );
2584 return $ ret ;
2685 }
86+
87+ protected function isValidHost ($ host )
88+ {
89+ if (in_array ($ host , ['localhost ' , '127.0.0.1 ' , '0.0.0.0 ' , '::1 ' ], true )) {
90+ return true ;
91+ }
92+
93+ // localhost with port
94+ if (preg_match ('/^(localhost|127\.0\.0\.1|0\.0\.0\.0)\:\d+$/ ' , $ host )) {
95+ return true ;
96+ }
97+
98+ // opencagedata.com or any subdomain
99+ if (preg_match ('/^([a-zA-Z0-9-]+\.)*opencagedata\.com$/ ' , $ host )) {
100+ return true ;
101+ }
102+
103+ return false ;
104+ }
105+
106+ protected function buildClient ()
107+ {
108+ $ config = [
109+ 'timeout ' => $ this ->timeout ,
110+ 'verify ' => true ,
111+ 'headers ' => [
112+ 'User-Agent ' => $ this ->user_agent ,
113+ ],
114+ ];
115+
116+ if ($ this ->proxy ) {
117+ $ config ['proxy ' ] = $ this ->proxy ;
118+ }
119+
120+ return new Client ($ config );
121+ }
122+
123+ protected function getJSON ($ query )
124+ {
125+ if ($ this ->client === null ) {
126+ $ this ->client = $ this ->buildClient ();
127+ }
128+
129+ try {
130+ $ response = $ this ->client ->get ($ query , ['http_errors ' => false ]);
131+ return (string ) $ response ->getBody ();
132+ } catch (ConnectException $ e ) {
133+ return $ this ->generateErrorJSON (498 , 'network issue ' . $ e ->getMessage ());
134+ }
135+ }
136+
137+ protected function generateErrorJSON ($ code , $ message )
138+ {
139+ $ response = [
140+ 'results ' => [],
141+ 'total_results ' => 0 ,
142+ 'status ' => [
143+ 'code ' => $ code ,
144+ 'message ' => $ message
145+ ]
146+ ];
147+ return json_encode ($ response );
148+ }
27149}
0 commit comments