Php 如何检查域是否为punycode?

Php 如何检查域是否为punycode?,php,punycode,Php,Punycode,我使用idna_convert PHP类()对域名进行编码/解码 不幸的是,它似乎没有提供一个接口来检查域名是否已经是punycode 实现这一目标的最佳方式是什么?如果有人能发布如何验证域是否为punycode的源代码就好了(附带说明,因为idna_convert代码对我来说并不清楚)。我已经知道如何从idna_convert捕获异常。:-) 顺便说一句:idna_convert在您尝试将域名转换为已经是punycode的punycode时引发异常(请参阅;第157行)。此外,我真的不明白他们

我使用idna_convert PHP类()对域名进行编码/解码

不幸的是,它似乎没有提供一个接口来检查域名是否已经是punycode

实现这一目标的最佳方式是什么?如果有人能发布如何验证域是否为punycode的源代码就好了(附带说明,因为idna_convert代码对我来说并不清楚)。我已经知道如何从idna_convert捕获异常。:-)


顺便说一句:idna_convert在您尝试将域名转换为已经是punycode的punycode时引发异常(请参阅;第157行)。此外,我真的不明白他们的检查是如何工作的。

最简单的方法就是转换它,然后检查结果是否等于输入

编辑:您可以通过如下检查扩展Punycode类:

class PunycodeCheck extends Punycode
{
  public function check_encoded($decoded)
  {
      $extract = self::byteLength(self::punycodePrefix);
      $check_pref = $this->UnicodeTranscoder->utf8_ucs4array(self::punycodePrefix);
      $check_deco = array_slice($decoded, 0, $extract);
      if ($check_pref == $check_deco) 
          return true;
      return false;
   }
}

encode()
方法引发的唯一例外是当域已经是punycode时。因此,您可以执行以下操作:

try {
    $punycode->encode($decoded);
} catch (\InvalidArgumentException $e) {
    //do whatever is needed when already punycode
    //or do nothing
}

不过,这是一个变通解决方案。

这取决于您到底想要什么

作为第一个基本检查,请查看域名是否仅包含ASCII字符。如果是的话,那么这个域“已经是punycode了”,也就是说它无法进一步转换。有关检查字符串是否仅包含ASCII字符的信息,请参阅

如果在此基础上,要检查域是否为IDN形式,请在点处拆分域
,并检查是否有任何子字符串以
xn--
开头


除此之外,如果您想检查域是否为IDN且有效,只需尝试使用库的解码功能对其进行解码。

检查域是否在Punycode中并不容易。根据@Wladston已经说过的规则,需要执行多个检入

这是我从我的库的组合中选取的
ValidateHelper
类中改编的代码示例:。我还添加了测试及其执行结果

/**
 * Validate helper.
 *
 * @author Maksim T. <zapalm@yandex.com>
 */
class ValidateHelper
{
    /**
     * Checks if the given domain is in Punycode.
     *
     * @param string $domain The domain to check.
     *
     * @return bool Whether the domain is in Punycode.
     *
     * @see https://developer.mozilla.org/en-US/docs/Mozilla/Internationalized_domain_names_support_in_Mozilla#ASCII-compatible_encoding_.28ACE.29
     *
     * @author Maksim T. <zapalm@yandex.com>
     */
    public static function isPunycodeDomain($domain)
    {
        $hasPunycode = false;

        foreach (explode('.', $domain) as $part) {
            if (false === static::isAscii($part)) {
                return false;
            }

            if (static::isPunycode($part)) {
                $hasPunycode = true;
            }
        }

        return $hasPunycode;
    }

    /**
     * Checks if the given value is in ASCII character encoding.
     *
     * @param string $value The value to check.
     *
     * @return bool Whether the value is in ASCII character encoding.
     *
     * @see https://en.wikipedia.org/wiki/ASCII
     *
     * @author Maksim T. <zapalm@yandex.com>
     */
    public static function isAscii($value)
    {
        return ('ASCII' === mb_detect_encoding($value, 'ASCII', true));
    }

    /**
     * Checks if the given value is in Punycode.
     *
     * @param string $value The value to check.
     *
     * @return bool Whether the value is in Punycode.
     *
     * @throws \LogicException If the string is not encoded by UTF-8.
     *
     * @see https://en.wikipedia.org/wiki/Punycode
     *
     * @author Maksim T. <zapalm@yandex.com>
     */
    public static function isPunycode($value)
    {
        if (false === static::isAscii($value)) {
            return false;
        }

        if ('UTF-8' !== mb_detect_encoding($value, 'UTF-8', true)) {
            throw new \LogicException('The string should be encoded by UTF-8 to do the right check.');
        }

        return (0 === mb_stripos($value, 'xn--', 0, 'UTF-8'));
    }
}

/**
 * Test Punycode domain validator.
 *
 * @author Maksim T. <zapalm@yandex.com>
 */
class Test
{
    /**
     * Run the test.
     *
     * @author Maksim T. <zapalm@yandex.com>
     */
    public static function run()
    {
        $domains = [
            // White list
            'почта@престашоп.рф'          => false, // Russian, Unicode
            'modulez.ru'                  => false, // English, ASCII
            'xn--80aj2abdcii9c.xn--p1ai'  => true,  // Russian, ASCII
            'xn--80a1acn3a.xn--j1amh'     => true,  // Ukrainian, ASCII
            'xn--srensen-90a.example.com' => true,  // German, ASCII
            'xn--mxahbxey0c.xn--xxaf0a'   => true,  // Greek, ASCII
            'xn--fsqu00a.xn--4rr70v'      => true,  // Chinese, ASCII

            // Black List
            'xn--престашоп.xn--рф'        => false, // Russian, Unicode
            'xn--prestashop.рф'           => false, // Russian, Unicode
        ];

        foreach ($domains as $domain => $isPunycode) {
            echo 'TEST: ' . $domain . (ValidateHelper::isPunycodeDomain($domain)
                ? ' is in Punycode [' . ($isPunycode ? 'OK' : 'FAIL') . ']'
                : ' is NOT in Punycode [' . (false === $isPunycode ? 'OK' : 'FAIL') . ']'
            ) . PHP_EOL;
        }
    }
}

Test::run();

// The output result:
//
// TEST: почта@престашоп.рф is NOT in Punycode [OK]
// TEST: modulez.ru is NOT in Punycode [OK]
// TEST: xn--80aj2abdcii9c.xn--p1ai is in Punycode [OK]
// TEST: xn--80a1acn3a.xn--j1amh is in Punycode [OK]
// TEST: xn--srensen-90a.example.com is in Punycode [OK]
// TEST: xn--mxahbxey0c.xn--xxaf0a is in Punycode [OK]
// TEST: xn--fsqu00a.xn--4rr70v is in Punycode [OK]
// TEST: xn--престашоп.xn--рф is NOT in Punycode [OK]
// TEST: xn--prestashop.рф is NOT in Punycode [OK]
/**
*验证帮助程序。
*
*@author Maksim T。
*/
类ValidateHelper
{
/**
*检查给定域是否在Punycode中。
*
*@param string$domain要检查的域。
*
*@return bool域是否为Punycode。
*
*@见https://developer.mozilla.org/en-US/docs/Mozilla/Internationalized_domain_names_support_in_Mozilla#ASCII-兼容编码28ACE.29
*
*@author Maksim T。
*/
公共静态函数isPunycodeDomain($domain)
{
$hasPunycode=false;
foreach(分解('.',$domain)为$part){
if(false==静态::isAscii($part)){
返回false;
}
if(静态::isPunycode($part)){
$hasPunycode=true;
}
}
返回$hasPunycode;
}
/**
*检查给定值是否采用ASCII字符编码。
*
*@param string$value要检查的值。
*
*@return bool值是否为ASCII字符编码。
*
*@见https://en.wikipedia.org/wiki/ASCII
*
*@author Maksim T。
*/
公共静态函数isAscii($value)
{
返回('ASCII'==mb_detect_encoding($value,'ASCII',true));
}
/**
*检查给定值是否在Punycode中。
*
*@param string$value要检查的值。
*
*@return bool值是否为Punycode。
*
*@throws\LogicException(如果字符串未由UTF-8编码)。
*
*@见https://en.wikipedia.org/wiki/Punycode
*
*@author Maksim T。
*/
公共静态函数isPunycode($value)
{
if(false==static::isAscii($value)){
返回false;
}
如果('UTF-8'!==mb\u检测\u编码($value,'UTF-8',true)){
抛出new\LogicException('字符串应该由UTF-8编码以进行正确的检查');
}
返回值(0==mb_stripos($value,'xn--',0,'UTF-8');
}
}
/**
*测试Punycode域验证程序。
*
*@author Maksim T。
*/
课堂测试
{
/**
*运行测试。
*
*@author Maksim T。
*/
公共静态函数run()
{
$domains=[
//白名单
'Пча@ПаааПааПа.Пф'=>false,//俄语,Unicode
'modulez.ru'=>false,//英语,ASCII
'xn--80aj2abdci9c.xn--p1ai'=>true,//俄语,ASCII
'xn--80a1acn3a.xn--j1amh'=>true,//乌克兰语,ASCII
'xn--srensen-90a.example.com'=>true,//德语,ASCII
'xn--mxahbxey0c.xn--xxaf0a'=>true,//希腊语,ASCII
'xn--fsqu00a.xn--4rr70v'=>true,//中文,ASCII
//黑名单
'xn--ПППСааП.xn--Пф'=>false,//俄语,Unicode
'xn--prestashop.crmk_ф'=>false,//俄语,Unicode
];
foreach($domain=>$isPunycode的域){
echo“TEST:”.$domain.(ValidateHelper::isPunycodeDomain($domain)
“?”位于Punycode[”($isPunycode?'OK':'FAIL')。]”
:'不在Punycode['(false===$isPunycode?'OK':'FAIL')。]
).PHP_EOL;
}
}
}
Test::run();
//输出结果:
//
//测试:Пчаа@ПаааСаааП。аф不在Punycode中[正常]
//测试:modulez.ru不在Punycode中[OK]
//测试:xn--80aj2abdcii9c.xn--p1ai在Punycode中[OK]
//测试:xn--80a1acn3a.xn--j1amh在Punycode中[正常]
//测试:xn--srensen-90a.example.com在Punycode中[OK]
//测试:xn--mxahbxey0c.xn--xxaf0a在Punycode中[正常]
//测试:xn--fsqu00a.xn--4rr70v在Punycode中[正常]
//测试:xn--ППССааП.xn--Пф不在Punycode中[正常]
//测试:xn——预ashop.ф不在Punycode中[正常]

这是一个很好的建议,但不幸的是它不起作用,因为idna_convert会抛出一个异常w