未捕获的异常'DOMException',错误消息为'未找到错误'。

4
基本上,我正在为我的CMS编写模板系统,并且希望采用模块化结构,其中涉及人们放置标签,如:<module name="news" /><include name="anotherTemplateFile" />,然后我想在php中查找它们并用动态html替换它们。
这里有人指向了我DOMDocument,但我已经遇到了一个问题。
我试图在模板中查找所有的<include />标签,并将它们替换为一些简单的html。以下是我的模板代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>CMS</title>
<include name="head" />
</head>

<body>

<include name="header" />

<include name="content" />

<include name="footer" />

</body>
</html>

以下是我的PHP代码:

$template = new DOMDocument();
$template->load("template/template.tpl");

foreach( $template->getElementsByTagName("include") as $include ) {
    $element = '<input type="text" value="'.print_r($include, true).'" />';
    $output = $template->createTextNode($element);
    $template->replaceChild($output, $include);
}

echo $template->saveHTML();

现在,我遇到了致命错误Uncaught exception 'DOMException' with message 'Not Found Error'
我查了一下,似乎是因为我的<include />标签不一定是$template的直接子元素,所以没有替换它们。
如何独立地替换它们?
谢谢。
汤姆
编辑:
基本上,我有一个点子。如果我对我的PHP做这样的事情,我看到它正在尝试做我想要的事情:
$template = new DOMDocument();
    $template->load("template/template.tpl");

    foreach( $template->getElementsByTagName("include") as $include ) {
        $element = '<input type="text" value="'.print_r($include, true).'" />';
        $output = $template->createTextNode($element);
        // this line is different:
        $include->parentNode->replaceChild($output, $include);
    }

    echo $template->saveHTML();

然而,它似乎只更改了我HTML中中的1个出现...当我有3个:/

请尝试使用http://simplehtmldom.sourceforge.net/代替DOMDocument。 - yoda
1
@yoda你建议他使用一把钝黄油刀而不是手术刀。 - Gordon
2
simplehtmldom消耗内存的速度让我见所未见。 - Liam Bailey
@Thomas你的代码不会起作用,因为createTextNode()会转义任何尖括号。请改用appendXML() - Gordon
谢谢 Gordon。:) 不然我会在那个障碍上跌倒。但现在的问题是根本没有替换它们... - Thomas Clayson
显示剩余2条评论
2个回答

3
这是一个与您的DOMDocument->load有关的问题,请尝试:
$template->loadHTMLFile("template/template.tpl"); 

但是您可能需要将其命名为.html扩展名。

这是在查找html或xml文件。此外,每当您使用DOMDocument处理html时,在调用load之前使用libxml_use_internal_errors(true);是一个好主意。

好的,这很有效:

foreach( $template->getElementsByTagName("include") as $include ) {
     if ($include->hasAttributes()) {
     $includes[] = $include;
     }
     //var_dump($includes);
 }
 foreach ($includes as $include) {
            $include_name = $include->getAttribute("name");
        $input = $template->createElement('input');
$type = $template->createAttribute('type');
$typeval = $template->createTextNode('text');
$type->appendChild($typeval);
$input->appendChild($type);
$name = $template->createAttribute('name');
$nameval = $template->createTextNode('the_name');
$name->appendChild($nameval);
$input->appendChild($name);
$value = $template->createAttribute('value');
$valueval = $template->createTextNode($include_name);
$value->appendChild($valueval);
$input->appendChild($value);
if ($include->getAttribute("name") == "head") {
       $template->getElementsByTagName('head')->item(0)->replaceChild($input,$include);
}
else {
        $template->getElementsByTagName("body")->item(0)->replaceChild($input,$include);
}
        }
        //$template->load($nht);
        echo $template->saveHTML();

啊,所有这些想法都很好,谢谢,但是它们都不起作用。如果您能看一下我的更新后的原始问题,那就太好了。基本上,我使用了$include->parentNode->replaceChild而不是$template->replaceChild,它可以工作,但只更改搜索的一个实例。有什么想法吗? - Thomas Clayson
@Thomas,我已经发布了一个可行的更新,但我也同意Bobince的观点,有更好的方法来实现你想要达到的目标。 - Liam Bailey

1

然而,它似乎只能更改我的HTML中的1个出现次数...当我有3个时。

DOM NodeLists是‘实时的’:当您从文档中删除(替换)<include>元素时,它将从列表中消失。反之,如果在文档中添加新的<include>,它将出现在列表中。

您可能希望对来自元素的childNodes的NodeList执行此操作,但是同样适用于返回getElementsByTagName的NodeList。这是W3C DOM标准的一部分,在Web浏览器的DOM和PHP的DOMDocument中也会发生。

因此,您在此处拥有的是破坏性迭代。删除第一个<include>(列表中的项目0),然后第二个<include>,以前的项目1,成为新的项目0。现在,当您转到列表中的下一个项目时,项目1就是以前的项目2,导致您只查看了一半的项目。

PHP的foreach循环看起来可能会保护你免受此类问题的困扰,但实际上,在底层它与传统的索引for循环完全相同。

我建议避免为PHP创建新的模板语言;已经有很多了,更不用说PHP本身了。使用DOMDocument创建一个模板语言也会特别慢。

ETA:通常情况下,正则表达式替换会更快,假设匹配模式简单且不会引入大量回溯。但是,如果您坚持使用XML语法,则正则表达式在解析方面并不是很好。但是,您要尝试做什么,这不能通过PHP完成吗?

<?php function write_header() { ?>
    <p>This is the header bit!</p>
<? } ?>

<body>
    ...
    <?php write_header(); ?>
    ...
</body>

@thomas-clayson 我同意,虽然我下面更新的答案是可行的,但如果你在DomDocument中有这样的问题,需要编写的代码量会很快变得荒谬。如果我必须自己做模板,我会使用"SECTION_NAME",然后 $template = str_replace("SECTION_NAME",$replacement,$template);。 - Liam Bailey
嗨,Bobince,我并不是真的想要创建一个模板语言,只是想要一种快速简便的方法来制作网站,而不需要每次都进行大量的代码更改!Liam,preg_replace_callback 比使用 DOM 文档更快吗? - Thomas Clayson
我正在尝试完全将PHP与HTML分离。就像纯MVC一样。我不想为了添加或删除结构而必须搜索代码。我们将创建的一些“模块”将非常复杂,我宁愿将它们完全从布局/HTML中移除,使它们成为自包含的。 - Thomas Clayson
@thomas,使用Bobince的方法(我已经使用过很多次)仍然可以实现这一点,只要将php放在单独的文件中,即modules.php或functions.php。无论如何,如果你坚持这样做,你是否已经让我下面的方法工作了? - Liam Bailey
哦,是的,抱歉 - 从您的示例中获得的解决方案是遍历节点列表中的每个“item”。 :) 所以我会将您的答案标记为正确。不过,考虑到这种方式似乎比使用简单的preg_replace更困难,我已经决定采用另一种方法了。 :) 虽然如此,非常感谢您的所有建议,我学到了很多。 :) - Thomas Clayson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接