PHP正则表达式性能优化指南
PHP正则表达式性能优化指南
正则表达式虽然强大,但性能问题是常见陷阱。一个写不好的正则可能会导致严重的性能问题。今天说说正则表达式的性能优化。
灾难性回溯是最常见的性能问题。当一个正则表达式在匹配失败时需要尝试大量的回溯时,可能导致CPU飙升甚至超时。
```php
// 灾难性回溯示例
function testPattern(string $pattern, string $subject): void
{
$start = microtime(true);
preg_match($pattern, $subject, $matches);
$time = (microtime(true) - $start) * 1000;
echo "模式: $pattern\n";
echo "耗时: " . round($time, 2) . "ms\n";
echo "匹配: " . ($matches[0] ?? '无') . "\n\n";
}
// 危险的模式
$dangerous = '/(a*)*b/';
$safe = '/(a+)*b/';
$subject = str_repeat('a', 25);
testPattern($dangerous, $subject);
testPattern($safe, $subject);
// 使用原子组防止回溯
$atomic = '/(?>(a*))*b/';
testPattern($atomic, $subject);
?>
```
非贪婪匹配可以避免不必要的回溯:
```php
$html = "
段落1
段落2
段落3
";
// 贪婪匹配
preg_match('/
.*<\/p>/', $html, $greedy);
echo "贪婪: {$greedy[0]}\n\n";
// 非贪婪匹配
preg_match('/
.*?<\/p>/', $html, $lazy);
echo "非贪婪: {$lazy[0]}\n\n";
// 所有段落
preg_match_all('/
(.*?)<\/p>/', $html, $paragraphs);
echo "所有段落:\n";
foreach ($paragraphs[1] as $p) {
echo " $p\n";
}
?>
```
正则表达式的编译也有开销。重复使用同一个正则时,可以预编译:
```php
// 不推荐:每次匹配都重新编译
function validateEmail(string $email): bool
{
return preg_match('/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/', $email) === 1;
}
// 推荐:使用预编译
class EmailValidator
{
private string $pattern;
public function __construct()
{
$this->pattern = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/';
}
public function validate(string $email): bool
{
return preg_match($this->pattern, $email) === 1;
}
}
$validator = new EmailValidator();
$emails = ['test@test.com', 'invalid', 'user@example.com', 'another@test.co.uk'];
foreach ($emails as $email) {
echo "$email: " . ($validator->validate($email) ? '有效' : '无效') . "\n";
}
?>
```
能用字符串函数解决的问题不要用正则。
```php
// 不推荐:用正则做简单操作
$url = "https://example.com/page";
if (preg_match('/^https:\/\//', $url)) {
echo "是HTTPS\n";
}
// 推荐:用字符串函数
if (str_starts_with($url, 'https://')) {
echo "是HTTPS\n";
}
// 不推荐:用正则替换固定字符串
$text = "Hello World";
$result = preg_replace('/World/', 'PHP', $text);
// 推荐:用str_replace
$result = str_replace('World', 'PHP', $text);
?>
```
正则表达式在数据验证和数据提取中很有用,但不要滥用。能用字符串函数解决的场景就别用正则。写正则的时候多考虑一下边界情况,避免陷入灾难性回溯的陷阱。性能测试也很重要,对可疑的模式做一下基准测试。
