如何使用 PHP 来注释带有 HTML 的字符串(即如何通过保持有效 HTML 的偏移量将 HTML 标记插入字符串)?
How to use PHP to annotate an string with HTML (i.e How. insert HTML tags to an string by offsets mantaining a valid HTML)?
我正在尝试在字符串中的单词之间添加 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)
);
所以要用下面的 HTML 标签 ($tag) 注释下面的 HTML 文本 ($source)。这是包装由 $annotationPositions 数组分隔的字符(不考虑源的 HTML 标签)。
$source="<div>This is</div> only a test for Whosebug";
$tag="<span class='annotation n-$cont'>";
结果应该如下 (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 Whosebug"
如何编写下一个功能:
$cont=0;
$myAnnotationClass="placesOfTheWorld";
for ($annotationCharactersPositions as $position) {
$tag="<span class='annotation $myAnnotationClass'>";
$source=addHTMLtoString($source,$tag,$position);
$cont++;
}
考虑到 在计算 $annotationCharactersPositions 数组 中描述的字符以及每次插入对于以下注释的 encapsulation/annotation,必须考虑 $source 文本中的注释(即 $tag)。
整个过程的想法是给定一个 输入 文本( 可能包含也可能不包含 HTML 标签 ) 一组字符将被注释(属于一个或几个单词)以便 结果将具有选定的字符 (通过定义每个注释开始和结束位置的数组) 由 HTML 标签包装,标签可以变化 (a、跨度、标记),具有可变数量的 html 属性(名称、class、id、数据- *).此外 结果必须是格式正确的有效 HTML 文档,这样如果任何注释位于多个注释之间, html 应该相应地写入输出.
你知道有什么库或解决方案可以做到这一点吗?也许 PHP DOMDocument 功能很有用?但是如何将偏移量应用于 php DomDocument 功能?任何想法或帮助都很受欢迎。
注意 1:输入文本是 UTF-8 原始文本,嵌入了任何类型的 HTML 实体 (0-n)。
注释 2:输入标签可以是任意 HTML 标签,具有可变数量的属性 (0-n)。
注3:起始位置必须包含,结束位置必须不包含。即 1º 注释从第 2 个字符(包括第 2 个字符 'i')开始到第 6 个字符(不包括第 6 个字符 's')
之前结束
将 HTML 加载到 DOM 文档后,您可以获取可迭代列表中具有 Xpath 表达式 (.//text()
) 的元素节点的任何文本节点后代。这允许您跟踪当前文本节点之前的字符。在文本节点上,您检查文本内容(或其一部分)是否必须包装到注释标记中。如果是这样,请将其分开并创建一个最多包含 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 Whosebug</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();
输出:
<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 Whosebug</div>
我正在尝试在字符串中的单词之间添加 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)
);
所以要用下面的 HTML 标签 ($tag) 注释下面的 HTML 文本 ($source)。这是包装由 $annotationPositions 数组分隔的字符(不考虑源的 HTML 标签)。
$source="<div>This is</div> only a test for Whosebug";
$tag="<span class='annotation n-$cont'>";
结果应该如下 (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 Whosebug"
如何编写下一个功能:
$cont=0;
$myAnnotationClass="placesOfTheWorld";
for ($annotationCharactersPositions as $position) {
$tag="<span class='annotation $myAnnotationClass'>";
$source=addHTMLtoString($source,$tag,$position);
$cont++;
}
考虑到 在计算 $annotationCharactersPositions 数组 中描述的字符以及每次插入对于以下注释的 encapsulation/annotation,必须考虑 $source 文本中的注释(即 $tag)。
整个过程的想法是给定一个 输入 文本( 可能包含也可能不包含 HTML 标签 ) 一组字符将被注释(属于一个或几个单词)以便 结果将具有选定的字符 (通过定义每个注释开始和结束位置的数组) 由 HTML 标签包装,标签可以变化 (a、跨度、标记),具有可变数量的 html 属性(名称、class、id、数据- *).此外 结果必须是格式正确的有效 HTML 文档,这样如果任何注释位于多个注释之间, html 应该相应地写入输出.
你知道有什么库或解决方案可以做到这一点吗?也许 PHP DOMDocument 功能很有用?但是如何将偏移量应用于 php DomDocument 功能?任何想法或帮助都很受欢迎。
注意 1:输入文本是 UTF-8 原始文本,嵌入了任何类型的 HTML 实体 (0-n)。
注释 2:输入标签可以是任意 HTML 标签,具有可变数量的属性 (0-n)。
注3:起始位置必须包含,结束位置必须不包含。即 1º 注释从第 2 个字符(包括第 2 个字符 'i')开始到第 6 个字符(不包括第 6 个字符 's')
之前结束将 HTML 加载到 DOM 文档后,您可以获取可迭代列表中具有 Xpath 表达式 (.//text()
) 的元素节点的任何文本节点后代。这允许您跟踪当前文本节点之前的字符。在文本节点上,您检查文本内容(或其一部分)是否必须包装到注释标记中。如果是这样,请将其分开并创建一个最多包含 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 Whosebug</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();
输出:
<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 Whosebug</div>