PHP Intl SpoofChecker :: isSuspicious()产生误报

时间:2018-04-06 07:35:59

标签: php intl

案例

似乎来自Spoofchecker扩展的Intl会产生误报:

<?php  // 7.0 on linux
// File encoding of this script is UTF-8 (thus without BOM)
$sDefaultLocale = (new \Locale)->getDefault();
$oSpoofchecker = new \Spoofchecker;
$oSpoofchecker->setAllowedLocales($sDefaultLocale);
$sText = 'abc';  // US-ASCII
header('Content-Type: text/plain');
print
    'Default locale: ' . $sDefaultLocale . PHP_EOL
  . 'Byte length: ' . strlen($sText) . PHP_EOL  // US-ASCII check
  . 'Text "' . $sText . '" '
  . ($oSpoofchecker->isSuspicious($sText, $sError) ? 'IS' : 'IS NOT')
  . ' suspicious' . PHP_EOL
  . 'Spoofchecker internal error information:' . PHP_EOL;
var_dump($sError);

结果

Default locale: en_US_POSIX
Byte length: 3
Text "abc" IS suspicious
Spoofchecker internal error information:
NULL    

预期结果

Text "abc" IS NOT suspicious

这是因为abc是US-ASCII, suppmably 应该是en_US_POSIX的默认值。另外PHP Spoofchecker class提到如果使用任何非英文字符,则Spoofchecker::isSuspicious()的返回码将为TRUE,而这不是这种情况。

可能的原因

documentation of Spoofchecker::setAllowedLocales()目前接近于不存在,参数列表不包含可能值的列表。只能假设它必须与Locale的那个兼容。文档内容如下:

  

使用RFC 4646语言标记(使用连字符,而不是下划线)标识语言环境

与测试结果相矛盾,其中Locale使用下划线表示默认语言环境而不是连字符。但是当使用$oSpoofchecker->setAllowedLocales('en-US');运行另一个测试时,结果保持不变。

问题

如何正确使用Spoofchecker::isSuspicious()

2 个答案:

答案 0 :(得分:2)

PHP的Intl扩展只是ICU的包装,其中Spoofchecker从ICU版本58开始减少了误报。

来自bug tracker

  

ICU 58反映了最新的Unicode更新,该更新弃用了   全脚本可混淆(WSC)检查和混合脚本可混淆   (MSC)检查,可在   http://www.unicode.org/L2/L2016/16229-revising-uts-39-algorithm.pdf

     

根据ICU 57,支票(WSC和MSC)存在以下缺陷:

     
      
  1. 他们没有将自己限制为指定的字符集   SpoofChecker#setAllowedChars或SpoofChecker#setAllowedLocales。
  2.   
  3. 他们没有正确处理包含多个骨架的混淆因素   字符,如'æ'到'ae'。
  4.   
  5. WSC表现出很高的假阳性   速率,特别是随着越来越多的条目被添加到   confusables.txt
  6.   
  7. 所有字符串失败的MSC也会失败限制级别。   (你的字符串“goօgle”就是一个例子。)
  8.         

    考虑到这些陷阱,   WSC和MSC从ICU 58中删除。

强调我的。 WSC检查是您的字符串failing。 (请注意,它通过ICU版本为58.1及以上,因为该检查已完全删除。)

关于如何正确使用Spoofchecker :: isSuspicious():

  1. 升级ICU(一般来说这是一个好主意)或
  2. Syscall's answer中所述使用printf()并省略WSC检查Spoofchecker::setChecks()(涵盖此案例)和MSC检查Spoofchecker::WHOLE_SCRIPT_CONFUSABLE(同样从最近版本中移除) 。)

答案 1 :(得分:1)

您可以使用Spoofchecker::setChecks(int $checks)指定如何验证字符串。

$checks常量列在Spoofchecker类documentation中,并由用户in comments描述。

您可以使用SpoofChecker::CHAR_LIMIT(或多个常量的组合,例如:SpoofChecker::CHAR_LIMIT|Spoofchecker::INVISIBLE):

  

CHAR_LIMIT:检查标识符是否仅包含指定可接受字符集中的字符   INVISIBLE:检查标识符是否存在不可见字符,例如零宽度空格或可能不显示的字符序列,例如多次出现相同的非间距标记。

$sDefaultLocale = (new \Locale)->getDefault();
$oSpoofchecker = new \Spoofchecker;
$oSpoofchecker->setAllowedLocales($sDefaultLocale);
$oSpoofchecker->setChecks(SpoofChecker::CHAR_LIMIT);
$sText = 'abc';  // US-ASCII
header('Content-Type: text/plain');
print
    'Default locale: ' . $sDefaultLocale . PHP_EOL
  . 'Byte length: ' . strlen($sText) . PHP_EOL  // US-ASCII check
  . 'Text "' . $sText . '" '
  . ($oSpoofchecker->isSuspicious($sText, $sError) ? 'IS' : 'IS NOT')
  . ' suspicious' . PHP_EOL
  . 'Spoofchecker internal error information:' . PHP_EOL;
var_dump($sError);

输出:

Default locale: en_US_POSIX
Byte length: 3
Text "abc" IS NOT suspicious
Spoofchecker internal error information:
NULL

使用isSuspicious() documentation中的示例,文本Рaypal.com(第一个字母来自Cyrylic),返回的方法为:

Text "Рaypal.com" IS suspicious