2010-10-18 42 views
45

Estoy generando un documento XML a partir de un script PHP y necesito escapar de los caracteres especiales XML. Conozco la lista de caracteres que deberían escaparse; pero, ¿cuál es la forma correcta de hacerlo?Generando documento XML en PHP (caracteres de escape)

¿Se deben escapar los caracteres solo con barra invertida (\ ') o cuál es la forma correcta? ¿Hay alguna función PHP incorporada que pueda manejar esto por mí?

+0

@Tchalvak: Usted está equivocado con muchos de los puntos criticas en su descripción de recompensas. Traté de hacer esto visible con las respuestas existentes, espero que esto sea útil. – hakre

+0

No sugerí en mi respuesta usar la API DOM solo para el escape de cadenas. Le sugerí que genere su documento XML completo usando esa API. Esto es en respuesta a los problemas que mencionas en la descripción de tu recompensa. –

Respuesta

33

Utilice los DOM clases para generar todo el código XML documento. Manejará codificaciones y decodings que ni siquiera queremos preocuparnos.


Editar: Este fue criticada por @Tchalvak:

El objeto DOM crea un documento XML completo, no se presta fácilmente a poco se codifica una cadena por sí mismo.

que está mal, DOMDocument puede adecuadamente salida sólo un fragmento no todo el documento:

$doc->saveXML($fragment); 

lo que da:

Test &amp; <b> and encode </b> :) 
Test &amp;amp; &lt;b&gt; and encode &lt;/b&gt; :) 

como en:

$doc = new DOMDocument(); 
$fragment = $doc->createDocumentFragment(); 

// adding XML verbatim: 
$xml = "Test &amp; <b> and encode </b> :)\n"; 
$fragment->appendXML($xml); 

// adding text: 
$text = $xml; 
$fragment->appendChild($doc->createTextNode($text)); 

// output the result 
echo $doc->saveXML($fragment); 

Ver Demo

+5

Según https://bugs.php.net/bug.php?id=31191, específicamente desea utilizar el [createTextNode] (http://www.php.net/manual/en/domdocument.createtextnode.php) función para obtener el escape automático adecuado. – Jonathan

+0

Creo que el problema de @Tchalvak es que no se basa en la transmisión. Eso es usando DOM creará un grupo de objetos. Como mencioné en mi [respuesta] (http://stackoverflow.com/a/15010355/318174) él puede usar XMLWriter o simplemente portar mi código Java a PHP que escapa correctamente (la forma de Tomás Jancik no es correcta). –

+0

Whoops, http://eval.in/10980 es la implementación correcta de una función 'xmlentities()' que usa el procesamiento nativo. Probablemente tenga algunos gastos generales, pero, probablemente, también le valga la tranquilidad de usar una funcionalidad nativa/a prueba de futuro. – Kzqai

0

Puede utilizar estos métodos: http://php.net/manual/en/function.htmlentities.php

de esa manera se escapan todas las entidades (HTML/XML) y usted puede poner su cadena dentro de las etiquetas XML

+5

Esta es una mala solución, porque las entidades HTML son un conjunto más grande que las entidades XML y la mayoría del analizador XML no reconoce muchas entidades HTML que no están en la lista de entidades XML. –

35

I creados función simple que escapa con el five "predefined entities" que están en XML:

function xml_entities($string) { 
    return strtr(
     $string, 
     array(
      "<" => "&lt;", 
      ">" => "&gt;", 
      '"' => "&quot;", 
      "'" => "&apos;", 
      "&" => "&amp;", 
     ) 
    ); 
} 

Ejemplo de uso Demo:

$text = "Test &amp; <b> and encode </b> :)"; 
echo xml_entities($text); 

de salida:

Test &amp;amp; &lt;b&gt; and encode &lt;/b&gt; :) 

Un efecto similar puede lograrse usando str_replace pero es frágil debido a doble-replacings (no probado, no se recomienda):

function xml_entities($string) { 
    return str_replace(
     array("&",  "<", ">", '"',  "'"), 
     array("&amp;", "&lt;", "&gt;", "&quot;", "&apos;"), 
     $string 
    ); 
} 
+11

Debe mover los elementos de matriz '" & '' y '" & "' al comienzo, o de lo contrario todas las demás entidades que se crearán también reemplazarán sus ampersands. Además, la solución 'strtr' no parece funcionar en absoluto. – Ryan

+2

las 5 entidades xml? Ojalá fuera así de simple ... – NDM

+1

Para el OP que respondía a su propia pregunta, había defectos realmente serios aquí. Traté de editarlo para siempre, pero me pregunto si el código original se usó o no;) - @Nicky De Maeyer: Sí, hay cinco entidades predefinidas en XML, coloqué un enlace. – hakre

12

se esforzó para hacer frente a problema de entidad XML, resolver de esta manera:

htmlspecialchars($value, ENT_QUOTES, 'UTF-8') 
+0

Esto solo funcionará si esas entidades están definidas en XML, consulte http://www.w3.org/TR/xml-entity-names/ – hakre

16

¿Qué pasa con la función htmlspecialchars()?

htmlspecialchars($input, ENT_QUOTES | ENT_XML1, $encoding); 

Nota: la bandera ENT_XML1 sólo está disponible si tiene PHP 5.4.0 o superior.

htmlspecialchars() con estos parámetros sustituye a los siguientes caracteres:

  • & (signo) se convierte en &amp;
  • " (comillas dobles) se convierte en &quot;
  • ' (comilla simple) se convierte en &apos;
  • < (menos de) se convierte en &lt;
  • > (mayor que) se convierte en &gt;

, usted puede obtener la tabla de traducción mediante el uso de la función get_html_translation_table().

+1

No es necesario utilizar 'ENT_XML1' para la codificación compatible con XML, al menos para PHP versiones 4.3.0 a 5.5.0alpha4. Un simple 'htmlspecialchars ($ input, ENT_QUOTES, $ encoding);' hace el trabajo también si puede vivir con un numérico en lugar de una entidad con nombre. – hakre

5

Para tener un texto XML final válido, debe escapar de todas las entidades XML y tener el texto escrito en la misma codificación que el documento XML-instruction instructions (la "codificación" en la línea <?xml). Los caracteres acentuados no necesitan escaparse mientras estén codificados como el documento.

Sin embargo, en muchas situaciones, simplemente escapar de la entrada con htmlspecialchars puede conducir a doble codificado entidades (por ejemplo &eacute; se convertiría en &amp;eacute;), por lo que sugiero entidades html decodificación primera:

function xml_escape($s) 
{ 
    $s = html_entity_decode($s, ENT_QUOTES, 'UTF-8'); 
    $s = htmlspecialchars($s, ENT_QUOTES, 'UTF-8', false); 
    return $s; 
} 

A continuación, debe asegúrese de que todos los caracteres acentuados sean válidos en la codificación del documento XML. Recomiendo encarecidamente codificar siempre la salida XML en UTF-8, ya que no todos los analizadores XML respetan la codificación de instrucciones de procesamiento de documentos XML. Si su entrada puede provenir de un juego de caracteres diferente, intente con utf8_encode().

Hay un caso especial, que es su entrada puede venir de una de estas codificaciones: ISO-8859-1, ISO-8859-15, UTF-8, cp866, cp1251, cp1252 y KOI8-R - PHP los trata de todos modos, pero hay algunas pequeñas diferencias en ellos, algunos de los cuales incluso iconv() no pueden manejar. Sólo podía resolver este problema codificación complementando utf8_encode() comportamiento:

function encode_utf8($s) 
{ 
    $cp1252_map = array(
    "\xc2\x80" => "\xe2\x82\xac", 
    "\xc2\x82" => "\xe2\x80\x9a", 
    "\xc2\x83" => "\xc6\x92", 
    "\xc2\x84" => "\xe2\x80\x9e", 
    "\xc2\x85" => "\xe2\x80\xa6", 
    "\xc2\x86" => "\xe2\x80\xa0", 
    "\xc2\x87" => "\xe2\x80\xa1", 
    "\xc2\x88" => "\xcb\x86", 
    "\xc2\x89" => "\xe2\x80\xb0", 
    "\xc2\x8a" => "\xc5\xa0", 
    "\xc2\x8b" => "\xe2\x80\xb9", 
    "\xc2\x8c" => "\xc5\x92", 
    "\xc2\x8e" => "\xc5\xbd", 
    "\xc2\x91" => "\xe2\x80\x98", 
    "\xc2\x92" => "\xe2\x80\x99", 
    "\xc2\x93" => "\xe2\x80\x9c", 
    "\xc2\x94" => "\xe2\x80\x9d", 
    "\xc2\x95" => "\xe2\x80\xa2", 
    "\xc2\x96" => "\xe2\x80\x93", 
    "\xc2\x97" => "\xe2\x80\x94", 
    "\xc2\x98" => "\xcb\x9c", 
    "\xc2\x99" => "\xe2\x84\xa2", 
    "\xc2\x9a" => "\xc5\xa1", 
    "\xc2\x9b" => "\xe2\x80\xba", 
    "\xc2\x9c" => "\xc5\x93", 
    "\xc2\x9e" => "\xc5\xbe", 
    "\xc2\x9f" => "\xc5\xb8" 
    ); 
    $s=strtr(utf8_encode($s), $cp1252_map); 
    return $s; 
} 
1

escape adecuada es la manera de conseguir correcta XML de salida, pero necesita manejar escapando de manera diferente para atributos y elementos. (Esa es la respuesta de Tomás es incorrecta).

Escribí/robé unos Java code hace un tiempo que diferencia entre el atributo y el elemento que se escapa. La razón es que el analizador XML considera todo el espacio en blanco especial, en particular en los atributos.

Debe ser trivial trasladar eso a PHP (puede usar el enfoque de Tomás Jancik con el escape apropiado anterior). No tiene que preocuparse por escapar de entidades extendidas si usa UTF-8.

Si no desea portar mi código Java, puede mirar XMLWriter que se basa en la transmisión y usa libxml, por lo que debería ser muy eficiente.

+0

+1 porque no tenía idea de que XMLWriter hiciera esto automáticamente por usted. – Shackrock

-1
function replace_char($arr1) 
{ 
    $arr[]=preg_replace('>','&gt', $arr1); 
    $arr[]=preg_replace('<','&lt', $arr1); 
    $arr[]=preg_replace('"','&quot', $arr1); 
    $arr[]=preg_replace('\'','&apos', $arr1); 
    $arr[]=preg_replace('&','&amp', $arr1); 

    return $arr; 
    }  
+4

Esto es malo en muchos niveles: ** (1) ** No es necesario usar expresiones regulares para una búsqueda tonta y reemplazar. ** (2) ** Los valores de reemplazo no son entidades apropiadas (no terminan con un punto y coma). ** (3) ** Obtendrá una matriz de cada versión reemplazada por separado. ** (4) ** Esta estrategia ni siquiera es a prueba de futuro; o vas a mantenerlo cada vez que cambia la especificación? No tengo idea de cómo obtuvo 4 votos. – Christian

-1

Sobre la base de la solución de sadeghj el siguiente código trabajó para mí:

/** 
* @param $arr1 the single string that shall be masked 
* @return the resulting string with the masked characters 
*/ 
function replace_char($arr1) 
{ 
    if (strpos ($arr1,'&')!== FALSE) { //test if the character appears 
     $arr1=preg_replace('/&/','&amp;', $arr1); // do this first 
    } 

    // just encode the 
    if (strpos ($arr1,'>')!== FALSE) { 
     $arr1=preg_replace('/>/','&gt;', $arr1); 
    } 
    if (strpos ($arr1,'<')!== FALSE) { 
     $arr1=preg_replace('/</','&lt;', $arr1); 
    } 

    if (strpos ($arr1,'"')!== FALSE) { 
     $arr1=preg_replace('/"/','&quot;', $arr1); 
    } 

    if (strpos ($arr1,'\'')!== FALSE) { 
     $arr1=preg_replace('/\'/','&apos;', $arr1); 
    } 

    return $arr1; 
}