Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

   1  <?php
   2  /**
   3   * Copyright 2014-2017 Horde LLC (http://www.horde.org/)
   4   *
   5   * See the enclosed file LICENSE for license information (BSD). If you
   6   * did not receive this file, see http://www.horde.org/licenses/bsd.
   7   *
   8   * @author   Michael Slusarz <slusarz@horde.org>
   9   * @category Horde
  10   * @license  http://www.horde.org/licenses/bsd BSD
  11   * @package  Idna
  12   */
  13  
  14  /**
  15   * Provide normalized encoding/decoding support for IDNA strings.
  16   *
  17   * @author    Michael Slusarz <slusarz@horde.org>
  18   * @category  Horde
  19   * @copyright 2014-2017 Horde LLC
  20   * @license   http://www.horde.org/licenses/bsd BSD
  21   * @package   Idna
  22   */
  23  class Horde_Idna
  24  {
  25      /**
  26       * The backend to use.
  27       *
  28       * @var mixed
  29       */
  30      protected static $_backend;
  31  
  32      /**
  33       * @throws Horde_Idna_Exception
  34       */
  35      public static function encode($data)
  36      {
  37          switch ($backend = static::_getBackend()) {
  38          case 'INTL':
  39              return idn_to_ascii($data);
  40  
  41          case 'INTL_UTS46':
  42              $result = idn_to_ascii($data, 0, INTL_IDNA_VARIANT_UTS46, $info);
  43              self::_checkForError($info);
  44              return $result;
  45  
  46          default:
  47              return $backend->encode($data);
  48          }
  49      }
  50  
  51      /**
  52       * @throws Horde_Idna_Exception
  53       */
  54      public static function decode($data)
  55      {
  56          switch ($backend = static::_getBackend()) {
  57          case 'INTL':
  58          case 'INTL_UTS46':
  59              $parts = explode('.', $data);
  60              foreach ($parts as &$part) {
  61                  if (strpos($part, 'xn--') === 0) {
  62                      switch ($backend) {
  63                      case 'INTL':
  64                          $part = idn_to_utf8($part);
  65                          break;
  66  
  67                      case 'INTL_UTS46':
  68                          $part = idn_to_utf8($part, 0, INTL_IDNA_VARIANT_UTS46, $info);
  69                          self::_checkForError($info);
  70                          break;
  71                      }
  72                  }
  73              }
  74              return implode('.', $parts);
  75  
  76          default:
  77              return $backend->decode($data);
  78          }
  79      }
  80  
  81      /**
  82       * Checks if the $idna_info parameter of idn_to_ascii() or idn_to_utf8()
  83       * contains errors.
  84       *
  85       * @param array $info  Fourth parameter to idn_to_ascii() or idn_to_utf8().
  86       *
  87       * @throws Horde_Idna_Exception
  88       */
  89      protected static function _checkForError($info)
  90      {
  91          if (!isset($info['errors'])) {
  92              return;
  93          }
  94          switch (true) {
  95          case $info['errors'] & IDNA_ERROR_EMPTY_LABEL:
  96              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
  97                  "Domain name is empty"
  98              ));
  99          case $info['errors'] & IDNA_ERROR_LABEL_TOO_LONG:
 100          case $info['errors'] & IDNA_ERROR_DOMAIN_NAME_TOO_LONG:
 101              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 102                  "Domain name is too long"
 103              ));
 104          case $info['errors'] & IDNA_ERROR_LEADING_HYPHEN:
 105              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 106                  "Starts with a hyphen"
 107              ));
 108          case $info['errors'] & IDNA_ERROR_TRAILING_HYPHEN:
 109              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 110                  "Ends with a hyphen"
 111              ));
 112          case $info['errors'] & IDNA_ERROR_HYPHEN_3_4:
 113              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 114                  "Contains hyphen in the third and fourth positions"
 115              ));
 116          case $info['errors'] & IDNA_ERROR_LEADING_COMBINING_MARK:
 117              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 118                  "Starts with a combining mark"
 119              ));
 120          case $info['errors'] & IDNA_ERROR_DISALLOWED:
 121              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 122                  "Contains disallowed characters"
 123              ));
 124          case $info['errors'] & IDNA_ERROR_PUNYCODE:
 125              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 126                  "Starts with \"xn--\" but does not contain valid Punycode"
 127              ));
 128          case $info['errors'] & IDNA_ERROR_LABEL_HAS_DOT:
 129              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 130                  "Contains a dot"
 131              ));
 132          case $info['errors'] & IDNA_ERROR_INVALID_ACE_LABEL:
 133              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 134                  "ACE label does not contain a valid label string"
 135              ));
 136          case $info['errors'] & IDNA_ERROR_BIDI:
 137              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 138                  "Does not meet the IDNA BiDi requirements (for right-to-left characters)"
 139              ));
 140          case $info['errors'] & IDNA_ERROR_CONTEXTJ:
 141              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 142                  "Does not meet the IDNA CONTEXTJ requirements"
 143              ));
 144          case $info['errors']:
 145              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 146                  "Unknown error"
 147              ));
 148          }
 149      }
 150  
 151      /**
 152       * Return the IDNA backend.
 153       *
 154       * @return mixed  IDNA backend (false if none available).
 155       */
 156      protected static function _getBackend()
 157      {
 158          if (!isset(self::$_backend)) {
 159              if (extension_loaded('intl')) {
 160                  /* Only available in PHP > 5.4.0 */
 161                  self::$_backend = defined('INTL_IDNA_VARIANT_UTS46')
 162                      ? 'INTL_UTS46'
 163                      : 'INTL';
 164              } else {
 165                  self::$_backend = new Horde_Idna_Punycode();
 166              }
 167          }
 168  
 169          return self::$_backend;
 170      }
 171  
 172  }