bishopb/pattern

一个具有一致、流畅 API 的字符串匹配 PHP 库。

dev-master 2020-12-29 02:48 UTC

This package is auto-updated.

Last update: 2024-08-29 04:14:28 UTC


README

Latest Stable Version Build Status Coverage Status Monthly Downloads

警告:此 API 不存在。在实现它并测量性能后,我决定开销没有达到我的目标,因此放弃了这段代码。在 PHP 8 中情况可能不同。如果您喜欢这个 API,并且想看到它变得活跃,请实现它并提交一个 PR。

Pattern 是一个具有一致、流畅 API 的字符串匹配 PHP 库。Pattern 统一了 strcmp 和其家族、fnmatchpreg_matchversion_compare 的 API,同时提供了对常见字符串匹配操作的便捷方法。

如果您有以下需求,Pattern 可能适合您:

  • 您希望编写可读的字符串匹配代码,清晰地描述您的意图。
  • 您对没有简单、内置的实现来查找一个字符串是否以另一个字符串结尾感到沮丧。
  • 您想在执行简单的字符串检查时避免愚蠢的偏移量错误。
  • 您希望针对特定类型的字符串检查使用最佳算法,无论该算法是 strstr 还是 strpos
  • 您厌倦了参考 PHP 用户手册来查找 strpos 和其朋友的参数顺序。

快速入门

使用 Composer 安装:composer require bishopb/pattern:dev-master

使用

use BishopB\Pattern;

// common matching API regardless of pattern language
$subjects = array ( 'Capable', 'Enabler', 'Able', );
$patterns = array (
    new Pattern\Literal('Able'),
    new Pattern\Wildcard('*able*')
);
foreach ($subjects as $subject) {
    foreach ($patterns as $pattern) {
        $pattern->matches($subject) and "$pattern matches $subject";
    }
}

// literal matching sugar
$able = new Pattern\Literal('Able')->fold();
$able->foundIn('tablet');
$able->begins('Abletic');
$able->ends('Parable');
$able->sorts->before('active');
$able->sorts->after('aardvark');

// version matching sugar
$stable = new Pattern\Version('1.0.0');
$stable->matches('1.0.0');
$stable->before('1.0.1');
$stable->after('0.9.9');

动机

在 PHP 中,开发者有四种常见的方式来匹配字符串到模式

这些 API 几乎没有问题

  • $pattern 是一个普通的旧字符串,这意味着你可能会犯出像这样的错误:fnmatch('^foo.*bar', $input)
  • strcmp 和家族返回一个可排序的结果,这并不鼓励有意编程。考虑:if (! strcasecmp('foo', $input)) { echo 'pop quiz: matches foo?'; }
  • 用于执行字面比较的函数散布在各个地方:strcmpstrcasecmpstrposstripos 等。
  • strcasecmp== 都是 危险的 字符串比较方式。
  • 很难记住哪个参数是模式,哪个是主题(比较 strpospreg_match)。
  • 在比较函数中指定 "不区分大小写" 的方法差异很大。
  • 如果您最初希望代码接受字面匹配,然后想支持正则表达式,您必须重写您的代码。
  • 并非每个平台都支持 fnmatch

此库通过在内置模式匹配函数上提供快速、薄的抽象来缓解这些问题。

性能

此包的哲学很简单:以最小的运行时成本提供语法糖。API 调用是请求匹配的最快实现的薄面纱。尽可能节省空间。

运行时基准测试

不同测试的每秒操作次数。

峰值内存消耗基准测试

注意:所有基准测试都在一个小型、未加载的 EC2 实例上运行了至少 1,000 次,使用 PHP 5.3。有关实际代码,请参阅 tests/*Event.php。有关不同 PHP 版本的运行时间,请参阅 Travis CI 构建。

高级使用

操作搜索主题

通常模式类(LiteralWildcardPcre)的方法接受字符串。但是,您也可以传递 Subject 实例,这是一个适合于字符串比较的常见方法的轻量级字符串类。

use BishopB\Pattern;

$device  = new Literal('Tablet')->fold();
$version = new Version('8.1');
$subject = new Subject('    Microsoft Tablet running Windows 8.1.0RC242.')-trim();

$device->matches(
    $subject->
    column(' ', 1) // explode at space and get the 1st index (0-based)
);
$version->after(
    $subject->
    column(' ', -1)-> // explode at space and get the last index (nth-from last)
    substring(0, 4)   // only the first 5 characters
);

对大文本或重复搜索进行更快的搜索

当您的主题文本很长,或者您预计将字面模式与许多不同的主题进行比较时,研究字面模式以提高性能是值得的。

// notice the use of study()
// without this, searching would be much slower
$zebra = new Literal('zebra')->fold()->study();
$words = file_get_contents('/usr/share/dict/words') or die('No dictionary');
$zebra->foundIn($words);

你可能想知道:“长”指的是多少个字符?或者,“很多”指的是多少次迭代?嗯,我想这取决于具体情况。但是,很久以前,一些PHP内建的基准测试表明,长度超过5000或更多会使研究变得值得。

常见问题解答(FAQ)

为什么不直接使用内建函数呢?

以上已经提到的原因。我个人写这个库是因为我经常参考官方文档来了解内建函数的参数顺序,也因为常见的用例没有简洁的处理方式。总之,这个库让我可以写更少的代码,并且意思更清晰。

例如,我看到很多代码遵循以下模式

if (! strcmp($actual, $expected)) {
    $this->doSomething();
} else {
    throw new \RuntimeException('Actual does not match expected');
}

技术上是对的。但是,对我来说,它看起来是错误的。我觉得这样更容易阅读

if ($actual->matches($expected)) {
    $this->doSomething();
} else {
    throw new \RuntimeException('Actual does not match expected');
}

有一个相关的副作用。在弱模式PHP中,接收无效参数的函数会发出警告并返回null。由于null会被评估为假值,上面的例子会意外地执行doSomething。考虑以下情况

// ?password=[]
if (! strcmp($_GET['password'], $user->password)) {
    $this->login($user);
} else {
    throw new \RuntimeException('Invalid password');
}

为什么?因为true === (! (null === strcmp(array (), '******')))。在这个库中,如果你尝试用数组进行匹配,会抛出异常。

为什么不向Subject添加更多字符串方法,比如length()呢?

这个包的总体目标是以最轻量级的方式支持模式匹配。给Subject添加与模式匹配无关的方法与这个目标相冲突。