PHP 趣味:regex builder 在 8.5 中
Source: Dev.to
为什么?
主要原因是这是一种生成文本的构建器,所以不应该需要实例化对象。
第二个原因是应该直接使用 PHP 内置的正则函数,而不是把它们包装在对象方法中:
$match = preg_match($pattern, 'test'); // instead of duplicates.IsMatch('test')
它将如何工作?
我不会创建完整的库;我会用管道运算符的形式突出 C# 库的几种可能性。
起始可以是空字符串、带分隔符的字符串,甚至是分隔符函数。
$pattern = '' |> anyCharacter(...); // regex: .*
// or
$pattern = '/' |> anyCharacter(...);
// or
$pattern = delimiter() |> anyCharacter(...);
该库使用类常量(例如 Pattern.With.LowercaseLetter)来添加已知字符模式。在 PHP 中这可以是带值的枚举,而 anyCharacter 函数可以改为带参数的 any。
enum CharacterPattern: string
{
case Any = '.';
case LowercaseLetter = '[a-z]';
case Word = '\w';
}
function any(string $pattern, CharacterPattern|string $add = CharacterPattern::Any): string
{
$addPattern = $add instanceof CharacterPattern ? $add->value : $add;
return "$pattern$addPattern*";
}
// examples
$pattern = '' |> fn($p) => any($p);
$pattern = '' |> fn($p) => any($p, CharacterPattern::LowercaseLetter);
$pattern = '' |> fn($p) => any($p, '[sunday|monday]');
对于最后一个示例,库需要一个 literal 方法;类型提示已经足够。
为了完成量化模式函数,我们可以添加 exact 和 atLeast 函数。
正向前瞻
PositiveLookahead 方法可能导致嵌套函数调用。将其拆分为两个函数可以保持构建器的平坦结构。
function positiveLookaheadStart(string $pattern, string $times = ''): string
{
return "$pattern(?=$times";
}
function positiveLookaheadEnd(string $pattern): string
{
return "$pattern)";
}
// example
$pattern = ''
|> fn($p) => positiveLookaheadStart($p, '.*')
|> fn($p) => any($p, '[sunday|monday]')
|> fn($p) => positiveLookaheadEnd($p);
命名组和反向引用
像 NamedGroup 这样的方法可以拆分为 group 函数,同时也处理非捕获组。反向引用仅仅是 \1 或 \k。
function backReference(string $pattern, int|string $add): string
{
$reference = is_string($add) ? "k" : $add;
return "$pattern\\$reference";
}
结论
使用管道运算符的好处在于代码更少,因为每个函数只做一件事且没有副作用。这也意味着需要测试的边缘情况更少,因为正则构建发生在语言层面。
一个小缺点是通用函数名可能没有流畅 API 那么直观。为了解决这个问题,你可以添加自己的描述性函数,使库易于扩展。
下次考虑构建器模式时,问问自己管道运算符是否更合适。
PS: 不要在生产环境中使用正则构建器;请使用预热缓存中的生成模式。