如何使用PHP用HTML注释字符串(即,如何通过偏移量将HTML标记插入字符串以保留有效的HTML)?

Mar*_*tin 7 html php string dom annotate

我正在尝试在字符串中的单词之间添加HTML标签(通过html标签即HTML注释包装单词)。HTML标记应写入的位置由偏移量数组界定,例如:

//array(Start offset, End offset) in characters
//Note that annotation starts in the Start offset number and ends before the End offset number
$annotationCharactersPositions= array(
   0=>array(0,3),
   1=>array(2,6),
   2=>array(8,10)
);
Run Code Online (Sandbox Code Playgroud)

因此,要使用以下HTML标记($ tag)注释以下HTML文本($ source)。包裹由$ annotationPositions数组定界的字符(不考虑源的HTML标记)。

$source="<div>This is</div> only a test for stackoverflow";
$tag="<span class='annotation n-$cont'>";
Run Code Online (Sandbox Code Playgroud)

结果应为以下内容(https://jsfiddle.net/cotg2pn1/):

charPos   =--------------------------------- 01---------------------------- 2-------------------------------------------3------------------------------------------45-------67-----------------------------89-------10,11,12,13......
$output = "<div><span class='annotation n-1'>Th<span class='annotation n-2'>i</span></span><span class='annotation n-2'>s</span><span class='annotation n-2'> i</span>s</div> <span class='annotation n-3'>on</span>ly a test for stackoverflow"
Run Code Online (Sandbox Code Playgroud)

我如何编程下一个功能:

    $cont=0;
    $myAnnotationClass="placesOfTheWorld";
    for ($annotationCharactersPositions as $position) {
         $tag="<span class='annotation $myAnnotationClass'>";             
         $source=addHTMLtoString($source,$tag,$position);
         $cont++;
    }
Run Code Online (Sandbox Code Playgroud)

考虑到在计数$ annotationCharactersPositions数组中描述的字符时,必须不考虑输入字符串的HTML标记,并且必须考虑到 $ source文本中每次插入注释(即$ tag)的情况。以下注释的封装/注释。

整个过程的想法是,给定输入文本(可能包含或不包含HTML标签),将对一组字符进行注释(属于一个或多个单词),以便结果将具有选定的字符(通过数组)定义了每个注释的起始和结束位置),该标记由HTML标记包裹,该标记可以随可变数量的html属性(名称,类,id,data- *)而变化(a,span,mark)。此外,结果必须是格式正确的有效HTML文档,这样,如果在多个注释之间有任何注释,则html应该相应地写入输出中。

您知道执行此操作的任何库或解决方案吗?也许PHP DOMDocument功能有用吗?但是如何将偏移量应用于php DomDocument函数?任何想法或帮助都受到好评。

注1:输入的文本是UTF-8原始文本,其中嵌入了任何类型的HTML实体(0-n)。

注2:输入标签可以是具有可变数量的属性(0-n)的任何HTML标签。

注3:初始位置必须是包容性的,而最终位置必须是排斥性的。即1º注释开始于第二个字符(包括2个字符“ i”)之前,结束于第6个字符(不包括6个字符“ s”)之前

ThW*_*ThW 8

将HTML加载到DOM文档中之后,您可以.//text()在可迭代列表中获取带有Xpath表达式()的元素节点的任何文本节点后代。这使您可以跟踪当前文本节点之前的字符。在文本节点上,检查是否必须将文本内容(或其一部分)包装到注释标记中。如果是这样,将其分离并创建一个最多包含3个节点的片段。(前文字,注释,后文字)。用片段替换文本节点。

function annotate(
  \DOMElement $container, int $start, int $end, string $name
) {
  $document = $container->ownerDocument;
  $xpath = new DOMXpath($document);
  $currentOffset = 0;
  // fetch and iterate all text node descendants 
  $textNodes = $xpath->evaluate('.//text()', $container);
  foreach ($textNodes as $textNode) {
    $text = $textNode->textContent;
    $nodeLength = grapheme_strlen($text);
    $nextOffset = $currentOffset + $nodeLength;
    if ($currentOffset > $end) {
      // after annotation: break
      break;
    }
    if ($start >= $nextOffset) {
      // before annotation: continue
      $currentOffset = $nextOffset;
      continue;
    }
    // make string offsets relative to node start
    $relativeStart = $start - $currentOffset;
    $relativeLength = $end - $start;
    if ($relativeStart < 0) {
      $relativeLength -= $relativeStart;
      $relativeStart = 0;
    }
    $relativeEnd = $relativeStart + $relativeLength;
    // create a fragment for the annotation nodes
    $fragment = $document->createDocumentFragment();
    if ($relativeStart > 0) {
      // append string before annotation as text node
      $fragment->appendChild(
        $document->createTextNode(grapheme_substr($text, 0, $relativeStart))
      );
    }
    // create annotation node, configure and append
    $span = $document->createElement('span');
    $span->setAttribute('class', 'annotation '.$name);
    $span->textContent = grapheme_substr($text, $relativeStart, $relativeLength);
    $fragment->appendChild($span);
    if ($relativeEnd < $nodeLength) {
      // append string after annotation as text node
      $fragment->appendChild(
        $document->createTextNode(grapheme_substr($text, $relativeEnd))
      );
    }
    // replace current text node with new fragment
    $textNode->parentNode->replaceChild($fragment, $textNode);
    $currentOffset = $nextOffset;
  }
}

$html = <<<'HTML'
<div><div>This is</div> only a test for stackoverflow</div>
HTML;

$annotations = [
  0 => [0, 3],
  1 => [2, 6],
  2 => [8, 10]
];

$document = new DOMDocument();
$document->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

foreach ($annotations as $index => $offsets) {
  annotate($document->documentElement, $offsets[0], $offsets[1], 'n-'.$index);
}

echo $document->saveHTML();
Run Code Online (Sandbox Code Playgroud)

输出:

<div><div><span class="annotation n-0">Th<span class="annotation n-1">i</span></span><span class="annotation n-1">s is</span></div> <span class="annotation n-2">on</span>ly a test for stackoverflow</div>
Run Code Online (Sandbox Code Playgroud)