2010-06-30 23 views
6

Sto provando a utilizzare la classe php simple_html_dom per creare una funzione di ricerca e sostituzione che cerca le parole chiave e le sostituisce con un collegamento a una definizione della parola chiave , con la parola chiave come testo del collegamento.trovare e sostituire le parole chiave da collegamenti ipertestuali in un frammento html, tramite php dom

Come posso trovare e sostituire "Dexia" con <a href="info.php?tag=dexia">Dexia</a> utilizzando questa classe, all'interno di una stringa come <div><p>The CEO of the Dexia bank has just decided to retire.</p></div>?

+0

Dovete usare simple_html_dom? Questo sembra qualcosa che potrebbe essere realizzato con regex usando preg_replace. –

+0

@threendib HTML non è regolare. – Gordon

risposta

5

che è un po 'complicato, ma si poteva fare in questo modo:

$html = <<< HTML 
<div><p>The CEO of the Dexia bank <em>has</em> just decided to retire.</p></div> 
HTML; 

Ho aggiunto un elemento enfasi solo per illustrare che funziona con elementi in linea troppo.

Setup

$dom = new DOMDocument; 
$dom->formatOutput = TRUE; 
$dom->loadXML($html); 
$xpath = new DOMXPath($dom); 
$nodes = $xpath->query('//text()[contains(., "Dexia")]'); 

La cosa interessante sopra è il XPath naturalmente. Interroga il DOM caricato per tutti i nodi DOMText contenenti l'ago "Dexia". Il risultato è DOMNodeList (come al solito).

La sostituzione

foreach($nodes as $node) { 
    $link  = '<a href="info.php?tag=dexia">Dexia</a>'; 
    $replaced = str_replace('Dexia', $link, $node->wholeText); 
    $newNode = $dom->createDocumentFragment(); 
    $newNode->appendXML($replaced); 
    $node->parentNode->replaceChild($newNode, $node); 
} 
echo $dom->saveXML($dom->documentElement); 

Il trovato $node conterrà la stringa Il CEO della banca Dexia per wholeText, nonostante sia all'interno dell'elemento P. Questo perché $node ha un fratello DOMElement con l'enfasi dopo banca. Sto creando il collegamento come una stringa invece di un nodo e sostituisco tutte le occorrenze di "Dexia" (indipendentemente dal limite della parola - che sarebbe una buona chiamata per Regex) nello stesso wholeText. Quindi creo un DocumentFragment dalla stringa risultante e sostituisco il nodo DOMText con esso.

W3C vs PHP

Utilizzando DocumentFragement::applyXML() è un approccio non standard, poiché il metodo non è parte del W3C DOM Spec.

Se si desidera eseguire la sostituzione con l'API standard, è necessario creare prima l'elemento A come nuovo DOMElement. Quindi dovresti trovare l'offset di "Dexia" nello nodeValue dello DOMText e dividere il nodo DOMText in due nodi in quella posizione. Rimuovi Dexia dal fratello restituito e inserisci l'elemento Link, prima del secondo. Ripeti questa procedura con il nodo fratello finché non vengono trovate più stringhe di Dexia nel nodo. Ecco come farlo per un'occorrenza di Dexia:

foreach($nodes as $node) { 
    $link = $dom->createElement('a', 'Dexia'); 
    $link->setAttribute('href', 'info.php?tag=dexia'); 
    $offset = strpos($node->nodeValue, 'Dexia'); 
    $newNode = $node->splitText($offset); 
    $newNode->deleteData(0, strlen('Dexia')); 
    $node->parentNode->insertBefore($link, $newNode); 
} 

E infine l'uscita

<div> 
    <p>The CEO of the <a href="info.php?tag=dexia">Dexia</a> bank <em>has</em> just decided to retire.</p> 
</div> 
+0

@gordon: è molto interessante. Ho semplificato la mia domanda come mi aspettavo qualcosa di più semplice; in realtà ho circa 500 parole chiave. Questo approccio si adatta correttamente? Dovrei avere un doppio ciclo foreach credo. – pixeline

+0

@pixeline beh, è ​​complicato all'inizio ma solo perché DOM è piuttosto prolisso e devi pensare nei nodi anziché nel testo. Capisco perché la gente vuole farlo con Regex, ma non è poi così difficile una volta che ci sei entrato. Voglio dire, sono solo 15 righe di codice. Inseriscilo in una corretta classe di servizio e avrai uno strumento riutilizzabile. Non so come si comporti, quindi dovresti confrontarti tu stesso e vedere se è accettabile per te. – Gordon

+1

Funziona abbastanza bene (ho dovuto cambiare per caricare HTML perché l'html che sto passando proviene da un CMS e potrebbe non essere ben formato.) – pixeline

Problemi correlati