Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]

   1  <?php
   2  
   3  declare(strict_types=1);
   4  
   5  namespace GeoIp2\WebService;
   6  
   7  use GeoIp2\Exception\AddressNotFoundException;
   8  use GeoIp2\Exception\AuthenticationException;
   9  use GeoIp2\Exception\GeoIp2Exception;
  10  use GeoIp2\Exception\HttpException;
  11  use GeoIp2\Exception\InvalidRequestException;
  12  use GeoIp2\Exception\OutOfQueriesException;
  13  use GeoIp2\Model\City;
  14  use GeoIp2\Model\Country;
  15  use GeoIp2\Model\Insights;
  16  use GeoIp2\ProviderInterface;
  17  use MaxMind\WebService\Client as WsClient;
  18  
  19  /**
  20   * This class provides a client API for all the GeoIP2 web services.
  21   * The services are Country, City Plus, and Insights. Each service returns
  22   * a different set of data about an IP address, with Country returning the
  23   * least data and Insights the most.
  24   *
  25   * Each web service is represented by a different model class, and these model
  26   * classes in turn contain multiple record classes. The record classes have
  27   * attributes which contain data about the IP address.
  28   *
  29   * If the web service does not return a particular piece of data for an IP
  30   * address, the associated attribute is not populated.
  31   *
  32   * The web service may not return any information for an entire record, in
  33   * which case all of the attributes for that record class will be empty.
  34   *
  35   * ## Usage ##
  36   *
  37   * The basic API for this class is the same for all of the web service end
  38   * points. First you create a web service object with your MaxMind `$accountId`
  39   * and `$licenseKey`, then you call the method corresponding to a specific end
  40   * point, passing it the IP address you want to look up.
  41   *
  42   * If the request succeeds, the method call will return a model class for
  43   * the service you called. This model in turn contains multiple record
  44   * classes, each of which represents part of the data returned by the web
  45   * service.
  46   *
  47   * If the request fails, the client class throws an exception.
  48   */
  49  class Client implements ProviderInterface
  50  {
  51      /**
  52       * @var array<string>
  53       */
  54      private $locales;
  55  
  56      /**
  57       * @var WsClient
  58       */
  59      private $client;
  60  
  61      /**
  62       * @var string
  63       */
  64      private static $basePath = '/geoip/v2.1';
  65  
  66      public const VERSION = 'v2.13.0';
  67  
  68      /**
  69       * Constructor.
  70       *
  71       * @param int    $accountId  your MaxMind account ID
  72       * @param string $licenseKey your MaxMind license key
  73       * @param array  $locales    list of locale codes to use in name property
  74       *                           from most preferred to least preferred
  75       * @param array  $options    array of options. Valid options include:
  76       *                           * `host` - The host to use when querying the web
  77       *                           service. To query the GeoLite2 web service
  78       *                           instead of the GeoIP2 web service, set the
  79       *                           host to `geolite.info`.
  80       *                           * `timeout` - Timeout in seconds.
  81       *                           * `connectTimeout` - Initial connection timeout in seconds.
  82       *                           * `proxy` - The HTTP proxy to use. May include a schema, port,
  83       *                           username, and password, e.g.,
  84       *                           `http://username:password@127.0.0.1:10`.
  85       */
  86      public function __construct(
  87          int $accountId,
  88          string $licenseKey,
  89          array $locales = ['en'],
  90          array $options = []
  91      ) {
  92          $this->locales = $locales;
  93  
  94          // This is for backwards compatibility. Do not remove except for a
  95          // major version bump.
  96          // @phpstan-ignore-next-line
  97          if (\is_string($options)) {
  98              $options = ['host' => $options];
  99          }
 100  
 101          if (!isset($options['host'])) {
 102              $options['host'] = 'geoip.maxmind.com';
 103          }
 104  
 105          $options['userAgent'] = $this->userAgent();
 106  
 107          $this->client = new WsClient($accountId, $licenseKey, $options);
 108      }
 109  
 110      private function userAgent(): string
 111      {
 112          return 'GeoIP2-API/' . self::VERSION;
 113      }
 114  
 115      /**
 116       * This method calls the City Plus service.
 117       *
 118       * @param string $ipAddress IPv4 or IPv6 address as a string. If no
 119       *                          address is provided, the address that the web service is called
 120       *                          from will be used.
 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 account 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 received by the web service but is
 129       *                                                    invalid for some other reason.  This may indicate an issue
 130       *                                                    with this API. Please report the error to MaxMind.
 131       * @throws \GeoIp2\Exception\HttpException   if an unexpected HTTP error code or message was returned.
 132       *                                           This could indicate a problem with the connection between
 133       *                                           your server and the web service or that the web service
 134       *                                           returned an invalid document or 500 error code
 135       * @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent
 136       *                                           class to the above exceptions. It will be thrown directly
 137       *                                           if a 200 status code is returned but the body is invalid.
 138       */
 139      public function city(string $ipAddress = 'me'): City
 140      {
 141          // @phpstan-ignore-next-line
 142          return $this->responseFor('city', City::class, $ipAddress);
 143      }
 144  
 145      /**
 146       * This method calls the Country service.
 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       * @throws \GeoIp2\Exception\AddressNotFoundException if the address you provided is not in our database (e.g.,
 153       *                                                    a private address).
 154       * @throws \GeoIp2\Exception\AuthenticationException  if there is a problem
 155       *                                                    with the account ID or license key that you provided
 156       * @throws \GeoIp2\Exception\OutOfQueriesException    if your account is out of queries
 157       * @throws \GeoIp2\Exception\InvalidRequestException} if your request was received by the web service but is
 158       *                                                    invalid for some other reason.  This may indicate an
 159       *                                                    issue with this API. Please report the error to MaxMind.
 160       * @throws \GeoIp2\Exception\HttpException   if an unexpected HTTP error
 161       *                                           code or message was returned. This could indicate a problem
 162       *                                           with the connection between your server and the web service
 163       *                                           or that the web service returned an invalid document or 500
 164       *                                           error code.
 165       * @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent class to the above exceptions. It
 166       *                                           will be thrown directly if a 200 status code is returned but
 167       *                                           the body is invalid.
 168       */
 169      public function country(string $ipAddress = 'me'): Country
 170      {
 171          return $this->responseFor('country', Country::class, $ipAddress);
 172      }
 173  
 174      /**
 175       * This method calls the Insights service. Insights is only supported by
 176       * the GeoIP2 web service. The GeoLite2 web service does not support it.
 177       *
 178       * @param string $ipAddress IPv4 or IPv6 address as a string. If no
 179       *                          address is provided, the address that the web service is called
 180       *                          from will be used.
 181       *
 182       * @throws \GeoIp2\Exception\AddressNotFoundException if the address you
 183       *                                                    provided is not in our database (e.g., a private address).
 184       * @throws \GeoIp2\Exception\AuthenticationException  if there is a problem
 185       *                                                    with the account ID or license key that you provided
 186       * @throws \GeoIp2\Exception\OutOfQueriesException    if your account is out
 187       *                                                    of queries
 188       * @throws \GeoIp2\Exception\InvalidRequestException} if your request was received by the web service but is
 189       *                                                    invalid for some other reason.  This may indicate an
 190       *                                                    issue with this API. Please report the error to MaxMind.
 191       * @throws \GeoIp2\Exception\HttpException   if an unexpected HTTP error code or message was returned.
 192       *                                           This could indicate a problem with the connection between
 193       *                                           your server and the web service or that the web service
 194       *                                           returned an invalid document or 500 error code
 195       * @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent
 196       *                                           class to the above exceptions. It will be thrown directly
 197       *                                           if a 200 status code is returned but the body is invalid.
 198       */
 199      public function insights(string $ipAddress = 'me'): Insights
 200      {
 201          // @phpstan-ignore-next-line
 202          return $this->responseFor('insights', Insights::class, $ipAddress);
 203      }
 204  
 205      private function responseFor(string $endpoint, string $class, string $ipAddress): Country
 206      {
 207          $path = implode('/', [self::$basePath, $endpoint, $ipAddress]);
 208  
 209          try {
 210              $service = (new \ReflectionClass($class))->getShortName();
 211              $body = $this->client->get('GeoIP2 ' . $service, $path);
 212          } catch (\MaxMind\Exception\IpAddressNotFoundException $ex) {
 213              throw new AddressNotFoundException(
 214                  $ex->getMessage(),
 215                  $ex->getStatusCode(),
 216                  $ex
 217              );
 218          } catch (\MaxMind\Exception\AuthenticationException $ex) {
 219              throw new AuthenticationException(
 220                  $ex->getMessage(),
 221                  $ex->getStatusCode(),
 222                  $ex
 223              );
 224          } catch (\MaxMind\Exception\InsufficientFundsException $ex) {
 225              throw new OutOfQueriesException(
 226                  $ex->getMessage(),
 227                  $ex->getStatusCode(),
 228                  $ex
 229              );
 230          } catch (\MaxMind\Exception\InvalidRequestException $ex) {
 231              throw new InvalidRequestException(
 232                  $ex->getMessage(),
 233                  $ex->getErrorCode(),
 234                  $ex->getStatusCode(),
 235                  $ex->getUri(),
 236                  $ex
 237              );
 238          } catch (\MaxMind\Exception\HttpException $ex) {
 239              throw new HttpException(
 240                  $ex->getMessage(),
 241                  $ex->getStatusCode(),
 242                  $ex->getUri(),
 243                  $ex
 244              );
 245          } catch (\MaxMind\Exception\WebServiceException $ex) {
 246              throw new GeoIp2Exception(
 247                  $ex->getMessage(),
 248                  $ex->getCode(),
 249                  $ex
 250              );
 251          }
 252  
 253          return new $class($body, $this->locales);
 254      }
 255  }