使用DomDocument将所有标头标签替换为h4标签

时间:2013-04-30 19:01:29

标签: php dom replace tags document

我已经将DomDocument用于GetElementById。它选择了一个div。我需要用h4标签替换该div中的所有标头标签。

1 个答案:

答案 0 :(得分:8)

您在问题中没有明确说明您遇到的具体问题。我认为有两个部分可能会引起一些问题。

第一个是如何获取要重命名的所有元素,第二个实际上是如何重命名元素。

获取DOMDocument的标题元素

首先要做的事情是:要选择所有标题元素,您需要选择Heading elements (h1 to h6)的所有标记。结合条件,他们也需要是具有特定id属性的div标签的子项,这似乎是一件相当复杂的事情。但是对于xpath查询,它仍然只是直截了当。

我的代码示例的示例我选择了id“”content“,以下xpath表达式查询所有标题元素:

(
    //div[@id="content"]//h1
    |//div[@id="content"]//h2
    |//div[@id="content"]//h3
    |//div[@id="content"]//h4
    |//div[@id="content"]//h5
    |//div[@id="content"]//h6
)

如果我在这里运行此网站(在我回答之前),它会创建以下标签列表:

Found 8 elements:
 #00: <h1>
 #01: <h2>
 #02: <h2>
 #03: <h3>
 #04: <h3>
 #05: <h3>
 #06: <h2>
 #07: <h4>

这很好地说明了,使用xpath查询甚至可以创建不同元素的列表,并且具有特定条件,例如可以创建具有id的div的子元素。这段代码一目了然:

$url = 'http://stackoverflow.com/questions/16307103/use-domdocument-to-replace-all-header-tags-with-the-h4-tags';

$dom = new DOMDocument();
$internalErrorsState = libxml_use_internal_errors(true);
$dom->loadHTMLFile($url);
libxml_use_internal_errors($internalErrorsState);
$xpath = new DOMXPath($dom);

$expression = '
(
    //div[@id="content"]//h1
    |//div[@id="content"]//h2
    |//div[@id="content"]//h3
    |//div[@id="content"]//h4
    |//div[@id="content"]//h5
    |//div[@id="content"]//h6
)';

$elements = $xpath->query($expression);
echo "Found ", $elements->length, " elements:\n";
foreach ($elements as $index => $element) {
    printf(" #%02d: <%s>\n", $index, $element->tagName);
}

重命名DOMElement

那么关于重命名元素的第二个问题呢?

开箱即用的DOMDocumet不支持此功能。有一个方法存根(DOMDocument::renameNode();在当前的PHP手册中没有记录),但如果你调用它,你会得到一个警告:它没有实现:

  

警告:DOMDocument :: renameNode():尚未实现

相反,人们需要推出自己的版本。这就是它的工作原理:由于你不能用DOMDocument重命名一个元素,你所能做的就是创建一个带有重命名名称的新元素,并复制节点以将其所有属性和子元素重命名为它,然后将其替换为重命名浅拷贝。这可以通过以下方法完成:

/**
 * Renames a node in a DOM Document.
 *
 * @param DOMElement $node
 * @param string     $name
 *
 * @return DOMNode
 */
function dom_rename_element(DOMElement $node, $name) {
    $renamed = $node->ownerDocument->createElement($name);

    foreach ($node->attributes as $attribute) {
        $renamed->setAttribute($attribute->nodeName, $attribute->nodeValue);
    }

    while ($node->firstChild) {
        $renamed->appendChild($node->firstChild);
    }

    return $node->parentNode->replaceChild($renamed, $node);
}

将其与上面的foreach循环结合在一起,在输出标记名称旁边,它们也可以重命名:

$elements = $xpath->query($expression);
echo "Found ", $elements->length, " elements:\n";
foreach ($elements as $index => $element) {
    printf(" #%02d: <%s>\n", $index, $element->tagName);
    dom_rename_element($element, 'h4');
    ###################################
}

然后再次查询xpath表达式,只会产生h4标签:

$elements = $xpath->query($expression);
echo "Found ", $elements->length, " elements:\n";
foreach ($elements as $index => $element) {
    printf(" #%02d: <%s>\n", $index, $element->tagName);
}

输出:

Found 8 elements:
 #00: <h1>
 #01: <h2>
 #02: <h2>
 #03: <h3>
 #04: <h3>
 #05: <h3>
 #06: <h2>
 #07: <h4>

完整代码示例

这里完整的代码示例及其输出一目了然:

<?php
/**
 * Use DomDocument to replace all header tags with the h4 tags
 * @link http://stackoverflow.com/q/16307103/367456
 */
$url = 'http://stackoverflow.com/questions/16307103/use-domdocument-to-replace-all-header-tags-with-the-h4-tags';

$dom = new DOMDocument();
$internalErrorsState = libxml_use_internal_errors(true);
$dom->loadHTMLFile($url);
libxml_use_internal_errors($internalErrorsState);
$xpath = new DOMXPath($dom);

$expression = '
(
    //div[@id="content"]//h1
    |//div[@id="content"]//h2
    |//div[@id="content"]//h3
    |//div[@id="content"]//h4
    |//div[@id="content"]//h5
    |//div[@id="content"]//h6
)';

$elements = $xpath->query($expression);
echo "Found ", $elements->length, " elements:\n";
foreach ($elements as $index => $element) {
    printf(" #%02d: <%s>\n", $index, $element->tagName);
    dom_rename_element($element, 'h4');
}

$elements = $xpath->query($expression);
echo "Found ", $elements->length, " elements:\n";
foreach ($elements as $index => $element) {
    printf(" #%02d: <%s>\n", $index, $element->tagName);
}

/**
 * Renames a node in a DOM Document.
 *
 * @param DOMElement $node
 * @param string     $name
 *
 * @return DOMNode
 */
function dom_rename_element(DOMElement $node, $name) {
    $renamed = $node->ownerDocument->createElement($name);

    foreach ($node->attributes as $attribute) {
        $renamed->setAttribute($attribute->nodeName, $attribute->nodeValue);
    }

    while ($node->firstChild) {
        $renamed->appendChild($node->firstChild);
    }

    return $node->parentNode->replaceChild($renamed, $node);
}

如果您尝试一下,您可能会注意到,在我的回答之后,标题元素的数量已经改变。我希望这有用!