文章內容關鍵字自動加連結功能,本地 (PHP 5.3) 正常,上線 (PHP 5.2) 提交後**頁面空白、無錯誤訊息、無輸出**。
pcre.backtrack_limit (預設 100000) 與 pcre.recursion_limit (預設 100000)preg_replace 返回 null → 後續邏輯處理 null 導致空白
$1', $content, $limit, $count);
// 優化策略
// A. 使用詞邊界 \b 避免部分匹配回溯
// B. 將多關鍵字合併為單一正則 (| 分隔),一次掃描完成
// C. 避免 .*? 貪婪/非貪婪在長文本中回溯
// 範例:批次替換所有關鍵字
$keywords = array_column($linkMap, 0); // 已按長度降序排序
$pattern = '/\b(' . implode('|', array_map('preg_quote', $keywords)) . ')\b/sui';
$content = preg_replace_callback($pattern, function($m) use ($linkMap) {
$kw = $m[1];
$url = $linkMap[$kw] ?? '#';
return '' . htmlspecialchars($kw) . '';
}, $content, $maxReplacements, $count);
?>
$chunk) {
$start = $i * $chunkSize;
// 取得含重疊的片段
$segment = substr($content, max(0, $start - $overlap), $chunkSize + $overlap * 2);
$processed = replaceKeywordsOnce($segment, $linkMap);
// 只取中間不重疊部分
$result .= substr($processed, $i === 0 ? 0 : $overlap, $chunkSize);
}
return $result;
}
?>
map[$keyword] = $url;
return $this;
}
public function setMaxReplacements(int $n): self {
$this->maxReplacements = $n;
return $this;
}
private function compilePattern(): void {
// 長關鍵字優先,避免「茶」吃掉「茶葉」
uksort($this->map, fn($a, $b) => strlen($b) <=> strlen($a));
$escaped = array_map('preg_quote', array_keys($this->map));
$this->pattern = '/\b(' . implode('|', $escaped) . ')\b/sui';
}
public function replace(string $content): string {
if (!$this->pattern) $this->compilePattern();
// 臨時調大 PCRE 限制
$oldBacktrack = ini_set('pcre.backtrack_limit', '1000000');
$oldRecursion = ini_set('pcre.recursion_limit', '1000000');
$result = preg_replace_callback($this->pattern, function($m) {
$kw = $m[1];
return '' . htmlspecialchars($kw) . '';
}, $content, $this->maxReplacements, $count);
ini_set('pcre.backtrack_limit', $oldBacktrack);
ini_set('pcre.recursion_limit', $oldRecursion);
return $result;
}
}
// 使用
$linker = new KeywordLinker();
$linker->addKeyword('PHP', 'https://php.net')
->addKeyword('Composer', 'https://getcomposer.org')
->setMaxReplacements(5);
$html = $linker->replace($articleContent);
?>
空白輸出多半是 **PCRE 回溯限制觸發導致 preg_replace 返回 null**。短期調大 ini_set 解燃眉之急,長期必須**優化正則結構、合併模式、分塊處理**。建議封裝為可複用類別,內建限制調整與錯誤監控。