简体   繁体   中英

Uncaught exception 'DOMException' with message 'Not Found Error'

Bascially I'm writing a templating system for my CMS and I want to have a modular structure which involves people putting in tags like:

<module name="news" /> or <include name="anotherTemplateFile" /> which I then want to find in my php and replace with dynamic html.

Someone on here pointed me towards DOMDocument, but I've already come across a problem.

I'm trying to find all <include /> tags in my template and replace them with some simple html. Here is my template code:

<!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>

And here is my 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();

Now, I get the fatal error Uncaught exception 'DOMException' with message 'Not Found Error' .

I've looked this up and it seems to be that because my <include /> tags aren't necessarily DIRECT children of $template its not replacing them.

How can I replace them independently of descent?

Thank you

Tom

EDIT

Basically I had a brainwave of sorts. If I do something like this for my PHP I see its trying to do what I want it to do:

$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();

However it only seems to change 1 occurence in the <body> of my HTML... when I have 3. :/

This is a problem with your DOMDocument->load, try

$template->loadHTMLFile("template/template.tpl"); 

But you may need to give it a .html extension.

this is looking for a html or an xml file. also, whenever you are using DOMDocument with html it is a good idea to use libxml_use_internal_errors(true); before the load call.

OKAY THIS WORKS:

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();

However it only seems to change 1 occurence in the of my HTML... when I have 3. :/

DOM NodeLists are 'live': when you remove an <include> element from the document (by replacing it), it disappears from the list. Conversely if you add a new <include> into the document, it will appear in your list.

You might expect this for a NodeList that comes from an element's childNodes , but the same is true of NodeLists that are returned getElementsByTagName . It's part of the W3C DOM standard and occurs in web browsers' DOMs as well as PHP's DOMDocument.

So what you have here is a destructive iteration. Remove the first <include> (item 0 in the list) and the second <include> , previously item 1, become the new item 0. Now when you move on to the next item in the list, item 1 is what used to be item 2, causing you to only look at half the items.

PHP's foreach loop looks like it might protect you from that, but actually under the covers it's doing exactly the same as a traditional indexed for loop.

I'd try to avoid creating a new templating language for PHP; there are already so many, not to mention PHP itself. Creating one out of DOMDocument is also going to be especially slow.

eta: In general regex replace would be faster, assuming a simple match pattern that doesn't introduce loads of backtracking. However if you are wedded to an XML syntax, regex isn't very good at parsing that. But what are you attempting to do, that can't already be done with PHP?

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM