You are here

ip2country.module in IP-based Determination of a Visitor's Country 8

Same filename and directory in other branches
  1. 6 ip2country.module
  2. 7 ip2country.module

Determination of user's Country based on IP.

This module uses the IP Address that a user is connected from to deduce the Country where the user is located. This method is not foolproof, because a user may connect through an anonymizing proxy, or may be in an unusual case, such as getting service from a neighboring country, or using an IP block leased from a company in another country. Additionally, users accessing a server on a local network may be using an IP that is not assigned to any country (e.g. 192.168.x.x).

Country determination occurs upon user login. If a country can be determined from the IP address, the ISO 3166 2-character country code is stored in the Drupal user.data service object with the key 'country_iso_code_2'. If no country can be determined, this member is left unset. More details about how to use this module can be found in the README.txt.

The database used is maintained by ARIN, the American Registry for Internet Numbers (http://www.arin.net/about_us/index.html), which is one of the 5 official Regional Internet Registries (RIR) responsible for assigning IP addresses. The claim is the database is 98% accurate, with most of the problems coming from users in less-developed countries. Regardless, there's no more-authoritative source of this information.

@author Tim Rohaly. <http://drupal.org/user/202830>

File

ip2country.module
View source
<?php

/**
 * @file
 * Determination of user's Country based on IP.
 *
 * This module uses the IP Address that a user is connected from to deduce
 * the Country where the user is located. This method is not foolproof,
 * because a user may connect through an anonymizing proxy, or may be in
 * an unusual case, such as getting service from a neighboring country,
 * or using an IP block leased from a company in another country.
 * Additionally, users accessing a server on a local network may be using
 * an IP that is not assigned to any country (e.g. 192.168.x.x).
 *
 * Country determination occurs upon user login. If a country can be
 * determined from the IP address, the ISO 3166 2-character country code
 * is stored in the Drupal user.data service object with the key
 * 'country_iso_code_2'. If no country can be determined, this member is
 * left unset. More details about how to use this module can be found in
 * the README.txt.
 *
 * The database used is maintained by ARIN, the American Registry for
 * Internet Numbers (http://www.arin.net/about_us/index.html), which is
 * one of the 5 official Regional Internet Registries (RIR) responsible
 * for assigning IP addresses. The claim is the database is 98% accurate,
 * with most of the problems coming from users in less-developed countries.
 * Regardless, there's no more-authoritative source of this information.
 *
 * @author Tim Rohaly.    <http://drupal.org/user/202830>
 */
use Drupal\Core\Routing\RouteMatchInterface;

/**
 * Implements hook_help().
 */
function ip2country_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    case 'help.page.ip2country':

      // @todo Replace this with a controller method.
      // @see https://www.drupal.org/node/2669988
      return t('Determines the Country where the user is located based on the IP address used.');
    case 'ip2country.settings':
      return t('Configuration settings for the ip2country module.');
  }
}

/**
 * Implements hook_cron().
 *
 * Updates the IP to Country database automatically on a periodic
 * basis. Default period is 1 week.
 */
function ip2country_cron() {
  $ip2country_config = \Drupal::config('ip2country.settings');
  $rir = $ip2country_config
    ->get('rir');
  $md5_checksum = $ip2country_config
    ->get('md5_checksum');
  $batch_size = $ip2country_config
    ->get('batch_size');

  // Automatic database updates are disabled when $update_interval == 0.
  $update_interval = $ip2country_config
    ->get('update_interval');
  if ($update_interval && \Drupal::state()
    ->get('ip2country_last_update') <= \Drupal::time()
    ->getRequestTime() - $update_interval) {
    $status = \Drupal::service('ip2country.manager')
      ->updateDatabase($rir, $md5_checksum, $batch_size);

    // Log to watchdog if requested.
    if ($ip2country_config
      ->get('watchdog')) {
      if ($status != FALSE) {

        // Success.
        \Drupal::logger('ip2country')
          ->notice('Database updated from @registry server. Table contains @rows rows.', [
          '@registry' => mb_strtoupper($ip2country_config
            ->get('rir')),
          '@rows' => $status,
        ]);
      }
      else {

        // Failure.
        \Drupal::logger('ip2country')
          ->warning('Database update from @registry server FAILED.', [
          '@registry' => mb_strtoupper($ip2country_config
            ->get('rir')),
        ]);
      }
    }
  }
}

/**
 * Implements hook_user_login().
 *
 * Detects IP and determines country upon user login.
 */
function ip2country_user_login($account) {

  // Successful login. First determine user's country based on IP.
  $ip = \Drupal::request()
    ->getClientIp();
  $country_lookup = \Drupal::service('ip2country.lookup');
  $country_code = $country_lookup
    ->getCountry($ip);
  $ip2country_config = \Drupal::config('ip2country.settings');

  // Now check to see if this user has "administer ip2country" permission
  // and if debug mode set. If both are TRUE, use debug information
  // instead of real information.
  if (\Drupal::currentUser()
    ->hasPermission('administer ip2country') && $ip2country_config
    ->get('debug')) {
    $type = $ip2country_config
      ->get('test_type');
    if ($type == 0) {

      // Debug Country entered.
      $country_code = $ip2country_config
        ->get('test_country');
    }
    else {

      // Debug IP entered.
      $ip = $ip2country_config
        ->get('test_ip_address');
      $country_code = $country_lookup
        ->getCountry($ip);
    }
    \Drupal::messenger()
      ->addMessage(t('Using DEBUG value for Country - @country', [
      '@country' => $country_code,
    ]));
  }

  // Finally, save country, if it has been determined.
  if ($country_code) {

    // Store the ISO country code in the user.data service object.
    \Drupal::service('user.data')
      ->set('ip2country', $account
      ->id(), 'country_iso_code_2', $country_code);
  }
}

/**
 * Implements hook_user_load().
 *
 * Takes care of restoring country data from {users_data}.
 */
function ip2country_user_load($accounts) {
  foreach ($accounts as $account) {
    $userdata = \Drupal::service('user.data')
      ->get('ip2country', $account
      ->id(), 'country_iso_code_2');
    if (isset($userdata)) {
      $accounts[$account
        ->id()]->country_iso_code_2 = $userdata;
    }
  }
}

/**
 * Gets the ISO 3166 2-character country code from the IP address.
 *
 * @param string|int $ip_address
 *   IP address either as a dotted quad string (e.g. "127.0.0.1") or
 *   as a 32-bit unsigned long integer.
 *
 * @return string|false
 *   ISO 3166-1 2-character country code for this IP address, or
 *   FALSE if the lookup failed to find a country.
 *
 * @deprecated in ip2country:8.x-1.9 and is removed from ip2country:8.x-2.0.
 *   Use the ip2country.lookup service instead. For example,
 *   \Drupal::service('ip2country.lookup')->getCountry($ip_address).
 *
 * @see https://www.drupal.org/node/3076484
 */
function ip2country_get_country($ip_address) {
  @trigger_error("ip2country_get_country() is deprecated in ip2country:8.x-1.9 and is removed from ip2country:8.x-2.0. Use \\Drupal::service('ip2country.lookup')->getCountry() instead. See https://www.drupal.org/node/3076484", E_USER_DEPRECATED);
  return \Drupal::service('ip2country.lookup')
    ->getCountry($ip_address);
}

/**
 * Gets the total count of IP ranges in database.
 *
 * @return int
 *   Integer count of the number of rows in the ip2country table.
 *
 * @deprecated in ip2country:8.x-1.9 and is removed from ip2country:8.x-2.0.
 *   Use the ip2country.manager service instead. For example,
 *   \Drupal::service('ip2country.manager')->getRowCount().
 *
 * @see https://www.drupal.org/node/3076484
 */
function ip2country_get_count() {
  @trigger_error("ip2country_get_count() is deprecated in ip2country:8.x-1.9 and is removed from ip2country:8.x-2.0. Use \\Drupal::service('ip2country.manager')->getRowCount() instead. See https://www.drupal.org/node/3076484", E_USER_DEPRECATED);
  return \Drupal::service('ip2country.manager')
    ->getRowCount();
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Alters Ubercart's uc_cart_checkout_form() to use ip2country's country
 * determination as the default billing and delivery country. If the user's
 * country hasn't been determined, the store country will be used instead.
 */
function ip2country_form_uc_cart_checkout_form_alter(&$form, &$form_state) {
  global $user;
  if (isset($user->data['country_iso_code_2'])) {
    if (\Drupal::moduleHandler()
      ->moduleExists('uc_store')) {
      $connection = \Drupal::database();
      $country_id = $connection
        ->query('SELECT country_id from {uc_countries} WHERE country_iso_code_2 = :iso2', [
        ':iso2' => $user->data['country_iso_code_2'],
      ])
        ->fetchField();
      if (!$country_id) {
        $country_id = uc_store_default_country();
      }
      $form['panes']['billing']['billing_country']['#default_value'] = $form['panes']['delivery']['delivery_country']['#default_value'] = $country_id;
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Alters Ubercart's uc_cart_pane_quotes() form to use ip2country's country
 * determination as the default billing and delivery country. If the user's
 * country hasn't been determined, the store country will be used instead.
 */
function ip2country_form_uc_cart_pane_quotes_alter(&$form, &$form_state) {
  global $user;
  if (isset($user->data['country_iso_code_2'])) {
    if (\Drupal::moduleHandler()
      ->moduleExists('uc_store')) {
      $connection = \Drupal::database();
      $country_id = $connection
        ->query('SELECT country_id from {uc_countries} WHERE country_iso_code_2 = :iso2', [
        ':iso2' => $user->data['country_iso_code_2'],
      ])
        ->fetchField();
      if (!$country_id) {
        $country_id = uc_store_default_country();
      }
      $form['delivery_country']['#default_value'] = $country_id;
    }
  }
}

Functions

Namesort descending Description
ip2country_cron Implements hook_cron().
ip2country_form_uc_cart_checkout_form_alter Implements hook_form_FORM_ID_alter().
ip2country_form_uc_cart_pane_quotes_alter Implements hook_form_FORM_ID_alter().
ip2country_get_count Deprecated Gets the total count of IP ranges in database.
ip2country_get_country Deprecated Gets the ISO 3166 2-character country code from the IP address.
ip2country_help Implements hook_help().
ip2country_user_load Implements hook_user_load().
ip2country_user_login Implements hook_user_login().