Overview

Namespaces

  • GeoIP2
    • Exception
    • Model
    • Record
    • WebService
  • PHP

Classes

  • Client
  • Overview
  • Namespace
  • Class
  • Tree
  1: <?php
  2: 
  3: namespace GeoIP2\WebService;
  4: 
  5: use GeoIP2\Exception\GeoIP2Exception;
  6: use GeoIP2\Exception\HttpException;
  7: use GeoIP2\Exception\WebServiceException;
  8: use GeoIP2\Model\City;
  9: use GeoIP2\Model\CityIspOrg;
 10: use GeoIP2\Model\Country;
 11: use GeoIP2\Model\Omni;
 12: use Guzzle\Http\Client as GuzzleClient;
 13: use Guzzle\Common\Exception\RuntimeException;
 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 web service's
 19:  * end points. The end points are Country, City, City/ISP/Org, and Omni. Each
 20:  * end point returns a different set of data about an IP address, with Country
 21:  * returning the least data and Omni 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:  * **Exceptions**
 49:  *
 50:  * For details on the possible errors returned by the web service itself, see
 51:  * {@link http://dev.maxmind.com/geoip2/geoip/web-services the GeoIP2 web
 52:  * service docs}.
 53:  *
 54:  * If the web service returns an explicit error document, this is thrown as a
 55:  * {@link \GeoIP2\Exception\WebServiceException}. If some other sort of
 56:  * transport error occurs, this is thrown as a {@link
 57:  * \GeoIP2\Exception\HttpException}. The difference is that the web service
 58:  * error includes an error message and error code delivered by the web
 59:  * service. The latter is thrown when some sort of unanticipated error occurs,
 60:  * such as the web service returning a 500 or an invalid error document.
 61:  *
 62:  * If the web service returns any status code besides 200, 4xx, or 5xx, this
 63:  * also becomes a {@link \GeoIP2\Exception\HttpException}.
 64:  *
 65:  * Finally, if the web service returns a 200 but the body is invalid, the
 66:  * client throws a {@link \GeoIP2\Exception\GeoIP2Exception}.
 67:  */
 68: class Client
 69: {
 70:     private $userId;
 71:     private $licenseKey;
 72:     private $languages;
 73:     private $host;
 74:     private $guzzleClient;
 75: 
 76:     /**
 77:      * Constructor.
 78:      *
 79:      * @param int    $userId     Your MaxMind user ID
 80:      * @param string $licenseKey Your MaxMind license key
 81:      * @param array  $languages  List of language codes to use in name property
 82:      * from most preferred to least preferred.
 83:      * @param string $host Optional host parameter
 84:      * @param object $guzzleClient Optional Guzzle client to use (to facilitate
 85:      * unit testing).
 86:      */
 87:     public function __construct(
 88:         $userId,
 89:         $licenseKey,
 90:         $languages = array('en'),
 91:         $host = 'geoip.maxmind.com',
 92:         $guzzleClient = null
 93:     ) {
 94:         $this->userId = $userId;
 95:         $this->licenseKey = $licenseKey;
 96:         $this->languages = $languages;
 97:         $this->host = $host;
 98:         // To enable unit testing
 99:         $this->guzzleClient = $guzzleClient;
100:     }
101: 
102:     /**
103:      * This method calls the GeoIP2 City endpoint.
104:      *
105:      * @param string $ipAddress IPv4 or IPv6 address as a string. If no
106:      * address is provided, the address that the web service is called
107:      * from will be used.
108:      *
109:      * @return \GeoIP2\Model\City
110:      *
111:      * @throws \GeoIP2\Exception\GeoIP2Exception if there was a generic
112:      * error processing your request.
113:      * @throws \GeoIP2\Exception\HttpException if there was an HTTP transport
114:      * error.
115:      * @throws \GeoIP2\Exception\WebServiceException if an error was returned
116:      * by MaxMind's GeoIP2 web service.
117:      */
118:     public function city($ipAddress = 'me')
119:     {
120:         return $this->responseFor('city', 'City', $ipAddress);
121:     }
122: 
123:     /**
124:      * This method calls the GeoIP2 Country endpoint.
125:      *
126:      * @param string $ipAddress IPv4 or IPv6 address as a string. If no
127:      * address is provided, the address that the web service is called
128:      * from will be used.
129:      *
130:      * @return \GeoIP2\Model\Country
131:      *
132:      * @throws \GeoIP2\Exception\GeoIP2Exception if there was a generic
133:      * error processing your request.
134:      * @throws \GeoIP2\Exception\HttpException if there was an HTTP transport
135:      * error.
136:      * @throws \GeoIP2\Exception\WebServiceException if an error was returned
137:      * by MaxMind's GeoIP2 web service.
138:      */
139:     public function country($ipAddress = 'me')
140:     {
141:         return $this->responseFor('country', 'Country', $ipAddress);
142:     }
143: 
144:     /**
145:      * This method calls the GeoIP2 City/ISP/Org endpoint.
146:      *
147:      * @param string $ipAddress IPv4 or IPv6 address as a string. If no
148:      * address is provided, the address that the web service is called
149:      * from will be used.
150:      *
151:      * @return \GeoIP2\Model\CityIspOrg
152:      *
153:      * @throws \GeoIP2\Exception\GeoIP2Exception if there was a generic
154:      * error processing your request.
155:      * @throws \GeoIP2\Exception\HttpException if there was an HTTP transport
156:      * error.
157:      * @throws \GeoIP2\Exception\WebServiceException if an error was returned
158:      * by MaxMind's GeoIP2 web service.
159:      */
160:     public function cityIspOrg($ipAddress = 'me')
161:     {
162:         return $this->responseFor('city_isp_org', 'CityIspOrg', $ipAddress);
163:     }
164: 
165:     /**
166:      * This method calls the GeoIP2 Omni endpoint.
167:      *
168:      * @param string $ipAddress IPv4 or IPv6 address as a string. If no
169:      * address is provided, the address that the web service is called
170:      * from will be used.
171:      *
172:      * @return \GeoIP2\Model\Omni
173:      *
174:      * @throws \GeoIP2\Exception\GeoIP2Exception if there was a generic
175:      * error processing your request.
176:      * @throws \GeoIP2\Exception\HttpException if there was an HTTP transport
177:      * error.
178:      * @throws \GeoIP2\Exception\WebServiceException if an error was returned
179:      * by MaxMind's GeoIP2 web service.
180:      */
181:     public function omni($ipAddress = 'me')
182:     {
183:         return $this->responseFor('omni', 'Omni', $ipAddress);
184:     }
185: 
186:     private function responseFor($endpoint, $class, $ipAddress)
187:     {
188:         $uri = implode('/', array($this->baseUri(), $endpoint, $ipAddress));
189: 
190:         $client = $this->guzzleClient ?
191:             $this->guzzleClient : new GuzzleClient();
192:         $request = $client->get($uri, array('Accept' => 'application/json'));
193:         $request->setAuth($this->userId, $this->licenseKey);
194:         $ua = $request->getHeader('User-Agent');
195:         $ua = "GeoIP2 PHP API ($ua)";
196:         $request->setHeader('User-Agent', $ua);
197: 
198:         $response = null;
199:         try {
200:             $response = $request->send();
201:         } catch (ClientErrorResponseException $e) {
202:             $this->handle4xx($e->getResponse(), $uri);
203:         } catch (ServerErrorResponseException $e) {
204:             $this->handle5xx($e->getResponse(), $uri);
205:         }
206: 
207:         if ($response && $response->isSuccessful()) {
208:             $body = $this->handleSuccess($response, $uri);
209:             $class = "GeoIP2\\Model\\" . $class;
210:             return new $class($body, $this->languages);
211:         } else {
212:             $this->handleNon200($response, $uri);
213:         }
214:     }
215: 
216:     private function handleSuccess($response, $uri)
217:     {
218:         if ($response->getContentLength() == 0) {
219:             throw new GeoIP2Exception(
220:                 "Received a 200 response for $uri but did not " .
221:                 "receive a HTTP body."
222:             );
223:         }
224: 
225:         try {
226:             return $response->json();
227:         } catch (RuntimeException $e) {
228:             throw new GeoIP2Exception(
229:                 "Received a 200 response for $uri but could not decode " .
230:                 "the response as JSON: " . $e->getMessage()
231:             );
232: 
233:         }
234:     }
235: 
236:     private function handle4xx($response, $uri)
237:     {
238:         $status = $response->getStatusCode();
239: 
240:         $body = array();
241: 
242:         if ($response->getContentLength() > 0) {
243:             if (strstr($response->getContentType(), 'json')) {
244:                 try {
245:                     $body = $response->json();
246:                     if (!isset($body['code']) || !isset($body['error'])) {
247:                         throw new GeoIP2Exception(
248:                             'Response contains JSON but it does not specify ' .
249:                             'code or error keys: ' . $response->getBody()
250:                         );
251:                     }
252:                 } catch (RuntimeException $e) {
253:                     throw new HttpException(
254:                         "Received a $status error for $uri but it did not " .
255:                         "include the expected JSON body: " .
256:                         $e->getMessage(),
257:                         $status,
258:                         $uri
259:                     );
260:                 }
261:             } else {
262:                 throw new HttpException(
263:                     "Received a $status error for $uri with the " .
264:                     "following body: " . $response->getBody(),
265:                     $status,
266:                     $uri
267:                 );
268:             }
269:         } else {
270:             throw new HttpException(
271:                 "Received a $status error for $uri with no body",
272:                 $status,
273:                 $uri
274:             );
275:         }
276: 
277:         throw new WebServiceException(
278:             $body['error'],
279:             $body['code'],
280:             $status,
281:             $uri
282:         );
283:     }
284: 
285:     private function handle5xx($response, $uri)
286:     {
287:         $status = $response->getStatusCode();
288: 
289:         throw new HttpException(
290:             "Received a server error ($status) for $uri",
291:             $status,
292:             $uri
293:         );
294:     }
295: 
296:     private function handleNon200($response, $uri)
297:     {
298:         $status = $response->getStatusCode();
299: 
300:         throw new HttpException(
301:             "Received a very surprising HTTP status " .
302:             "($status) for $uri",
303:             $status,
304:             $uri
305:         );
306:     }
307: 
308:     private function baseUri()
309:     {
310:         return 'https://' . $this->host . '/geoip/v2.0';
311:     }
312: }
313: 
API documentation generated by ApiGen 2.8.0