You are here

class SmartIp in Smart IP 8.3

Same name and namespace in other branches
  1. 8.4 src/SmartIp.php \Drupal\smart_ip\SmartIp
  2. 8.2 src/SmartIp.php \Drupal\smart_ip\SmartIp

Smart IP static basic methods wrapper.

@package Drupal\smart_ip

Hierarchy

Expanded class hierarchy of SmartIp

6 files declare their use of SmartIp
DeviceGeolocation.php in modules/device_geolocation/src/DeviceGeolocation.php
Contains \Drupal\device_geolocation\DeviceGeolocation.
DeviceGeolocationController.php in modules/device_geolocation/src/Controller/DeviceGeolocationController.php
Contains \Drupal\device_geolocation\Controller\DeviceGeolocationController.
device_geolocation.module in modules/device_geolocation/device_geolocation.module
Provides visitor's geographical location using client device location source that implements W3C Geolocation API and Google Geocoding service.
GeolocateUserSubscriber.php in src/EventSubscriber/GeolocateUserSubscriber.php
Contains \Drupal\smart_ip\EventSubscriber\GeolocateUserSubscriber.
SmartIpAdminSettingsForm.php in src/Form/SmartIpAdminSettingsForm.php
Contains \Drupal\smart_ip\Form\SmartIpAdminSettingsForm.

... See full list

File

src/SmartIp.php, line 19
Contains \Drupal\smart_ip\SmartIp.

Namespace

Drupal\smart_ip
View source
class SmartIp {

  /**
   * Get the geolocation from the IP address.
   *
   * @param string $ip
   *   IP address to query for geolocation. It will use current user's IP
   *   address if empty.
   * @return array
   *   Geolocation details of queried IP address.
   */
  public static function query($ip = NULL) {
    $ip = trim($ip);
    if (empty($ip)) {
      $ip = \Drupal::request()
        ->getClientIp();
    }

    // Skip querying if IP address is in exclude list
    $excludedIps = \Drupal::configFactory()
      ->get('smart_ip.settings')
      ->get('excluded_ips');
    if (!empty($excludedIps)) {
      $patternsQuoted = preg_quote($excludedIps, '/');
      $patterns = '/^(' . preg_replace('/(\\r\\n?|\\n)/', '|', $patternsQuoted) . ')$/';
      if ((bool) preg_match($patterns, $ip)) {
        return array();
      }
    }

    // Use a static cache if this function is called more often
    // for the same ip on the same page.
    $results =& drupal_static(__FUNCTION__);
    if (isset($results[$ip])) {
      return $results[$ip];
    }

    /** @var \Drupal\smart_ip\GetLocationEvent $event */
    $event = \Drupal::service('smart_ip.get_location_event');
    $location = $event
      ->getLocation();
    $location
      ->set('source', SmartIpLocationInterface::SMART_IP)
      ->set('ipAddress', $ip)
      ->set('ipVersion', self::ipAddressVersion($ip))
      ->set('timestamp', \Drupal::time()
      ->getRequestTime());

    // Allow Smart IP source module populate the variable.
    \Drupal::service('event_dispatcher')
      ->dispatch(SmartIpEvents::QUERY_IP, $event);
    $result = $location
      ->getData(FALSE);
    self::updateFields($result);
    $location
      ->setData($result);

    // Allow other modules to modify the result via Symfony Event Dispatcher.
    \Drupal::service('event_dispatcher')
      ->dispatch(SmartIpEvents::DATA_ACQUIRED, $event);
    $result = $location
      ->getData(FALSE);
    $results[$ip] = $result;
    return $result;
  }

  /**
   * Update the values of location fields.
   *
   * @param array $fields
   *   Location fields.
   */
  public static function updateFields(&$fields) {
    $coordinatesExist = isset($fields['latitude']) && isset($fields['longitude']);
    if ($coordinatesExist) {

      // If coordinates are (0, 0) there was no match.
      if ($fields['latitude'] === 0 && $fields['longitude'] === 0) {
        $fields['latitude'] = NULL;
        $fields['longitude'] = NULL;
      }
    }

    // Determine if EU member country.
    if (isset($fields['countryCode']) && (!isset($fields['isEuCountry']) || isset($fields['isEuCountry']) && empty($fields['isEuCountry']))) {
      $euCountry = smart_ip_is_eu_gdpr_country($fields['countryCode']);
      $fields['isEuCountry'] = !empty($euCountry);
      if ($fields['isEuCountry']) {
        $fields['isGdprCountry'] = TRUE;
      }
    }

    // Determine if GDPR country.
    if (isset($fields['countryCode']) && (!isset($fields['isGdprCountry']) || isset($fields['isGdprCountry']) && empty($fields['isGdprCountry']))) {
      $gdprCountry = smart_ip_is_eu_gdpr_country($fields['countryCode'], FALSE);
      $fields['isGdprCountry'] = !empty($gdprCountry);
    }

    // Update the format of time zone field according to user's preference.
    $config = \Drupal::config('smart_ip.settings');
    $tzFormat = $config
      ->get('timezone_format');
    $geotimezoneExists = \Drupal::moduleHandler()
      ->moduleExists('geotimezone');
    if ($geotimezoneExists && empty($fields['timeZone']) && ($coordinatesExist || isset($fields['countryCode']))) {

      // Time zone is empty, get value from Geo Time Zone.
      $timeZone = geotimezone_query($fields, $tzFormat);
      if (is_array($timeZone)) {
        $fields['timeZone'] = !empty($timeZone) ? Tags::implode($timeZone) : '';
      }
      else {
        $fields['timeZone'] = $timeZone;
      }
    }
    elseif (!empty($fields['timeZone'])) {
      $a = strpos($fields['timeZone'], '+') === 0;
      $b = strpos($fields['timeZone'], '-') === 0;
      if ($geotimezoneExists && $tzFormat == 'identifier' && (strpos($fields['timeZone'], '+') === 0 || strpos($fields['timeZone'], '-') === 0) && ($coordinatesExist || isset($fields['countryCode']))) {

        // Convert to time zone identifier.
        $timeZone = geotimezone_query($fields, $tzFormat);
        if (is_array($timeZone)) {
          $fields['timeZone'] = !empty($timeZone) ? Tags::implode($timeZone) : '';
        }
        else {
          $fields['timeZone'] = $timeZone;
        }
      }
      elseif ($tzFormat == 'offset' && strpos($fields['timeZone'], '+') === FALSE) {

        // Convert to time zone offset.
        $time = new \DateTime('now', new \DateTimeZone($fields['timeZone']));
        $fields['timeZone'] = $time
          ->format('P');
      }
    }

    // Make sure external data in UTF-8.
    if (!empty($fields)) {
      foreach ($fields as &$item) {
        if (is_string($item) && !mb_detect_encoding($item, 'UTF-8', TRUE)) {
          $item = mb_convert_encoding($item, 'UTF-8', 'ISO-8859-1');
        }
      }
    }
  }

  /**
   * Write session variable.
   *
   * @param string $key
   *   The session variable to write. Pass 'smart_ip' to write the smart_ip data.
   * @param mixed $value
   *   The value of session variable.
   */
  public static function setSession($key, $value) {
    if (\Drupal::moduleHandler()
      ->moduleExists('session_cache')) {
      session_cache_set($key, $value);
    }
    else {

      /** @var \Symfony\Component\HttpFoundation\Session\Session $session */
      $session = \Drupal::service('session');
      $session
        ->set($key, $value);
    }
  }

  /**
   * Read session variable.
   *
   * @param string $key
   *   The session variable to read. Pass 'smart_ip' to read the smart_ip data.
   * @param mixed $default
   *   Default value if the session variable is not set.
   * @return array
   *   Session value.
   */
  public static function getSession($key, $default = NULL) {
    if (\Drupal::moduleHandler()
      ->moduleExists('session_cache')) {
      $smartIpSession = session_cache_get($key);
    }
    else {

      /** @var \Symfony\Component\HttpFoundation\Session\Session $session */
      $session = \Drupal::service('session');

      // Make sure session is started before calling any functions.
      if ($session
        ->isStarted()) {
        $smartIpSession = $session
          ->get($key);
      }
      else {
        $smartIpSession = $default;
      }
    }
    if ($smartIpSession === NULL) {
      $smartIpSession = $default;
    }
    return $smartIpSession;
  }

  /**
   * Check if the current user is in debug mode.
   *
   * @param int $uid
   *   User ID.
   * @return bool
   *   TRUE if the current user is in debug mode and FALSE if not.
   */
  public static function isUserDebugMode($uid = NULL) {
    $inDebugMode = FALSE;
    $config = \Drupal::config('smart_ip.settings');
    $user = $uid ? User::load($uid) : \Drupal::currentUser();
    $userRoles = $user
      ->getRoles();
    $rolesDebug = $config
      ->get('roles_in_debug_mode');
    foreach ($userRoles as $userRole) {
      if (isset($rolesDebug[$userRole]) && $rolesDebug[$userRole]) {
        $inDebugMode = TRUE;
        break;
      }
    }
    return $inDebugMode;
  }

  /**
   * Update user's location only if the IP address stored in session is not the
   * same as the IP address detected by the server or debug mode IP.
   */
  public static function updateUserLocation() {

    // Check if the user permitted to share location.
    $shareLocation = self::getSession('smart_ip_user_share_location_permitted', TRUE);
    if ($shareLocation) {
      $config = \Drupal::config('smart_ip.settings');
      $inDebugMode = FALSE;
      $dontGeolocate = TRUE;
      $debugModeIp = '';
      $user = \Drupal::currentUser();
      $userRoles = $user
        ->getRoles();
      $rolesDebug = $config
        ->get('roles_in_debug_mode');
      $rolesDebugIp = $config
        ->get('roles_in_debug_mode_ip');
      $rolesGeolocate = $config
        ->get('roles_to_geolocate');
      foreach ($userRoles as $userRole) {
        if (isset($rolesGeolocate[$userRole]) && $rolesGeolocate[$userRole]) {

          // This user role is in the list of "Roles to Geolocate".
          $dontGeolocate = FALSE;
        }
        if ($userRole != 'authenticated' && isset($rolesDebug[$userRole]) && $rolesDebug[$userRole]) {

          // Prioritize other roles than 'authenticated'.
          $inDebugMode = TRUE;
          $debugModeIp = $rolesDebugIp[$userRole];
          break;
        }
      }
      if (!$dontGeolocate) {
        if ($inDebugMode) {

          // Use debug information instead of real information.
          $ip = $debugModeIp;
        }
        elseif ($user
          ->isAuthenticated() && isset($rolesDebug['authenticated']) && $rolesDebug['authenticated']) {

          // The 'authenticated' role should be the last to check if it is in
          // debug mode then use debug information instead of real information.
          $ip = $rolesDebugIp['authenticated'];
        }
        else {
          $ip = \Drupal::request()
            ->getClientIp();
        }
        $smartIpSession = self::getSession('smart_ip');
        if (!isset($smartIpSession['location']['ipAddress']) || $smartIpSession['location']['ipAddress'] != $ip) {

          /** @var \Drupal\smart_ip\SmartIpLocation $location */
          $location = \Drupal::service('smart_ip.smart_ip_location');
          $location
            ->delete();
          $result = self::query($ip);
          self::userLocationFallback($result);
          $location
            ->setData($result);
          $location
            ->save();
        }
      }
    }
  }

  /**
   * Use server's mod_geoip, X-GeoIP and Cloudflare IP Geolocation as fallback
   * if the user's geolocation is empty.
   *
   * @param array $location
   *   Location details.
   */
  private static function userLocationFallback(array &$location) {
    if (!isset($location['country']) && !isset($location['countryCode']) && !isset($location['region']) && !isset($location['regionCode']) && !isset($location['city']) && !isset($location['zip']) && !isset($location['latitude']) && !isset($location['longitude'])) {
      if (function_exists('apache_note')) {
        if ($country = apache_note('GEOIP_COUNTRY_NAME')) {
          $location['country'] = $country;
        }
        if ($country_code = apache_note('GEOIP_COUNTRY_CODE')) {
          $location['countryCode'] = $country_code;
        }
        if ($region = apache_note('GEOIP_REGION_NAME')) {
          $location['region'] = $region;
        }
        if ($region_code = apache_note('GEOIP_REGION')) {
          $location['regionCode'] = $region_code;
        }
        if ($city = apache_note('GEOIP_CITY')) {
          $location['city'] = $city;
        }
        if ($zip = apache_note('GEOIP_POSTAL_CODE')) {
          $location['zip'] = $zip;
        }
        if ($latitude = apache_note('GEOIP_LATITUDE')) {
          $location['latitude'] = $latitude;
        }
        if ($longitude = apache_note('GEOIP_LONGITUDE')) {
          $location['longitude'] = $longitude;
        }
      }
      elseif (isset($_SERVER['HTTP_X_AKAMAI_EDGESCAPE'])) {
        parse_str(str_replace(',', '&', $_SERVER['HTTP_X_AKAMAI_EDGESCAPE']), $esHeader);
        if (isset($esHeader['country_code'])) {
          module_load_include('inc', 'smart_ip', 'includes/smart_ip.country_list');
          $countries = country_get_predefined_list();
          $location['countryCode'] = $esHeader['country_code'];
          $location['country'] = $countries[$esHeader['country_code']];
        }
        if (isset($esHeader['region'])) {
          $location['region'] = $esHeader['region'];
        }
        if (isset($esHeader['region_code'])) {
          $location['regionCode'] = $esHeader['region_code'];
          $region = '';
          if (isset($esHeader['region_code']) && isset($esHeader['country_code'])) {
            $regionResult = smart_ip_get_region_static($esHeader['country_code'], $esHeader['region_code']);
            $region = $regionResult[$esHeader['country_code']][$esHeader['region_code']];
          }
          $result['region'] = $region;
        }
        if (isset($esHeader['city'])) {
          $location['city'] = $esHeader['city'];
        }
        if (isset($esHeader['zip'])) {
          $location['zip'] = $esHeader['zip'];
        }
        if (isset($esHeader['lat'])) {
          $location['latitude'] = $esHeader['lat'];
        }
        if (isset($esHeader['long'])) {
          $location['longitude'] = $esHeader['long'];
        }
      }
      else {
        if (isset($_SERVER['GEOIP_COUNTRY_NAME'])) {
          $location['country'] = $_SERVER['GEOIP_COUNTRY_NAME'];
        }
        elseif (isset($_SERVER['HTTP_X_GEOIP_COUNTRY'])) {
          module_load_include('inc', 'smart_ip', 'includes/smart_ip.country_list');
          $countries = country_get_predefined_list();
          $location['country'] = $countries[$_SERVER['HTTP_X_GEOIP_COUNTRY']];
        }
        elseif (isset($_SERVER['HTTP_CF_IPCOUNTRY'])) {
          module_load_include('inc', 'smart_ip', 'includes/smart_ip.country_list');
          $countries = country_get_predefined_list();
          $location['country'] = $countries[$_SERVER['HTTP_CF_IPCOUNTRY']];
        }
        if (isset($_SERVER['GEOIP_COUNTRY_CODE'])) {
          $location['country'] = $_SERVER['GEOIP_COUNTRY_CODE'];
        }
        elseif (isset($_SERVER['HTTP_X_GEOIP_COUNTRY'])) {
          $location['countryCode'] = $_SERVER['HTTP_X_GEOIP_COUNTRY'];
        }
        elseif (isset($_SERVER['HTTP_CF_IPCOUNTRY'])) {
          $location['countryCode'] = $_SERVER['HTTP_CF_IPCOUNTRY'];
        }
        if (isset($_SERVER['GEOIP_REGION_NAME'])) {
          $location['region'] = $_SERVER['GEOIP_REGION_NAME'];
        }
        if (isset($_SERVER['GEOIP_REGION'])) {
          $location['regionCode'] = $_SERVER['GEOIP_REGION'];
        }
        if (isset($_SERVER['GEOIP_CITY'])) {
          $location['city'] = $_SERVER['GEOIP_CITY'];
        }
        if (isset($_SERVER['GEOIP_POSTAL_CODE'])) {
          $location['zip'] = $_SERVER['GEOIP_POSTAL_CODE'];
        }
        if (isset($_SERVER['GEOIP_LATITUDE'])) {
          $location['latitude'] = $_SERVER['GEOIP_LATITUDE'];
        }
        if (isset($_SERVER['GEOIP_LONGITUDE'])) {
          $location['longitude'] = $_SERVER['GEOIP_LONGITUDE'];
        }
      }
    }
  }

  /**
   * Check the page URL in allowed geolocate list.
   *
   * @return bool
   */
  public static function checkAllowedPage() {
    $pages = \Drupal::config('smart_ip.settings')
      ->get('allowed_pages');
    if (empty($pages)) {

      // No pages specified then all pages are allowed.
      return TRUE;
    }
    else {
      if (isset($_GET['geolocate_uri'])) {

        // Handle "geolocate_uri" from ajax request.
        $url = $_GET['geolocate_uri'];
      }
      else {
        $url = \Drupal::service('path.current')
          ->getPath();
      }

      /** @var \Drupal\Core\Path\AliasManagerInterface $aliasManager */
      $aliasManager = \Drupal::service('path.alias_manager');
      $pathAlias = $aliasManager
        ->getAliasByPath($url);

      // Convert the Drupal path to lowercase.
      $path = Unicode::strtolower($pathAlias);

      /** @var \Drupal\Core\Path\PathMatcherInterface $pathMatcher */
      $pathMatcher = \Drupal::service('path.matcher');

      // Compare the lowercase internal and lowercase path alias (if any).
      $pageMatch = $pathMatcher
        ->matchPath($path, $pages);
      if ($path != $url) {
        $pageMatch = $pageMatch || $pathMatcher
          ->matchPath($url, $pages);
      }
      elseif ($path == $url) {
        $url = $aliasManager
          ->getPathByAlias($url);
        $pageMatch = $pageMatch || $pathMatcher
          ->matchPath($url, $pages);
      }
      return $pageMatch;
    }
  }

  /**
   * Determine IP address version.
   *
   * @param string $ipAddress
   *   IP address to check for version.
   * @return int
   */
  public static function ipAddressVersion($ipAddress = NULL) {
    if (filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
      return 4;
    }
    elseif (filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
      return 6;
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
SmartIp::checkAllowedPage public static function Check the page URL in allowed geolocate list.
SmartIp::getSession public static function Read session variable.
SmartIp::ipAddressVersion public static function Determine IP address version.
SmartIp::isUserDebugMode public static function Check if the current user is in debug mode.
SmartIp::query public static function Get the geolocation from the IP address.
SmartIp::setSession public static function Write session variable.
SmartIp::updateFields public static function Update the values of location fields.
SmartIp::updateUserLocation public static function Update user's location only if the IP address stored in session is not the same as the IP address detected by the server or debug mode IP.
SmartIp::userLocationFallback private static function Use server's mod_geoip, X-GeoIP and Cloudflare IP Geolocation as fallback if the user's geolocation is empty.