Вы можете сделать это довольно легко с DOMDocument
и DOMXPath
. XPath особенно полезен в вашем случае, потому что вы легко разделяете логику, чтобы выбрать, какие элементы удалять, и способ их удаления.
Прежде всего, нормализуйте ввод. Я не совсем понял, что вы имеете в виду с пустыми пробелами, я подумал, что это могут быть либо пустые текстовые узлы (которые могли быть удалены как preserveWhiteSpace
, равно FALSE
, но я не уверен), либо если их нормализованный пробел пуст. Я выбрал первый (если даже необходимо), в случае, если это другой вариант, я оставил комментарий, что использовать вместо:
$xp = new DOMXPath($dom);
//remove empty textnodes - if necessary at all
// (in case remove WS: [normalize-space()=""])
foreach($xp->query('//text()[""]') as $i => $tn)
{
$tn->parentNode->removeChild($tn);
}
После этой нормализации текстового узла вы не должны сталкиваться с проблемой, о которой вы говорили в одном комментарии здесь.
Следующая часть состоит в том, чтобы найти все элементы, которые имеют то же имя, что и их родительский элемент, и которые являются единственными дочерними элементами. Это может быть снова выражено в xpath. Если такие элементы найдены, все их дочерние элементы перемещаются в родительский элемент, а затем этот элемент также будет удален:
// all child elements with same name as parent element and being
// the only child element.
$r = $xp->query('body//*/child::*[name(.)=name(..) and count(../child::*)=1]');
foreach($r as $i => $dupe)
{
while($dupe->childNodes->length)
{
$child = $dupe->firstChild;
$dupe->removeChild($child);
$dupe->parentNode->appendChild($child);
}
$dupe->parentNode->removeChild($dupe);
}
Полная демонстрация .
Как вы можете видеть в демонстрации, это не зависит от текстовых узлов и комментариев. Если вы этого не хотите, например В реальных текстах выражение для подсчета детей должно распространяться на все типы узлов. Но я не знаю, точно ли это тебе нужно. Если это так, это делает подсчет дочерних элементов для всех типов узлов:
body//*/child::*[name(.)=name(..) and count(../child::node())=1]
Если вы не нормализовали пустые текстовые узлы заранее (удалите пустые), то это слишком строго. Выберите набор инструментов, который вам нужен, я думаю, что нормализация плюс это строгое правило может быть лучшим выбором.