2009-04-27 10 views
5

Ich habe eine Funktion, die das href-Attribut von Ankern in einem String durch das DOMDocument von PHP ersetzt. Hier ist ein Ausschnitt:Wie verhindere ich, dass Php's DOMDocument Html Entities kodiert?

$doc  = new DOMDocument('1.0', 'UTF-8'); 
$doc->loadHTML($text); 
$anchors = $doc->getElementsByTagName('a'); 

foreach($anchors as $a) { 
    $a->setAttribute('href', 'http://google.com'); 
} 

return $doc->saveHTML(); 

Das Problem ist, dass loadhtml ($ text) den Text $ in doctype, html, Körper usw. Tags. Ich habe versucht, um diese Arbeit durch diese anstelle von loadhtml tun():

$doc  = new DOMDocument('1.0', 'UTF-8'); 
$node  = $doc->createTextNode($text); 
$doc->appendChild($node); 
... 

Leider ist dies alle Entitäten kodiert (Anker enthalten). Weiß jemand, wie man das abstellt? Ich habe die Dokumente bereits gründlich durchgesehen und versucht, sie zu hacken, kann es aber nicht herausfinden.

Danke! :)

Antwort

3
$ text ist eine übersetzte Zeichenfolge mit Platzhalter Anker-Tags

Wenn diese Platzhalter haben ein strenges, gut definierte Format einen einfachen preg_replace oder preg_replace_callback könnte den Trick tun.
Ich schlage nicht vor, über HTML-Dokumente mit Regex im Allgemeinen zu fiedeln, aber für eine kleine wohldefinierte Untergruppe sind sie geeignet.

1

XML hat nur very few predefined entities. Alle Ihre HTML-Entitäten sind an anderer Stelle definiert. Wenn Sie loadhtml() verwenden, werden diese Entitätsdefinitionen automatisch geladen, mit loadxml() (oder überhaupt keiner load()).
createTextNode() macht genau das, was der Name andeutet. Alles, was Sie als Wert übergeben, wird als Textinhalt behandelt, nicht als Markup. I.e. Wenn Sie etwas übergeben, das eine besondere Bedeutung für das Markup hat (<, >, ...), ist es so codiert, dass ein Parser den Text vom tatsächlichen Markup unterscheiden kann (& lt ;, & gt ;, ...)

Woher kommt $ text? Können Sie nicht den Ersatz innerhalb des tatsächlichen HTML-Dokuments tun?

+0

loadHTML, keine Entitätsübersetzung. Am Ende habe ich das Problem auf miserable Weise gemaked, indem ich mb_substr ($ text, 122, -19) ausgeführt habe; auf das Ergebnis von $ doc-> saveHTML(). Huch! :) $ text ist eine übersetzte Zeichenfolge mit Platzhalter-Anker-Tags, daher muss der Austausch zur Laufzeit ausgeführt werden.Ich würde lieber nicht das gesamte Dokument analysieren, da es schwierig wäre, nur die übersetzten Links zu analysieren. Gute Idee, obwohl. – thesmart

0

landete ich in einer tenuous Weise Hacking diese auf, zu ändern:

return $doc->saveHTML(); 

in:

$text  = $doc->saveHTML(); 
return mb_substr($text, 122, -19); 

Dies schneidet alle unnötigen Müll, dies zu ändern:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" 
"http://www.w3.org/TR/REC-html40/loose.dtd"> <html><body><p> 
You can <a href="http://www.google.com">click here</a> to visit Google.</p> 
</body></html> 

in diese:

You can <a href="http://www.google.com">click here</a> to visit Google. 

Kann jemand etwas besser herausfinden?

-1

OK, hier ist die endgültige Lösung, mit der ich endete. Beschlossen, mit VolkerKs Vorschlag zu gehen.

public static function ReplaceAnchors($text, array $attributeSets) 
{ 
    $expression = '/(<a)([\s\w\d:\/=_&\[\]\+%".?])*(>)/'; 

    if (empty($attributeSets) || !is_array($attributeSets)) { 
     // no attributes to set. Set href="#". 
     return preg_replace($expression, '$1 href="#"$3', $text); 
    } 

    $attributeStrs = array(); 
    foreach ($attributeSets as $attributeKeyVal) { 
     // loop thru attributes and set the anchor 
     $attributePairs = array(); 
     foreach ($attributeKeyVal as $name => $value) { 
      if (!is_string($value) && !is_int($value)) { 
       continue; // skip 
      } 

      $name    = htmlspecialchars($name); 
      $value    = htmlspecialchars($value); 
      $attributePairs[] = "$name=\"$value\""; 
     } 
     $attributeStrs[] = implode(' ', $attributePairs); 
    } 

    $i  = -1; 
    $pieces = preg_split($expression, $text); 
    foreach ($pieces as &$piece) { 
     if ($i === -1) { 
      // skip the first token 
      ++$i; 
      continue; 
     } 

     // figure out which attribute string to use 
     if (isset($attributeStrs[$i])) { 
      // pick the parallel attribute string 
      $attributeStr = $attributeStrs[$i]; 
     } else { 
      // pick the last attribute string if we don't have enough 
      $attributeStr = $attributeStrs[count($attributeStrs) - 1]; 
     } 

     // build a opening new anchor for this token. 
     $piece = '<a '.$attributeStr.'>'.preg_replace($expression, '$1 href="#"$3', $piece); 
     ++$i; 
    } 

    return implode('', $pieces); 

Dies ermöglicht es, die Funktion mit einer Reihe von verschiedenen Ankerattributen aufzurufen.