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