PHP DomDocument HTML манипуляция


У меня есть следующий HTML.

<div id="container">
    <div id="current">Current Div</div>
</div>

Используя DomDocument в PHP, я пытаюсь добавить дополнительный div в HTML перед div с идентификатором "current".

<div id="container">
    <div id="new">New Div</div>
    <div id="current">Current Div</div>
</div>

Когда я использую следующий код, он, по-видимому, добавляет div внутри div с идентификатором "current", но перед содержимым этого div.Может ли кто-нибудь сказать мне, почему это так и как я могу получить результаты, подобные HTML выше? (Смотрите вопрос HTML ниже)

Текущий PHP

$doc = new DOMDocument();
$doc->formatOutput = true;

$doc->loadHTMLFile('index.html');

$head = $doc->getElementById('current');
$base = $doc->createElement('div', 'New Div');
$base->setAttribute('id', 'new');
echo $doc->saveHTML();

HTML Выпуск

<div id="container">
    <div id="current">
        <div id="new">New Div</div>
        Current Div
    </div>
</div>

Редактировать:

PHP

$doc = new DOMDocument();
$doc->formatOutput = true;

$doc->loadHTMLFile('index.html');
$container = $doc->getElementById('container');
$current = $doc->getElementById('username');
$new = $doc->createElement('div', 'New Div');

$new->setAttribute('id', 'new');
$container->insertBefore($new, $current);
echo $doc->saveHTML();

HTML

<div id="container">
    <form method="post" action="">
        <input type="text" name="username" id="username" />
    </form>
</div>

Ошибка:

Fatal error: Uncaught exception 'DOMException' with message 'Not Found Error' 
in index.php:55 Stack trace: #0 index.php(55): 
DOMNode->insertBefore(Object(DOMElement), Object(DOMElement))
1 3

1 ответ:

Вы можете использовать DOMNode::insertBefore():

<?php

$doc = new DOMDocument();
$doc->formatOutput = true;
$doc->loadHTMLFile('index.html');

$container = $doc->getElementById('container');
$current = $doc->getElementById('current');
$new = $doc->createElement('div', 'New Div');

$new->setAttribute('id', 'new');
$container->insertBefore($new, $current);

var_dump($doc->saveHTML());

Это дает:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
    <body>
        <div id="container">
            <div id="new">New Div</div>
            <div id="current">Current Div</div>
        </div>
    </body>
</html>

Надеюсь, это поможет :)

EDIT

Ссылаясь на вашу сообщенную проблему... Я вижу проблему: input#username не является непосредственным потомком div#container, это непосредственный ребенок form. Таким образом, вам нужно получить элемент form как объект DOMNode, а не его родитель div#container. Самый простой способ сделать это-добавить идентификатор к form и сделать что-то вроде этого:

$form = $doc->getElementById('form');
$username = $doc->getElementById('username');
$username->setAttribute('value', 'Enter username!');

var_dump($doc->saveHTML());

Это основано на следующем: HTML. Обратите внимание, что элемент form имеет идентификатор:

<div id="container">
    <form id="form" method="post" action="">
        <input type="text" name="username" id="username">
    </form>
</div>

Если по какой-то причине вы не можете добавить идентификатор к элементу формы, вы можете перейти из div#container следующим образом:

// find div#container element
$container = $doc->getElementBy('id');
// find all form elements that are children of div#container
$forms = $container->getElementsByTagName('form');

if (0 !== $forms->length) {
   $form = $forms->item(0);
}

// etc.

Править #2

Я почти забыл XPath... Если вы хотите получить более лаконичный / авантюрный вы можете использовать DOMXPath::query() чтобы найти узлы DOM:

$xpath = new DOMXPath($doc);
$form  = $xpath->query('body/div[@id="container"]/form')->item(0);
$input = $xpath->query('body//input[@id="username"]')->item(0);

Синтаксис запроса XPath довольно загадочный, но мощный, я не могу сказать, что я эксперт в любом случае, поэтому я не могу говорить за эффективность этих запросы. Кроме того, вы можете добавить проверку ошибок-метод query возвращает DOMNodeList, который имеет свойство length.

Наконец, стоит отметить, что DOMDocument украшает ваш HTML-фрагмент тегами DOCTYPE и html и body; Именно поэтому запрос XPath проходит от body.