Overview

Namespaces

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

Classes

  • Client
  • Overview
  • Namespace
  • Class
  • Tree
  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 `$userId`
 37:  * and `$licenseKey`, then you call the method corresponding to a specific end
 38:  * point, passing it the IP address you want to look up.
 39:  *
 40:  * If the request succeeds, the method call will return a model class for
 41:  * the end point you called. This model in turn contains multiple record
 42:  * classes, each of which represents part of the data returned by the web
 43:  * service.
 44:  *
 45:  * If the request fails, the client class throws an exception.
 46:  */
 47: class Client implements ProviderInterface
 48: {
 49:     private $userId;
 50:     private $licenseKey;
 51:     private $locales;
 52:     private $host;
 53:     private $guzzleClient;
 54: 
 55:     /**
 56:      * Constructor.
 57:      *
 58:      * @param int $userId     Your MaxMind user ID
 59:      * @param string $licenseKey Your MaxMind license key
 60:      * @param array $locales  List of locale codes to use in name property
 61:      * from most preferred to least preferred.
 62:      * @param string $host Optional host parameter
 63:      * @param object $guzzleClient Optional Guzzle client to use (to facilitate
 64:      * unit testing).
 65:      */
 66:     public function __construct(
 67:         $userId,
 68:         $licenseKey,
 69:         $locales = array('en'),
 70:         $host = 'geoip.maxmind.com',
 71:         $guzzleClient = null
 72:     ) {
 73:         $this->userId = $userId;
 74:         $this->licenseKey = $licenseKey;
 75:         $this->locales = $locales;
 76:         $this->host = $host;
 77:         // To enable unit testing
 78:         $this->guzzleClient = $guzzleClient;
 79:     }
 80: 
 81:     /**
 82:      * This method calls the GeoIP2 Precision: City endpoint.
 83:      *
 84:      * @param string $ipAddress IPv4 or IPv6 address as a string. If no
 85:      * address is provided, the address that the web service is called
 86:      * from will be used.
 87:      *
 88:      * @return \GeoIp2\Model\City
 89:      *
 90:      * @throws \GeoIp2\Exception\AddressNotFoundException if the address you
 91:      *   provided is not in our database (e.g., a private address).
 92:      * @throws \GeoIp2\Exception\AuthenticationException if there is a problem
 93:      *   with the user ID or license key that you provided.
 94:      * @throws \GeoIp2\Exception\OutOfQueriesException if your account is out
 95:      *   of queries.
 96:      * @throws \GeoIp2\Exception\InvalidRequestException} if your request was
 97:      *   received by the web service but is invalid for some other reason.
 98:      *   This may indicate an issue with this API. Please report the error to
 99:      *   MaxMind.
100:      * @throws \GeoIp2\Exception\HttpException if an unexpected HTTP error
101:      *   code or message was returned. This could indicate a problem with the
102:      *   connection between your server and the web service or that the web
103:      *   service returned an invalid document or 500 error code.
104:      * @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent
105:      *   class to the above exceptions. It will be thrown directly if a 200
106:      *   status code is returned but the body is invalid.
107:      */
108:     public function city($ipAddress = 'me')
109:     {
110:         return $this->responseFor('city', 'City', $ipAddress);
111:     }
112: 
113:     /**
114:      * This method calls the GeoIP2 Precision: Country endpoint.
115:      *
116:      * @param string $ipAddress IPv4 or IPv6 address as a string. If no
117:      * address is provided, the address that the web service is called
118:      * from will be used.
119:      *
120:      * @return \GeoIp2\Model\Country
121:      *
122:      * @throws \GeoIp2\Exception\AddressNotFoundException if the address you
123:      *   provided is not in our database (e.g., a private address).
124:      * @throws \GeoIp2\Exception\AuthenticationException if there is a problem
125:      *   with the user ID or license key that you provided.
126:      * @throws \GeoIp2\Exception\OutOfQueriesException if your account is out
127:      *   of queries.
128:      * @throws \GeoIp2\Exception\InvalidRequestException} if your request was
129:      *   received by the web service but is invalid for some other reason.
130:      *   This may indicate an issue with this API. Please report the error to
131:      *   MaxMind.
132:      * @throws \GeoIp2\Exception\HttpException if an unexpected HTTP error
133:      *   code or message was returned. This could indicate a problem with the
134:      *   connection between your server and the web service or that the web
135:      *   service returned an invalid document or 500 error code.
136:      * @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent
137:      *   class to the above exceptions. It will be thrown directly if a 200
138:      *   status code is returned but the body is invalid.
139:      */
140:     public function country($ipAddress = 'me')
141:     {
142:         return $this->responseFor('country', 'Country', $ipAddress);
143:     }
144: 
145:     /**
146:      * This method calls the GeoIP2 Precision: Insights endpoint.
147:      *
148:      * @param string $ipAddress IPv4 or IPv6 address as a string. If no
149:      * address is provided, the address that the web service is called
150:      * from will be used.
151:      *
152:      * @return \GeoIp2\Model\Insights
153:      *
154:      * @throws \GeoIp2\Exception\AddressNotFoundException if the address you
155:      *   provided is not in our database (e.g., a private address).
156:      * @throws \GeoIp2\Exception\AuthenticationException if there is a problem
157:      *   with the user ID or license key that you provided.
158:      * @throws \GeoIp2\Exception\OutOfQueriesException if your account is out
159:      *   of queries.
160:      * @throws \GeoIp2\Exception\InvalidRequestException} if your request was
161:      *   received by the web service but is invalid for some other reason.
162:      *   This may indicate an issue with this API. Please report the error to
163:      *   MaxMind.
164:      * @throws \GeoIp2\Exception\HttpException if an unexpected HTTP error
165:      *   code or message was returned. This could indicate a problem with the
166:      *   connection between your server and the web service or that the web
167:      *   service returned an invalid document or 500 error code.
168:      * @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent
169:      *   class to the above exceptions. It will be thrown directly if a 200
170:      *   status code is returned but the body is invalid.
171:      */
172:     public function insights($ipAddress = 'me')
173:     {
174:         return $this->responseFor('insights', 'Insights', $ipAddress);
175:     }
176: 
177:     private function responseFor($endpoint, $class, $ipAddress)
178:     {
179:         $uri = implode('/', array($this->baseUri(), $endpoint, $ipAddress));
180: 
181:         $client = $this->guzzleClient ?
182:             $this->guzzleClient : new GuzzleClient();
183:         $request = $client->get($uri, array('Accept' => 'application/json'));
184:         $request->setAuth($this->userId, $this->licenseKey);
185:         $this->setUserAgent($request);
186: 
187:         try {
188:             $response = $request->send();
189:         } catch (ClientErrorResponseException $e) {
190:             $this->handle4xx($e->getResponse(), $uri);
191:         } catch (ServerErrorResponseException $e) {
192:             $this->handle5xx($e->getResponse(), $uri);
193:         }
194: 
195:         if ($response && $response->isSuccessful()) {
196:             $body = $this->handleSuccess($response, $uri);
197:             $class = "GeoIp2\\Model\\" . $class;
198:             return new $class($body, $this->locales);
199:         } else {
200:             $this->handleNon200($response, $uri);
201:         }
202:     }
203: 
204:     private function handleSuccess($response, $uri)
205:     {
206:         if ($response->getContentLength() == 0) {
207:             throw new GeoIp2Exception(
208:                 "Received a 200 response for $uri but did not " .
209:                 "receive a HTTP body."
210:             );
211:         }
212: 
213:         try {
214:             return $response->json();
215:         } catch (RuntimeException $e) {
216:             throw new GeoIp2Exception(
217:                 "Received a 200 response for $uri but could not decode " .
218:                 "the response as JSON: " . $e->getMessage()
219:             );
220: 
221:         }
222:     }
223: 
224:     private function handle4xx($response, $uri)
225:     {
226:         $status = $response->getStatusCode();
227: 
228:         if ($response->getContentLength() > 0) {
229:             if (strstr($response->getContentType(), 'json')) {
230:                 try {
231:                     $body = $response->json();
232:                     if (!isset($body['code']) || !isset($body['error'])) {
233:                         throw new GeoIp2Exception(
234:                             'Response contains JSON but it does not specify ' .
235:                             'code or error keys: ' . $response->getBody()
236:                         );
237:                     }
238:                 } catch (RuntimeException $e) {
239:                     throw new HttpException(
240:                         "Received a $status error for $uri but it did not " .
241:                         "include the expected JSON body: " .
242:                         $e->getMessage(),
243:                         $status,
244:                         $uri
245:                     );
246:                 }
247:             } else {
248:                 throw new HttpException(
249:                     "Received a $status error for $uri with the " .
250:                     "following body: " . $response->getBody(),
251:                     $status,
252:                     $uri
253:                 );
254:             }
255:         } else {
256:             throw new HttpException(
257:                 "Received a $status error for $uri with no body",
258:                 $status,
259:                 $uri
260:             );
261:         }
262:         $this->handleWebServiceError(
263:             $body['error'],
264:             $body['code'],
265:             $status,
266:             $uri
267:         );
268:     }
269: 
270:     private function handleWebServiceError($message, $code, $status, $uri)
271:     {
272:         switch ($code) {
273:             case 'IP_ADDRESS_NOT_FOUND':
274:             case 'IP_ADDRESS_RESERVED':
275:                 throw new AddressNotFoundException($message);
276:             case 'AUTHORIZATION_INVALID':
277:             case 'LICENSE_KEY_REQUIRED':
278:             case 'USER_ID_REQUIRED':
279:                 throw new AuthenticationException($message);
280:             case 'OUT_OF_QUERIES':
281:                 throw new OutOfQueriesException($message);
282:             default:
283:                 throw new InvalidRequestException(
284:                     $message,
285:                     $code,
286:                     $status,
287:                     $uri
288:                 );
289:         }
290:     }
291: 
292:     private function handle5xx($response, $uri)
293:     {
294:         $status = $response->getStatusCode();
295: 
296:         throw new HttpException(
297:             "Received a server error ($status) for $uri",
298:             $status,
299:             $uri
300:         );
301:     }
302: 
303:     private function handleNon200($response, $uri)
304:     {
305:         $status = $response->getStatusCode();
306: 
307:         throw new HttpException(
308:             "Received a very surprising HTTP status " .
309:             "($status) for $uri",
310:             $status,
311:             $uri
312:         );
313:     }
314: 
315:     private function setUserAgent($request)
316:     {
317:         $userAgent = $request->getHeader('User-Agent');
318:         $userAgent = "GeoIP2 PHP API ($userAgent)";
319:         $request->setHeader('User-Agent', $userAgent);
320:     }
321: 
322:     private function baseUri()
323:     {
324:         return 'https://' . $this->host . '/geoip/v2.1';
325:     }
326: }
327: 
GeoIP2 PHP API v2.1.1 API documentation generated by ApiGen