Overview

Namespaces

  • GeoIp2
    • Database
    • Exception
    • Model
    • Record
    • WebService
  • PHP

Classes

  • Client
  • Overview
  • Namespace
  • Class
  • Tree
  • Download
  1: <?php
  2: 
  3: namespace GeoIp2\WebService;
  4: 
  5: use GeoIp2\Exception\AddressNotFoundException;
  6: use GeoIp2\Exception\AuthenticationException;
  7: use GeoIp2\Exception\GeoIp2Exception;
  8: use GeoIp2\Exception\HttpException;
  9: use GeoIp2\Exception\InvalidRequestException;
 10: use GeoIp2\Exception\OutOfQueriesException;
 11: use GeoIp2\ProviderInterface;
 12: use Guzzle\Common\Exception\RuntimeException;
 13: use Guzzle\Http\Client as GuzzleClient;
 14: use Guzzle\Http\Exception\ClientErrorResponseException;
 15: use Guzzle\Http\Exception\ServerErrorResponseException;
 16: 
 17: /**
 18:  * This class provides a client API for all the GeoIP2 Precision web service
 19:  * end points. The end points are Country, City, and Insights. Each end point
 20:  * returns a different set of data about an IP address, with Country returning
 21:  * the least data and Insights the most.
 22:  *
 23:  * Each web service end point is represented by a different model class, and
 24:  * these model classes in turn contain multiple Record classes. The record
 25:  * classes have attributes which contain data about the IP address.
 26:  *
 27:  * If the web service does not return a particular piece of data for an IP
 28:  * address, the associated attribute is not populated.
 29:  *
 30:  * The web service may not return any information for an entire record, in
 31:  * which case all of the attributes for that record class will be empty.
 32:  *
 33:  * **Usage**
 34:  *
 35:  * The basic API for this class is the same for all of the web service end
 36:  * points. First you create a web service object with your MaxMind
 37:  * <code>$userId</code> and <code>$licenseKey</code>, then you call the method
 38:  * corresponding to a specific end point, passing it the IP address you want
 39:  * to look up.
 40:  *
 41:  * If the request succeeds, the method call will return a model class for
 42:  * the end point you called. This model in turn contains multiple record
 43:  * classes, each of which represents part of the data returned by the web
 44:  * service.
 45:  *
 46:  * If the request fails, the client class throws an exception.
 47:  */
 48: class Client implements ProviderInterface
 49: {
 50:     private $userId;
 51:     private $licenseKey;
 52:     private $locales;
 53:     private $host;
 54:     private $guzzleClient;
 55: 
 56:     /**
 57:      * Constructor.
 58:      *
 59:      * @param int $userId     Your MaxMind user ID
 60:      * @param string $licenseKey Your MaxMind license key
 61:      * @param array $locales  List of locale codes to use in name property
 62:      * from most preferred to least preferred.
 63:      * @param string $host Optional host parameter
 64:      * @param object $guzzleClient Optional Guzzle client to use (to facilitate
 65:      * unit testing).
 66:      */
 67:     public function __construct(
 68:         $userId,
 69:         $licenseKey,
 70:         $locales = array('en'),
 71:         $host = 'geoip.maxmind.com',
 72:         $guzzleClient = null
 73:     ) {
 74:         $this->userId = $userId;
 75:         $this->licenseKey = $licenseKey;
 76:         $this->locales = $locales;
 77:         $this->host = $host;
 78:         // To enable unit testing
 79:         $this->guzzleClient = $guzzleClient;
 80:     }
 81: 
 82:     /**
 83:      * This method calls the GeoIP2 Precision: City endpoint.
 84:      *
 85:      * @param string $ipAddress IPv4 or IPv6 address as a string. If no
 86:      * address is provided, the address that the web service is called
 87:      * from will be used.
 88:      *
 89:      * @return \GeoIp2\Model\City
 90:      *
 91:      * @throws \GeoIp2\Exception\AddressNotFoundException if the address you
 92:      *   provided is not in our database (e.g., a private address).
 93:      * @throws \GeoIp2\Exception\AuthenticationException if there is a problem
 94:      *   with the user ID or license key that you provided.
 95:      * @throws \GeoIp2\Exception\OutOfQueriesException if your account is out
 96:      *   of queries.
 97:      * @throws \GeoIp2\Exception\InvalidRequestException} if your request was
 98:      *   received by the web service but is invalid for some other reason.
 99:      *   This may indicate an issue with this API. Please report the error to
100:      *   MaxMind.
101:      * @throws \GeoIp2\Exception\HttpException if an unexpected HTTP error
102:      *   code or message was returned. This could indicate a problem with the
103:      *   connection between your server and the web service or that the web
104:      *   service returned an invalid document or 500 error code.
105:      * @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent
106:      *   class to the above exceptions. It will be thrown directly if a 200
107:      *   status code is returned but the body is invalid.
108:      */
109:     public function city($ipAddress = 'me')
110:     {
111:         return $this->responseFor('city', 'City', $ipAddress);
112:     }
113: 
114:     /**
115:      * This method calls the GeoIP2 Precision: City endpoint.
116:      *
117:      * @param string $ipAddress IPv4 or IPv6 address as a string. If no
118:      * address is provided, the address that the web service is called
119:      * from will be used.
120:      *
121:      * @return \GeoIp2\Model\City
122:      *
123:      * @throws \GeoIp2\Exception\AddressNotFoundException if the address you
124:      *   provided is not in our database (e.g., a private address).
125:      * @throws \GeoIp2\Exception\AuthenticationException if there is a problem
126:      *   with the user ID or license key that you provided.
127:      * @throws \GeoIp2\Exception\OutOfQueriesException if your account is out
128:      *   of queries.
129:      * @throws \GeoIp2\Exception\InvalidRequestException} if your request was
130:      *   received by the web service but is invalid for some other reason.
131:      *   This may indicate an issue with this API. Please report the error to
132:      *   MaxMind.
133:      * @throws \GeoIp2\Exception\HttpException if an unexpected HTTP error
134:      *   code or message was returned. This could indicate a problem with the
135:      *   connection between your server and the web service or that the web
136:      *   service returned an invalid document or 500 error code.
137:      * @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent
138:      *   class to the above exceptions. It will be thrown directly if a 200
139:      *   status code is returned but the body is invalid.
140:      *
141:      * @deprecated deprecated since version 0.7.0
142:      */
143:     public function cityIspOrg($ipAddress = 'me')
144:     {
145:         return $this->city($ipAddress);
146:     }
147: 
148:     /**
149:      * This method calls the GeoIP2 Precision: Country endpoint.
150:      *
151:      * @param string $ipAddress IPv4 or IPv6 address as a string. If no
152:      * address is provided, the address that the web service is called
153:      * from will be used.
154:      *
155:      * @return \GeoIp2\Model\Country
156:      *
157:      * @throws \GeoIp2\Exception\AddressNotFoundException if the address you
158:      *   provided is not in our database (e.g., a private address).
159:      * @throws \GeoIp2\Exception\AuthenticationException if there is a problem
160:      *   with the user ID or license key that you provided.
161:      * @throws \GeoIp2\Exception\OutOfQueriesException if your account is out
162:      *   of queries.
163:      * @throws \GeoIp2\Exception\InvalidRequestException} if your request was
164:      *   received by the web service but is invalid for some other reason.
165:      *   This may indicate an issue with this API. Please report the error to
166:      *   MaxMind.
167:      * @throws \GeoIp2\Exception\HttpException if an unexpected HTTP error
168:      *   code or message was returned. This could indicate a problem with the
169:      *   connection between your server and the web service or that the web
170:      *   service returned an invalid document or 500 error code.
171:      * @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent
172:      *   class to the above exceptions. It will be thrown directly if a 200
173:      *   status code is returned but the body is invalid.
174:      */
175:     public function country($ipAddress = 'me')
176:     {
177:         return $this->responseFor('country', 'Country', $ipAddress);
178:     }
179: 
180:     /**
181:      * This method calls the GeoIP2 Precision: Insights endpoint.
182:      *
183:      * @param string $ipAddress IPv4 or IPv6 address as a string. If no
184:      * address is provided, the address that the web service is called
185:      * from will be used.
186:      *
187:      * @return \GeoIp2\Model\Insights
188:      *
189:      * @throws \GeoIp2\Exception\AddressNotFoundException if the address you
190:      *   provided is not in our database (e.g., a private address).
191:      * @throws \GeoIp2\Exception\AuthenticationException if there is a problem
192:      *   with the user ID or license key that you provided.
193:      * @throws \GeoIp2\Exception\OutOfQueriesException if your account is out
194:      *   of queries.
195:      * @throws \GeoIp2\Exception\InvalidRequestException} if your request was
196:      *   received by the web service but is invalid for some other reason.
197:      *   This may indicate an issue with this API. Please report the error to
198:      *   MaxMind.
199:      * @throws \GeoIp2\Exception\HttpException if an unexpected HTTP error
200:      *   code or message was returned. This could indicate a problem with the
201:      *   connection between your server and the web service or that the web
202:      *   service returned an invalid document or 500 error code.
203:      * @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent
204:      *   class to the above exceptions. It will be thrown directly if a 200
205:      *   status code is returned but the body is invalid.
206:      *
207:      * @deprecated deprecated since version 0.7.0
208:      */
209:     public function insights($ipAddress = 'me')
210:     {
211:         return $this->responseFor('insights', 'Insights', $ipAddress);
212:     }
213: 
214:     /**
215:      * This method calls the GeoIP2 Precision: Insights (prev. Omni) endpoint.
216:      *
217:      * @param string $ipAddress IPv4 or IPv6 address as a string. If no
218:      * address is provided, the address that the web service is called
219:      * from will be used.
220:      *
221:      * @return \GeoIp2\Model\Insights
222:      *
223:      * @throws \GeoIp2\Exception\AddressNotFoundException if the address you
224:      *   provided is not in our database (e.g., a private address).
225:      * @throws \GeoIp2\Exception\AuthenticationException if there is a problem
226:      *   with the user ID or license key that you provided.
227:      * @throws \GeoIp2\Exception\OutOfQueriesException if your account is out
228:      *   of queries.
229:      * @throws \GeoIp2\Exception\InvalidRequestException} if your request was
230:      *   received by the web service but is invalid for some other reason.
231:      *   This may indicate an issue with this API. Please report the error to
232:      *   MaxMind.
233:      * @throws \GeoIp2\Exception\HttpException if an unexpected HTTP error
234:      *   code or message was returned. This could indicate a problem with the
235:      *   connection between your server and the web service or that the web
236:      *   service returned an invalid document or 500 error code.
237:      * @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent
238:      *   class to the above exceptions. It will be thrown directly if a 200
239:      *   status code is returned but the body is invalid.
240:      *
241:      * @deprecated deprecated since version 0.7.0
242:      */
243:     public function omni($ipAddress = 'me')
244:     {
245:         return $this->insights($ipAddress);
246:     }
247: 
248:     private function responseFor($endpoint, $class, $ipAddress)
249:     {
250:         $uri = implode('/', array($this->baseUri(), $endpoint, $ipAddress));
251: 
252:         $client = $this->guzzleClient ?
253:             $this->guzzleClient : new GuzzleClient();
254:         $request = $client->get($uri, array('Accept' => 'application/json'));
255:         $request->setAuth($this->userId, $this->licenseKey);
256:         $this->setUserAgent($request);
257: 
258:         try {
259:             $response = $request->send();
260:         } catch (ClientErrorResponseException $e) {
261:             $this->handle4xx($e->getResponse(), $uri);
262:         } catch (ServerErrorResponseException $e) {
263:             $this->handle5xx($e->getResponse(), $uri);
264:         }
265: 
266:         if ($response && $response->isSuccessful()) {
267:             $body = $this->handleSuccess($response, $uri);
268:             $class = "GeoIp2\\Model\\" . $class;
269:             return new $class($body, $this->locales);
270:         } else {
271:             $this->handleNon200($response, $uri);
272:         }
273:     }
274: 
275:     private function handleSuccess($response, $uri)
276:     {
277:         if ($response->getContentLength() == 0) {
278:             throw new GeoIp2Exception(
279:                 "Received a 200 response for $uri but did not " .
280:                 "receive a HTTP body."
281:             );
282:         }
283: 
284:         try {
285:             return $response->json();
286:         } catch (RuntimeException $e) {
287:             throw new GeoIp2Exception(
288:                 "Received a 200 response for $uri but could not decode " .
289:                 "the response as JSON: " . $e->getMessage()
290:             );
291: 
292:         }
293:     }
294: 
295:     private function handle4xx($response, $uri)
296:     {
297:         $status = $response->getStatusCode();
298: 
299:         if ($response->getContentLength() > 0) {
300:             if (strstr($response->getContentType(), 'json')) {
301:                 try {
302:                     $body = $response->json();
303:                     if (!isset($body['code']) || !isset($body['error'])) {
304:                         throw new GeoIp2Exception(
305:                             'Response contains JSON but it does not specify ' .
306:                             'code or error keys: ' . $response->getBody()
307:                         );
308:                     }
309:                 } catch (RuntimeException $e) {
310:                     throw new HttpException(
311:                         "Received a $status error for $uri but it did not " .
312:                         "include the expected JSON body: " .
313:                         $e->getMessage(),
314:                         $status,
315:                         $uri
316:                     );
317:                 }
318:             } else {
319:                 throw new HttpException(
320:                     "Received a $status error for $uri with the " .
321:                     "following body: " . $response->getBody(),
322:                     $status,
323:                     $uri
324:                 );
325:             }
326:         } else {
327:             throw new HttpException(
328:                 "Received a $status error for $uri with no body",
329:                 $status,
330:                 $uri
331:             );
332:         }
333:         $this->handleWebServiceError(
334:             $body['error'],
335:             $body['code'],
336:             $status,
337:             $uri
338:         );
339:     }
340: 
341:     private function handleWebServiceError($message, $code, $status, $uri)
342:     {
343:         switch ($code) {
344:             case 'IP_ADDRESS_NOT_FOUND':
345:             case 'IP_ADDRESS_RESERVED':
346:                 throw new AddressNotFoundException($message);
347:             case 'AUTHORIZATION_INVALID':
348:             case 'LICENSE_KEY_REQUIRED':
349:             case 'USER_ID_REQUIRED':
350:                 throw new AuthenticationException($message);
351:             case 'OUT_OF_QUERIES':
352:                 throw new OutOfQueriesException($message);
353:             default:
354:                 throw new InvalidRequestException(
355:                     $message,
356:                     $code,
357:                     $status,
358:                     $uri
359:                 );
360:         }
361:     }
362: 
363:     private function handle5xx($response, $uri)
364:     {
365:         $status = $response->getStatusCode();
366: 
367:         throw new HttpException(
368:             "Received a server error ($status) for $uri",
369:             $status,
370:             $uri
371:         );
372:     }
373: 
374:     private function handleNon200($response, $uri)
375:     {
376:         $status = $response->getStatusCode();
377: 
378:         throw new HttpException(
379:             "Received a very surprising HTTP status " .
380:             "($status) for $uri",
381:             $status,
382:             $uri
383:         );
384:     }
385: 
386:     private function setUserAgent($request)
387:     {
388:         $userAgent = $request->getHeader('User-Agent');
389:         $userAgent = "GeoIP2 PHP API ($userAgent)";
390:         $request->setHeader('User-Agent', $userAgent);
391:     }
392: 
393:     private function baseUri()
394:     {
395:         return 'https://' . $this->host . '/geoip/v2.1';
396:     }
397: }
398: 
GeoIP2 PHP API v0.7.0 API documentation generated by ApiGen 2.8.0