[英]How to combine PHP's DOMDocument with a JavaScript template
我在这里有一个奇怪的问题,但这完全使我难过。 无论如何,这是因为我找不到想搜索的正确术语,所以这个问题很可能在StackOverflow上的某个地方得到了回答,但我找不到。
我们有一个校对系统,可让我们截取页面并对其进行注释。 我们可以将页面发送给客户,他们可以在发送回信之前在页面上做笔记。 在大多数情况下,这可以正常工作。 当我们尝试使用类似于Handlebars的JavaScript模板系统时,就会出现问题。 我们倾向于在页面上有如下脚本模板:
<script type="client/template" id="foo-div">
<div>#foo#</div>
</script>
我们可以在脚本中使用它在模板中生成标记,用正确的数据替换#foo#
。
当我们尝试将其放入我们的打样系统时,问题就来了。 因为我们需要抓取页面以便可以在域中进行渲染,所以我们使用PHP的DOMDocument
来解析HTML,以便我们可以轻松地对其进行修改(将target="_blank"
到外部链接等中)。 当我们尝试通过DOMDocument
运行模板时,它会奇怪地解析它(可能将其视为无效的XML),从而导致页面出现问题。 为了更好地说明这一点,这是PHP中的示例:
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
$html = '<!DOCTYPE html>'.
'<html>'.
'<head></head>'.
'<body>'.
'<script type="client/template" id="foo-div"><div>#foo#</div></script>'.
'</body>'.
'</html>';
$dom = new DOMDocument();
libxml_use_internal_errors(true);
try {
$html = $dom->loadHTML($html);
} catch (Exception $e) {
throw new Exception('Invalid HTML on the page has caused a parsing error');
}
if ($html === false) {
throw new Exception('Unable to properly parse page');
}
$dom->preserveWhiteSpace = false;
$dom->formatOutput = false;
echo $dom->saveHTML();
该脚本产生的代码类似于下面的HTML,并且似乎不会引发任何异常。
<!DOCTYPE html>
<html>
<head></head>
<body><script type="client/template" id="foo-div"><div>#foo#</script></body>
</html>
我的问题是:有人知道我可以使用PHP的DOMDocument
留下模板script
标记的方法吗? 我是否可以使用设置或插件使DOMDocument
看到具有type
属性的纯文本type
的script
标签的内容,就像浏览器一样?
编辑
我最终选择了Alf Eaton的解决方案,或者将字符串解析为XML。 但是,并非所有HTML标记都是自动关闭的,这会导致问题。 如果有人遇到相同问题,我将在此处发布完整的解决方案:
/**
* Inserts a new string into an old string at the specified position.
*
* @param string $old_string Old string to modify.
* @param string $new_string New string to insert.
* @param int $position Position at which the new string should be inserted.
* @return string Old string with new string inserted.
* @see http://stackoverflow.com/questions/8251426/insert-string-at-specified-position
*/
function str_insert($old_string, $new_string, $position) {
return substr($old_string, 0, $position) . $new_string .
substr($old_string, $position);
}
/**
* Inspects a string of HTML and closes any tags that need self-closing in order
* to make the HTML valid XML.
*
* @param string $html Raw HTML (potentially invalid XML)
* @return string Original HTML with self-closing slashes added.
*/
function self_close($html) {
$fixed = $html;
$tags = array('area', 'base', 'basefont', 'br', 'col', 'frame',
'hr', 'img', 'input', 'link', 'meta', 'param');
foreach ($tags as $tag) {
$offset = 0;
while (($offset = strpos($fixed, '<' . $tag, $offset)) !== false) {
if (($close = strpos($fixed, '>', $offset)) !== false &&
$fixed[$close - 1] !== '/') {
$fixed = str_insert($fixed, '/', $close);
}
$offset += 1; // Prevent infinite loops
}
}
return $fixed;
}
// When parsing the original string:
$html = $dom->loadXML(self_close($html));
如果输入文档是有效的XML,则将其解析为XML而不是HTML将保留<script>
标记的内容:
<?php
$html = <<<END
<!DOCTYPE html>
<html><body>
<script type="client/template" id="foo-div"><div>#foo#</div></script>
</body></html>
END;
$doc = new DOMDocument();
$doc->preserveWhiteSpace = true; // needs to be before loading, to have any effect
$doc->loadXML($html);
$doc->formatOutput = false;
print $doc->saveHTML();
// <!DOCTYPE html>
// <html><body>
// <script type="client/template" id="foo-div"><div>#foo#</div></script>
// </body></html>
当PHP的DOMDocument解析HTML时,它将使用一些故障保护技术。
对于脚本标签,有两个。
首先是特殊的cript-tag内容处理-因为<script>
标签不能包含任何其他标签,其内部的所有内容均假定为文本。
第二种技术是整个html tag-autoclose hack。 当解析器找到节奏错误的结束标记时,它会尝试查找最接近的父项开始标记,并自动关闭此找到的开始标记和放置错误的结束标记之间的每个标记。 如果解析器找不到正确的打开标记,它将忽略关闭标记。
如果您尝试解析这样的代码,则可以看到此<body><div><script type="client/template" id="foo-div"><div>#foo#</div>dfdf</script></div></body>
-您将获得<body><div><script type="client/template" id="foo-div"><div>#foo#</script></div>dfdf</body>
移出您的脚本。
没有正常的方法可以使DOMDocument以所需的方式解析html5。
但是您可以使用简单的技巧-只需用<
<
或正则表达式在script标记内只是任何其他未使用的符号。 在处理之后,您可以通过相同的步骤将其取回。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.