2009-04-27 11 views
5

Tengo una función que reemplaza el atributo href de los anclajes en una cadena usando DOMDocument de Php. He aquí un fragmento:¿Cómo evito que DOMDocument de Php codifique entidades html?

$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(); 

El problema es que loadHTML ($ texto) rodea el texto en el tipo de documento $, html, cuerpo, etc. etiquetas. He intentado trabajar alrededor de esto haciendo esto en vez de loadHTML():

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

Desafortunadamente, esto codifica todas las entidades (anclajes incluidos). ¿Alguien sabe cómo apagar esto? Ya he examinado detenidamente los documentos e intenté hackearlo, pero no puedo resolverlo.

Gracias! :)

Respuesta

3
$ texto es una traducción de la cadena con etiquetas de anclaje lugar-titular

Si estos marcadores de posición tienen un estricto formato, bien definido o un simple preg_replacepreg_replace_callback puede hacer el truco.
No sugiero alterar los documentos html con expresiones regulares en general, pero para un pequeño subconjunto bien definido son adecuados.

1

XML tiene solo very few predefined entities. Todas las entidades html están definidas en otro lugar. Cuando usa loadhtml() estas definiciones de entidad se cargan automágicamente, con loadxml() (o no load() en absoluto) no lo son.
createTextNode() hace exactamente lo que sugiere su nombre. Todo lo que pasa como valor se trata como contenido de texto, no como marcado. Es decir. si pasa algo que tiene un significado especial para el marcado (<, >, ...) está codificado de manera que un analizador puede distinguir el texto del marcado real (& lt ;, & gt ;, ...)

¿De dónde viene el texto $? ¿No puedes hacer el reemplazo dentro del documento html real?

+0

loadHTML, ninguna traducción de entidad ocurre. Terminé pirateando el problema de una manera tenue ejecutando mb_substr ($ text, 122, -19); en el resultado de $ doc-> saveHTML(). ¡Ay! :) $ texto es una cadena traducida con etiquetas de anclaje de marcador de posición, por lo que la sustitución debe realizarse durante el tiempo de ejecución.Preferiría no analizar todo el documento, ya que sería difícil analizar únicamente los enlaces traducidos. Buena idea sin embargo. – thesmart

0

acabé cortar esto de una manera tenue, cambiando:

return $doc->saveHTML(); 

en:

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

Esto reduce toda la basura innecesaria, cambiando esto:

<!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> 

en esto:

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

¿Alguien puede encontrar algo mejor?

-1

OK, esta es la solución final con la que terminé. Decidió ir con la sugerencia de VolkerK.

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); 

Esto permite invocar la función con un conjunto de diferentes atributos de anclaje.

Cuestiones relacionadas