PHP |复制不包括标题属性的特定单词

PHP | Replicate specific word excluding the title attribute

我正在尝试替换“自定义”一词并用 <span> custom </span> 复制它。 使用 str_replace () 函数它可以工作,但这也会在 title 属性中替换它,我不希望发生这种情况,因为 title 中的 span 标记是一个错误。 如何在不触及标题属性的情况下替换“自定义”一词?

这是我的代码:

$oldText = "custom";
$newText = "<span>custom</span>";
$string = "<a href='#' title='Products custom'>Products custom</a>";
str_ireplace($oldText, $newText,$string);

这只是一个例子。 custom这个词也可以放在字符串的中间或者开头...

谢谢

您可能必须使用 PHP's DOM parser 才能做到这一点。写一个正则表达式来解决它并不适用于所有情况。

A) DOM

我会从 开始,然后稍微更改一下以完成您想要做的事情。当您将 custom 替换为 <span>custom</span> 时,您将创建一个新的 DOM 元素。替换文本内容将不起作用,因为 <span> 将被转义并替换为 &lt;span&gt;

所以我会这样做:

  1. 使用preg_match_all()/\bcustom\b/等模式来获取文本中找到的项目的所有偏移量:

    // Search for the word, but delimited by word boundaries to
    // avoid matching 'custom' in 'customization' or 'customer'.
    $pattern = '/\b' . preg_quote($word_to_search) . '\b/';
    if (preg_match_all($pattern, $child->wholeText, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
        var_export($matches);
    }
    
  2. 将这些以字节为单位的偏移量转换为以字符为单位的偏移量(这是因为 UTF-8 可以有 1 个或 n 个字节的字符):

    function char_offset($string, $byte_offset, $encoding = null)
    {
        $substr = substr($string, 0, $byte_offset);
        return mb_strlen($substr, $encoding ?: mb_internal_encoding());
    }
    
  3. 使用DOMText::splitText()将文本节点拆分为两个文本节点,偏移量以字符为单位。

  4. 使用 DOMDocument::createElement()

    创建一个 <span> 元素
    $new_text = 'custom'; // or whatever.
    $spanElement = $domNode->ownerDocument->createElement('span', $new_text);
    
  5. 在具有 DOMNode::insertBefore()

    的第二个文本节点之前插入此跨度元素
  6. 更正第二个文本节点以删除开头的 custom 单词。

B) 使用正则表达式

但是如果你的案例总是在 <a> 标签中,那么你可以尝试这样的事情:https://regex101.com/r/ksPqxe/1

正则表达式的解释请看右栏的说明。如果需要,您可以删除 case-insensitive 的 i 标志。使用 s 标志以便 . 也匹配新行。我不得不使用带有 .*? 而不是 .* 的非贪婪搜索。所以最后我使用 U 作为 Ungreedy 标志,然后使用 .*.

此解决方案无法处理 link 中多个 custom 个单词的情况。但你可能只有一次。如果需要,则使用一个正则表达式获取 link 的文本内容,然后使用第二个正则表达式将 custom 的所有实例替换为 <span>custom</span>.

<?php

$pattern = '/(<a[^>]*>.*)\bcustom\b(.*<\/a>)/isU';
// Or without the ungreedy flag:
//$pattern = '/(<a[^>]*>.*?)\bcustom\b(.*?<\/a>)/is';

$substitution = '<span>custom</span>';

$inputs = [
    "<a href='#' title='Products custom'>Products custom</a>",
    '<a href="https://www.customer.com" title="customer" data-type="custom">Custom stuff</a>',
    '<a href=\"https://www.customer.com\" title=\"customer" 
    data-type="custom">Customer stuff</a>',
    '<a href="#">customize it!</a>',
];

$results = [];
foreach ($inputs as $input) {
    $result = preg_replace($pattern, $substitution, $input);
    
    $results[] = "$input\n$result\n";
}

print implode(str_repeat('-', 80) . "\n", $results);

输出:

<a href='#' title='Products custom'>Products custom</a>
<a href='#' title='Products custom'>Products <span>custom</span></a>
--------------------------------------------------------------------------------
<a href="https://www.customer.com" title="customer" data-type="custom">Custom stuff</a>
<a href="https://www.customer.com" title="customer" data-type="custom"><span>custom</span> stuff</a>
--------------------------------------------------------------------------------
<a href=\"https://www.customer.com\" title=\"customer" 
    data-type="custom">Customer stuff</a>
<a href=\"https://www.customer.com\" title=\"customer" 
    data-type="custom">Customer stuff</a>
--------------------------------------------------------------------------------
<a href="#">customize it!</a>
<a href="#">customize it!</a>