Я пишу скрипт для очистки так называемого HTML-документа, который MS Word создает, когда вы сохраняете как «Web Page, Filtered». Я хочу, чтобы полученный документ был действительным XHTML1.
Первое, что я хочу сделать, это изменить! DOCTYPE, чтобы он был XHTML 1.0 Strict
вместо ...4.0 Transitional
,
Я написал код, который выглядел так, как будто он должен работать, но когда я запускаю его, я получаю Segmentation fault
из PHP. Сначала я думал, что это происходит в save
функция, но после добавления некоторых echo
Заявления для отладки Теперь я думаю, что проблема в местах, отмеченных {{{1}}}
а также {{{2}}}
в коде (ниже).
Вот что я думаю: {{{1}}}
Я перебираю DOMNodeList
, обрабатывая его так, как если бы это был обычный массив, с которым я могу пройти foreach
,
Но в {{{2}}} я меняю список подузлов родителя. Я подозреваю, что это ломает мою foreach
: либо DOMNodeList
или мой foreach
указатель становится недействительным.
Итак, каков «правильный» способ внести изменения в дерево DOM, пока вы обойдете его? Я придумал два возможных варианта:
Скопируйте DOMNodeList в обычный массив:
$ nodelist = [];
foreach ($ node-> childNodes как $ subnode) {
$ nodelist [] = $ subnode;
// Или, возможно, объект, который содержит соответствующий код и параметры для изменения, которое я хочу сделать
}
foreach ($ nodelist как $ subnode) {
// сделать соответствующее изменение
}
Пройдите по дереву DOM, но не вносите никаких изменений. Вместо этого создайте массив всех мест, где я хочу внести изменения. Когда закончите, просмотрите этот массив и внесите изменения.
Может быть, есть какой-то «официальный» способ сделать это ????
Соответствующие части моего кода ниже:
<?
$dom = new DOMDocument();
$dom->loadHTMLFile($htmFName);
$trav = new DOMTraverser($dom);
$storyParms = new StoryParams("some string");
$callback = new StoryDocCallback($htmFName);
$trav->traverse($callback, $storyParms);
$dom->save("y");
class DOMTraverser
{
private $docNode;
private $callback;
private $param;
public function __construct(DOMNode $node)
{
$this->docNode = $node;
}
public function traverse(GeneralCallBack $cb, $param)
{
$this->callback = $cb;
$this->param = $param;
$this->traverseNode($this->docNode);
}
public function traverseNode($node)
{
$this->callback->callBefore($node, $this->param);
if ($node->hasChildNodes()) {
{{1}} foreach ($node->childNodes as $subnode) {
if($subnode != null) {
$this->traverseNode($subnode);
}
}
}
}
}
class StoryDocCallback implements GeneralCallback
{
public function callbefore($node, $param)
{
$name = $node->nodeName;
if (is_a($node, "DOMDocumentType")) {
$this->repairDocType($node);
return;
}
...
}
protected function repairDocType(DOMNode $node)
{
$impl = new DomImplementation();
$rootName = "html";
$pubID = "-//W3C//DTD XHTML 1.0 Strict//EN";
$sysID = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";
$newDocType = $impl->createDocumentType($rootName, $pubID, $sysID);
$parent = $node->parentNode;
{{2}} $rc = $parent->replaceChild($newDocType, $node);
assert($rc != false);
}
...
}
Задача ещё не решена.
Других решений пока нет …