You are here

class MobileNumberUtil in Mobile Number 8

Same name and namespace in other branches
  1. 2.0.x src/MobileNumberUtil.php \Drupal\mobile_number\MobileNumberUtil

Turns a render array into a HTML string.

Hierarchy

Expanded class hierarchy of MobileNumberUtil

1 string reference to 'MobileNumberUtil'
mobile_number.services.yml in ./mobile_number.services.yml
mobile_number.services.yml
1 service uses MobileNumberUtil
mobile_number.util in ./mobile_number.services.yml
Drupal\mobile_number\MobileNumberUtil

File

src/MobileNumberUtil.php, line 21

Namespace

Drupal\mobile_number
View source
class MobileNumberUtil implements MobileNumberUtilInterface {

  /**
   * The PhoneNumberUtil object.
   *
   * @var \libphonenumber\PhoneNumberUtil
   */
  public $libUtil;

  /**
   * The module handler service.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  public $moduleHandler;

  /**
   * The config factory service.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  public $configFactory;

  /**
   * The flood service.
   *
   * @var \Drupal\Core\Flood\FloodInterface
   */
  public $flood;

  /**
   * The field manager service.
   *
   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
   */
  public $fieldMananger;

  /**
   * The country manager service.
   *
   * @var \Drupal\Core\Locale\CountryManagerInterface
   */
  public $countryManager;

  /**
   * The token service.
   *
   * @var \Drupal\Core\Utility\Token
   */
  public $token;

  /**
   * MobileNumberUtil constructor.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   Config factory.
   * @param \Drupal\Core\Flood\FloodInterface $flood
   *   Flood manager.
   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $field_manager
   *   Field manager.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   Module handler.
   * @param \Drupal\Core\Locale\CountryManagerInterface $country_manager
   *   Country manager.
   * @param \Drupal\Core\Utility\Token $token
   *   Token service.
   */
  public function __construct(ConfigFactoryInterface $config_factory, FloodInterface $flood, EntityFieldManagerInterface $field_manager, ModuleHandlerInterface $module_handler, CountryManagerInterface $country_manager, Token $token) {
    $this->libUtil = PhoneNumberUtil::getInstance();
    $this->moduleHandler = $module_handler;
    $this->flood = $flood;
    $this->configFactory = $config_factory;
    $this->countryManager = $country_manager;
    $this->fieldMananger = $field_manager;
    $this->token = $token;
  }

  /**
   * {@inheritdoc}
   */
  public function libUtil() {
    return $this->libUtil;
  }

  /**
   * {@inheritdoc}
   */
  public function getMobileNumber($number, $country = NULL, $types = [
    1 => 1,
    2 => 2,
  ]) {
    try {
      return $this
        ->testMobileNumber($number, $country, $types);
    } catch (MobileNumberException $e) {
      return NULL;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function testMobileNumber($number, $country = NULL, $types = [
    1 => 1,
    2 => 2,
  ]) {
    if (!$number) {
      throw new MobileNumberException('Empty number', MobileNumberException::ERROR_NO_NUMBER);
    }
    try {

      /** @var \libphonenumber\PhoneNumber $phone_number */
      $phone_number = $this->libUtil
        ->parse($number, $country);
    } catch (NumberParseException $e) {
      throw new MobileNumberException('Invalid number or unknown country', MobileNumberException::ERROR_INVALID_NUMBER);
    }
    if ($types) {
      if (!in_array($this->libUtil
        ->getNumberType($phone_number), $types)) {
        throw new MobileNumberException('Not a mobile number', MobileNumberException::ERROR_WRONG_TYPE);
      }
    }
    $mcountry = $this->libUtil
      ->getRegionCodeForNumber($phone_number);
    if ($country && $mcountry != $country) {
      throw new MobileNumberException('Mismatch country with the number\'s prefix', MobileNumberException::ERROR_WRONG_COUNTRY);
    }
    return $phone_number;
  }

  /**
   * {@inheritdoc}
   */
  public function getCallableNumber(PhoneNumber $mobile_number) {
    return $mobile_number ? $this->libUtil
      ->format($mobile_number, PhoneNumberFormat::E164) : NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function getLocalNumber(PhoneNumber $mobile_number) {
    if (!$mobile_number) {
      return NULL;
    }
    $region_code = $this->libUtil
      ->getRegionCodeForNumber($mobile_number);
    $prefix = $this->libUtil
      ->getNddPrefixForRegion($region_code, TRUE);
    $national_number = $mobile_number
      ->getNationalNumber();
    return $prefix . $national_number;
  }

  /**
   * {@inheritdoc}
   */
  public function getCountry(PhoneNumber $mobile_number) {
    return $mobile_number ? $this
      ->libUtil()
      ->getRegionCodeForNumber($mobile_number) : NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function getCountryCode($country) {
    return $this->libUtil
      ->getCountryCodeForRegion($country);
  }

  /**
   * {@inheritdoc}
   */
  public function getCountryOptions($filter = [], $show_country_names = FALSE) {
    $libUtil = $this->libUtil;
    $regions = $libUtil
      ->getSupportedRegions();
    $countries = [];
    foreach ($regions as $region => $country) {
      $code = $libUtil
        ->getCountryCodeForRegion($country);
      if (!$filter || !empty($filter[$country])) {
        $name = $this
          ->getCountryName($country);
        $countries[$country] = $show_country_names && $name ? "{$name} (+{$code})" : "{$country} (+{$code})";
      }
    }
    asort($countries);
    return $countries;
  }

  /**
   * {@inheritdoc}
   */
  public function getCountryName($country) {
    $drupal_countries = $this->countryManager
      ->getList();
    return !empty($drupal_countries[$country]) ? $drupal_countries[$country] : $country;
  }

  /**
   * {@inheritdoc}
   */
  public function generateVerificationCode($length = 4) {
    return str_pad((string) rand(0, pow(10, $length)), $length, '0', STR_PAD_LEFT);
  }

  /**
   * {@inheritdoc}
   */
  public function checkFlood(PhoneNumber $mobile_number, $type = 'verification') {
    switch ($type) {
      case 'verification':
        return $this->flood
          ->isAllowed('mobile_number_verification', $this::VERIFY_ATTEMPTS_COUNT, $this::VERIFY_ATTEMPTS_INTERVAL, $this
          ->getCallableNumber($mobile_number));
      case 'sms':
        return $this->flood
          ->isAllowed('mobile_number_sms', $this::SMS_ATTEMPTS_COUNT, $this::SMS_ATTEMPTS_INTERVAL, $this
          ->getCallableNumber($mobile_number)) && $this->flood
          ->isAllowed('mobile_number_sms_ip', $this::SMS_ATTEMPTS_COUNT * 5, $this::SMS_ATTEMPTS_INTERVAL * 5);
      default:
        return TRUE;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getToken(PhoneNumber $mobile_number) {
    if (!empty($_SESSION['mobile_number_verification'][$this
      ->getCallableNumber($mobile_number)]['token'])) {
      return $_SESSION['mobile_number_verification'][$this
        ->getCallableNumber($mobile_number)]['token'];
    }
    return NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function sendVerification(PhoneNumber $mobile_number, $message, $code, $token_data = []) {
    $message = t($message);
    $message = str_replace('!code', $code, $message);
    $message = str_replace('!site_name', $this->configFactory
      ->get('system.site')
      ->get('name'), $message);
    $message = $this->token
      ->replace($message, $token_data);
    $this->flood
      ->register('mobile_number_sms', $this::SMS_ATTEMPTS_INTERVAL, $this
      ->getCallableNumber($mobile_number));
    $this->flood
      ->register('mobile_number_sms_ip', $this::SMS_ATTEMPTS_INTERVAL * 5);
    if ($this
      ->sendSms($this
      ->getCallableNumber($mobile_number), $message)) {
      $token = $this
        ->registerVerificationCode($mobile_number, $code);
      $_SESSION['mobile_number_verification'][$this
        ->getCallableNumber($mobile_number)] = [
        'token' => $token,
        'verified' => FALSE,
      ];
      return $token;
    }
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function registerVerificationCode(PhoneNumber $mobile_number, $code) {
    $time = time();
    $token = \Drupal::csrfToken()
      ->get(rand(0, 999999999) . $time . 'mobile verification token' . $this
      ->getCallableNumber($mobile_number));
    $hash = $this
      ->codeHash($mobile_number, $token, $code);
    \Drupal::database()
      ->insert('mobile_number_verification')
      ->fields([
      'token' => $token,
      'timestamp' => $time,
      'verification_code' => $hash,
    ])
      ->execute();
    return $token;
  }

  /**
   * {@inheritdoc}
   */
  public function verifyCode(PhoneNumber $mobile_number, $code, $token = NULL) {
    $token = $token ? $token : $this
      ->getToken($mobile_number);
    if ($code && $token) {
      $hash = $this
        ->codeHash($mobile_number, $token, $code);
      $query = \Drupal::database()
        ->select('mobile_number_verification', 'm');
      $query
        ->fields('m', [
        'token',
      ])
        ->condition('token', $token)
        ->condition('timestamp', time() - 60 * 60 * 24, '>')
        ->condition('verification_code', $hash);
      $result = $query
        ->execute()
        ->fetchAssoc();
      if ($result) {
        $_SESSION['mobile_number_verification'][$this
          ->getCallableNumber($mobile_number)]['verified'] = TRUE;
        return TRUE;
      }
      $this->flood
        ->register('mobile_number_verification', $this::VERIFY_ATTEMPTS_INTERVAL, $this
        ->getCallableNumber($mobile_number));
      return FALSE;
    }
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function isVerified(PhoneNumber $mobile_number) {
    return !empty($_SESSION['mobile_number_verification'][$this
      ->getCallableNumber($mobile_number)]['verified']);
  }

  /**
   * {@inheritdoc}
   */
  public function codeHash(PhoneNumber $mobile_number, $token, $code) {
    $number = $this
      ->getCallableNumber($mobile_number);
    $secret = $this->configFactory
      ->getEditable('mobile_number.settings')
      ->get('verification_secret');
    return sha1("{$number}{$secret}{$token}{$code}");
  }

  /**
   * {@inheritdoc}
   */
  public function smsCallback() {
    $module_handler = $this->moduleHandler;
    $callback = [];
    if ($module_handler
      ->moduleExists('sms')) {
      $callback = 'mobile_number_send_sms';
    }
    $module_handler
      ->alter('mobile_number_send_sms_callback', $callback);
    return is_callable($callback) ? $callback : FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function sendSms($number, $message) {
    $callback = $this
      ->smsCallback();
    if (!$callback) {
      return FALSE;
    }
    return call_user_func($callback, $number, $message);
  }

  /**
   * {@inheritdoc}
   */
  public function isSmsEnabled() {
    return $this
      ->smsCallback() ? TRUE : FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function tfaAccountNumber($uid) {
    $user = User::load($uid);
    $field_name = $this
      ->getTfaField();
    if ($this
      ->isTfaEnabled() && $field_name && !empty($user
      ->get($field_name)
      ->getValue()[0]['value']) && !empty($user
      ->get($field_name)
      ->getValue()[0]['tfa'])) {
      return $user
        ->get($field_name)
        ->getValue()[0]['value'];
    }
    return '';
  }

  /**
   * {@inheritdoc}
   */
  public function getTfaField() {
    $tfa_field = $this->configFactory
      ->get('mobile_number.settings')
      ->get('tfa_field');
    $user_fields = $this->fieldMananger
      ->getFieldDefinitions('user', 'user');
    return $this
      ->isTfaEnabled() && !empty($user_fields[$tfa_field]) ? $tfa_field : '';
  }

  /**
   * {@inheritdoc}
   */
  public function setTfaField($field_name) {
    $this->configFactory
      ->getEditable('mobile_number.settings')
      ->set('tfa_field', $field_name)
      ->save(TRUE);
  }

  /**
   * {@inheritdoc}
   */
  public function isTfaEnabled() {
    return $this->configFactory
      ->get('tfa.settings')
      ->get('enabled') && $this
      ->isSmsEnabled();
  }

}

Members

Namesort descending Modifiers Type Description Overrides
MobileNumberUtil::$configFactory public property The config factory service.
MobileNumberUtil::$countryManager public property The country manager service.
MobileNumberUtil::$fieldMananger public property The field manager service.
MobileNumberUtil::$flood public property The flood service.
MobileNumberUtil::$libUtil public property The PhoneNumberUtil object.
MobileNumberUtil::$moduleHandler public property The module handler service.
MobileNumberUtil::$token public property The token service.
MobileNumberUtil::checkFlood public function Checks whether there were too many verifications attempted with the current number. Overrides MobileNumberUtilInterface::checkFlood
MobileNumberUtil::codeHash public function Generate hash given token and code. Overrides MobileNumberUtilInterface::codeHash
MobileNumberUtil::generateVerificationCode public function Generates a random numeric string. Overrides MobileNumberUtilInterface::generateVerificationCode
MobileNumberUtil::getCallableNumber public function Get international number. Overrides MobileNumberUtilInterface::getCallableNumber
MobileNumberUtil::getCountry public function Get country code. Overrides MobileNumberUtilInterface::getCountry
MobileNumberUtil::getCountryCode public function Gets the country phone number prefix given a country code. Overrides MobileNumberUtilInterface::getCountryCode
MobileNumberUtil::getCountryName public function Get country display name given country code. Overrides MobileNumberUtilInterface::getCountryName
MobileNumberUtil::getCountryOptions public function Get all supported countries. Overrides MobileNumberUtilInterface::getCountryOptions
MobileNumberUtil::getLocalNumber public function Get national number. Overrides MobileNumberUtilInterface::getLocalNumber
MobileNumberUtil::getMobileNumber public function Get mobile number object. Overrides MobileNumberUtilInterface::getMobileNumber
MobileNumberUtil::getTfaField public function Gets the tfa field configuration. Overrides MobileNumberUtilInterface::getTfaField
MobileNumberUtil::getToken public function Gets token generated if verification code was sent. Overrides MobileNumberUtilInterface::getToken
MobileNumberUtil::isSmsEnabled public function Checks if sms sending is enabled. Overrides MobileNumberUtilInterface::isSmsEnabled
MobileNumberUtil::isTfaEnabled public function Checks if tfa is enabled. Overrides MobileNumberUtilInterface::isTfaEnabled
MobileNumberUtil::isVerified public function Is the number already verified. Overrides MobileNumberUtilInterface::isVerified
MobileNumberUtil::libUtil public function Get libphonenumber Util instance. Overrides MobileNumberUtilInterface::libUtil
MobileNumberUtil::registerVerificationCode public function Registers code for mobile number and returns it's token. Overrides MobileNumberUtilInterface::registerVerificationCode
MobileNumberUtil::sendSms public function Sends an sms, based on callback provided by smsCallback(). Overrides MobileNumberUtilInterface::sendSms
MobileNumberUtil::sendVerification public function Send verification code to mobile number. Overrides MobileNumberUtilInterface::sendVerification
MobileNumberUtil::setTfaField public function Sets the tfa field configuration. Overrides MobileNumberUtilInterface::setTfaField
MobileNumberUtil::smsCallback public function Gets sms callback for sending SMS's. The callback should accept $number and $message, and returns status booleans. Overrides MobileNumberUtilInterface::smsCallback
MobileNumberUtil::testMobileNumber public function Test mobile number validity. Overrides MobileNumberUtilInterface::testMobileNumber
MobileNumberUtil::tfaAccountNumber public function Gets account mobile number if tfa was enabled for the user. Overrides MobileNumberUtilInterface::tfaAccountNumber
MobileNumberUtil::verifyCode public function Verifies input code matches code sent to user. Overrides MobileNumberUtilInterface::verifyCode
MobileNumberUtil::__construct public function MobileNumberUtil constructor.
MobileNumberUtilInterface::MOBILE_NUMBER_DEFAULT_SMS_MESSAGE constant
MobileNumberUtilInterface::MOBILE_NUMBER_NOT_VERIFIED constant Specifies the mobile number was not verified.
MobileNumberUtilInterface::MOBILE_NUMBER_TFA_DISABLED constant Specifies the tfa was disabled.
MobileNumberUtilInterface::MOBILE_NUMBER_TFA_ENABLED constant Specifies the tfa was enabled.
MobileNumberUtilInterface::MOBILE_NUMBER_UNIQUE_NO constant
MobileNumberUtilInterface::MOBILE_NUMBER_UNIQUE_YES constant
MobileNumberUtilInterface::MOBILE_NUMBER_UNIQUE_YES_VERIFIED constant
MobileNumberUtilInterface::MOBILE_NUMBER_VERIFIED constant Specifies the mobile number was verified.
MobileNumberUtilInterface::MOBILE_NUMBER_VERIFY_NONE constant
MobileNumberUtilInterface::MOBILE_NUMBER_VERIFY_OPTIONAL constant
MobileNumberUtilInterface::MOBILE_NUMBER_VERIFY_REQUIRED constant
MobileNumberUtilInterface::SMS_ATTEMPTS_COUNT constant
MobileNumberUtilInterface::SMS_ATTEMPTS_INTERVAL constant
MobileNumberUtilInterface::VERIFY_ATTEMPTS_COUNT constant
MobileNumberUtilInterface::VERIFY_ATTEMPTS_INTERVAL constant