Usando DOMXPath para substituir um nó, mantendo a sua posição
-
22-07-2019 - |
Pergunta
Ok, então eu tive esta pequena idéia pura na outra noite para criar uma classe auxiliar para DomDocument que imita, em certa medida, a capacidade do jQuery para manipular o DOM de um HTML ou string baseada em XML. Em vez de seletores css, XPath é usado. Por exemplo:
$Xml->load($source)
->path('//root/items')
->each(function($Context)
{
echo $Context->nodeValue;
});
Este seria chamar uma função de chamada de retorno em cada nó resultante. Infelizmente, a versão do PHP <5.3.x faz funções lambda não apoio ou encerramento, então eu sou forçado a fazer algo um pouco mais parecido com isso por enquanto:
$Xml->load($source)
->path('//root/items')
->walk('printValue', 'param1', 'param2');
Tudo está funcionando muito bem no momento e acho que esse projeto seria útil para muita gente, mas eu estou preso com uma das funções. Eu estou tentando método 'substituir' imitador do jQuery. Usando o seguinte código, eu posso fazer isso muito facilmente, aplicando o método a seguir:
$Xml->load($source)
->path('//root/items')
->replace($Xml->createElement('foo', 'bar')); // can be an object, string or XPath pattern
O código por trás deste método é:
public function replace($Content)
{
foreach($this->results as $Element)
{
$Element->parentNode->appendChild($Content->cloneNode(true));
$Element->parentNode->removeChild($Element);
}
return $this;
}
Agora, isso funciona. Ele substitui cada elemento resultante com uma versão clonada de US $ conteúdo. O problema é que ele adiciona-los para o fundo da lista do nó pai das crianças. A questão é: como faço para clonar este elemento para substituir outros elementos, mantendo a posição original no DOM?
Eu estava pensando sobre engenharia reversa do nó eu era substituir. Basicamente, copiando sobre os valores, atributos e nome do elemento de US $ conteúdo, mas eu sou incapaz de mudar o nome do elemento real do elemento alvo.
Reflexão poderia ser uma possibilidade, mas tem de haver uma maneira mais fácil de fazer isso.
Anybody?
Solução
Use replaceChild vez de appendChild / removeChild.
Outras dicas
Lookup se $ elemento tem um nextsibbling antes de remover se assim for fazer uma insertBefore que próximo irmão de outra forma simplesmente acréscimo.
public function replace($Content)
{
foreach($this->results as $Element)
{
if ($Element->nextSibling) {
$NextSiblingReference = $Element->nextSibling;
$Element->parentNode->insertBefore($Content->cloneNode(true),$NextSiblingReference);
}
else {
$Element->parentNode->appendChild($Content->cloneNode(true));
}
$Element->parentNode->removeChild($Element);
}
return $this;
}
embora totalmente testado.
Ou, como AnthonyWJones sugeriu replaceChild , grande oomph como eu perder aquele momento:)