.*?<\/code>|.*?<\/pre>|`[^`]*`)/is';
+
+ $parts = preg_split($splitPattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE);
+
+ if ($parts === false) {
+ // 分割失败,使用备用方法
+ $processed = self::replaceWithWordInterval($text, $pattern, $replacements, $options->skipChars, 0);
+ $text = $processed['text'];
+ } else {
+ $result = '';
+ $currentPos = 0;
+
+ foreach ($parts as $i => $part) {
+ // 奇数索引是匹配的部分(保持原样)
+ if ($i % 2 == 1) {
+ $result .= $part;
+ // 更新当前位置
+ $currentPos += mb_strlen(strip_tags($part), 'UTF-8');
+ } else {
+ // 偶数索引是非匹配文本,进行替换
+ $processed = self::replaceWithWordInterval($part, $pattern, $replacements, $options->skipChars, $currentPos);
+ $result .= $processed['text'];
+ $currentPos += $processed['char_count'];
+ }
+ }
+
+ $text = $result;
+ }
+
+ // 恢复被保护的标题
+ $text = self::restoreHeaders($text);
+ } else {
+ // 直接替换所有匹配(带间隔限制)
+ $processed = self::replaceWithWordInterval($text, $pattern, $replacements, $options->skipChars, 0);
+ $text = $processed['text'];
+ }
+
+ return $text;
+ }
+
+ /**
+ * 保护标题内容不被替换
+ */
+ private static function protectHeaders($text, $excludeHeaders)
+ {
+ if (!is_array($excludeHeaders) || empty($excludeHeaders)) {
+ return $text;
+ }
+
+ $headerTags = array();
+ foreach ($excludeHeaders as $header) {
+ $headerTags[] = $header;
+ }
+
+ // 为每个标题标签创建唯一的占位符
+ $placeholderMap = array();
+ $placeholderIndex = 0;
+
+ foreach ($headerTags as $tag) {
+ // 匹配标题标签及其内容
+ $pattern = '/<(' . $tag . ')\b[^>]*>(.*?)<\/\1>/is';
+
+ $text = preg_replace_callback($pattern, function($matches) use (&$placeholderMap, &$placeholderIndex) {
+ $placeholder = "";
+ $placeholderMap[$placeholder] = $matches[0];
+ $placeholderIndex++;
+ return $placeholder;
+ }, $text);
+ }
+
+ // 将占位符映射存储在文本中(隐藏的注释)
+ if (!empty($placeholderMap)) {
+ $mapJson = json_encode($placeholderMap);
+ $text = "\n" . $text;
+ }
+
+ return $text;
+ }
+
+ /**
+ * 恢复被保护的标题内容
+ */
+ private static function restoreHeaders($text)
+ {
+ // 查找并提取占位符映射
+ if (preg_match('//', $text, $mapMatch)) {
+ $mapJson = base64_decode($mapMatch[1]);
+ $placeholderMap = json_decode($mapJson, true);
+
+ if (is_array($placeholderMap)) {
+ // 替换所有占位符回原始内容
+ foreach ($placeholderMap as $placeholder => $original) {
+ $text = str_replace($placeholder, $original, $text);
+ }
+
+ // 移除映射注释
+ $text = preg_replace('/\n?/', '', $text);
+ }
+ }
+
+ return $text;
+ }
+
+ /**
+ * 带有相同词间隔限制的替换函数
+ */
+ private static function replaceWithWordInterval($text, $pattern, &$replacements, $skipChars, $startPos)
+ {
+ $result = '';
+ $offset = 0;
+ $currentCharPos = $startPos;
+
+ // 使用preg_match_all获取所有匹配位置
+ if (preg_match_all($pattern, $text, $matches, PREG_OFFSET_CAPTURE)) {
+ foreach ($matches[0] as $match) {
+ $matchedWord = $match[0];
+ $matchPos = $match[1];
+ $matchLength = strlen($matchedWord);
+
+ // 计算匹配位置在当前文本中的字符位置
+ $textBeforeMatch = substr($text, 0, $matchPos);
+ $charPosInText = mb_strlen($textBeforeMatch, 'UTF-8');
+
+ // 实际在全文中的字符位置
+ $actualCharPos = $startPos + $charPosInText;
+
+ // 获取这个词的替换数据
+ $wordData = &$replacements[$matchedWord];
+
+ // 检查是否需要跳过(同一个词距离上次出现太近)
+ if ($actualCharPos - $wordData['last_pos'] < $skipChars) {
+ // 跳过这个匹配,直接添加到结果
+ if ($matchPos > $offset) {
+ $result .= substr($text, $offset, $matchPos - $offset);
+ }
+ $result .= $matchedWord; // 原样输出,不替换
+ $offset = $matchPos + $matchLength;
+ continue;
+ }
+
+ // 执行替换
+ if ($matchPos > $offset) {
+ $result .= substr($text, $offset, $matchPos - $offset);
+ }
+
+ $result .= $wordData['replace'];
+ $offset = $matchPos + $matchLength;
+
+ // 更新这个词的最后出现位置
+ $wordData['last_pos'] = $actualCharPos;
+ }
+ }
+
+ // 添加剩余文本
+ if ($offset < strlen($text)) {
+ $result .= substr($text, $offset);
+ }
+
+ // 计算处理后的文本字符数(不包括HTML标签)
+ $charCount = mb_strlen(strip_tags($result), 'UTF-8');
+
+ return array(
+ 'text' => $result,
+ 'char_count' => $charCount
+ );
+ }
+
+ /**
+ * 构建URL
+ */
+ private static function buildUrl($format, $slug, $type = 'tag')
+ {
+ $options = Helper::options();
+ $siteUrl = rtrim($options->siteUrl, '/');
+
+ if (empty($slug)) {
+ return '#';
+ }
+
+ // 处理格式中的变量
+ $url = str_replace('{slug}', urlencode($slug), $format);
+
+ // 确保URL是完整的
+ if (strpos($url, 'http') !== 0 && strpos($url, '//') !== 0) {
+ if (strpos($url, '/') === 0) {
+ $url = $siteUrl . $url;
+ } else {
+ $url = $siteUrl . '/' . $url;
+ }
+ }
+
+ return $url;
+ }
+}
\ No newline at end of file