La biblioteca de utilidades CakePHP Xml desencadena la advertencia de DOMDocument
cakephp-2.2 (4)
Esto podría ser un error en el método DOMDocument::createElement()
. Puedes evitarlo Cree el nodo de texto por separado y añádalo al nodo del elemento.
$dom = new DOMDocument;
$dom
->appendChild($dom->createElement(''element''))
->appendChild($dom->createTextNode(''S & T: ERROR''));
var_dump($dom->saveXml());
Salida: https://eval.in/134277
string(58) "<?xml version="1.0"?>
<element>S & T: ERROR</element>
"
Esta es la forma prevista de agregar nodos de texto a un DOM. Siempre crea un nodo (elemento, texto, cdata, ...) y lo agrega a su nodo padre. Puede agregar más de un nodo y diferentes tipos de nodos a uno de los padres. Como en el siguiente ejemplo:
$dom = new DOMDocument;
$p = $dom->appendChild($dom->createElement(''p''));
$p->appendChild($dom->createTextNode(''Hello ''));
$b = $p->appendChild($dom->createElement(''b''));
$b->appendChild($dom->createTextNode(''World!''));
echo $dom->saveXml();
Salida:
<?xml version="1.0"?>
<p>Hello <b>World!</b></p>
Estoy generando XML en una vista con la biblioteca principal Xml de CakePHP:
$xml = Xml::build($data, array(''return'' => ''domdocument''));
echo $xml->saveXML();
La vista se alimenta desde el controlador con una matriz:
$this->set(
array(
''data'' => array(
''root'' => array(
array(
''@id'' => ''A & B: OK'',
''name'' => ''C & D: OK'',
''sub1'' => array(
''@id'' => ''E & F: OK'',
''name'' => ''G & H: OK'',
''sub2'' => array(
array(
''@id'' => ''I & J: OK'',
''name'' => ''K & L: OK'',
''sub3'' => array(
''@id'' => ''M & N: OK'',
''name'' => ''O & P: OK'',
''sub4'' => array(
''@id'' => ''Q & R: OK'',
''@'' => ''S & T: ERROR'',
),
),
),
),
),
),
),
),
)
);
Por la razón que sea, CakePHP está emitiendo una llamada interna como esta:
$dom = new DOMDocument;
$key = ''sub4'';
$childValue = ''S & T: ERROR'';
$dom->createElement($key, $childValue);
... que desencadena una advertencia de PHP:
Warning (2): DOMDocument::createElement(): unterminated entity reference T [CORE/Cake/Utility/Xml.php, line 292
... porque ( como se documenta ), DOMDocument::createElement
no escapa a los valores. Sin embargo, solo lo hace en ciertos nodos, como lo ilustra el caso de prueba.
¿Estoy haciendo algo mal o acabo de encontrar un error en CakePHP?
es debido a este personaje: &
Debe reemplazarlo con la entidad HTML relevante. &
Para realizar la traducción, puede usar la función htmlspecialchars . Debe evitar el valor al escribir en la propiedad nodeValue. Como se cita de un informe de error en 2005 ubicado aquí
los símbolos están codificados correctamente al establecer la propiedad textContent. Lamentablemente, no están codificados cuando se pasa la cadena de texto como segundo argumento opcional a DOMElement :: createElement. Debe crear un nodo de texto, establecer el contenido de texto y luego agregar el nodo de texto al nuevo elemento.
htmlspecialchars($string, ENT_QUOTES, ''UTF-8'');
Esta es la tabla de traducción:
''&'' (ampersand) becomes ''&''
''"'' (double quote) becomes ''"'' when ENT_NOQUOTES is not set.
"''" (single quote) becomes ''''' (or ') only when ENT_QUOTES is set.
''<'' (less than) becomes ''<''
''>'' (greater than) becomes ''>''
Este script hará las traducciones recursivamente:
<?php
function clean($type) {
if(is_array($type)) {
foreach($type as $key => $value){
$type[$key] = clean($value);
}
return $type;
} else {
$string = htmlspecialchars($type, ENT_QUOTES, ''UTF-8'');
return $string;
}
}
$data = array(
''data'' => array(
''root'' => array(
array(
''@id'' => ''A & B: OK'',
''name'' => ''C & D: OK'',
''sub1'' => array(
''@id'' => ''E & F: OK'',
''name'' => ''G & H: OK'',
''sub2'' => array(
array(
''@id'' => ''I & J: OK'',
''name'' => ''K & L: OK'',
''sub3'' => array(
''@id'' => ''M & N: OK'',
''name'' => ''O & P: OK'',
''sub4'' => array(
''@id'' => ''Q & R: OK'',
''@'' => ''S & T: ERROR'',
) ,
) ,
) ,
) ,
) ,
) ,
) ,
) ,
);
$data = clean($data);
Salida
Array
(
[data] => Array
(
[root] => Array
(
[0] => Array
(
[@id] => A & B: OK
[name] => C & D: OK
[sub1] => Array
(
[@id] => E & F: OK
[name] => G & H: OK
[sub2] => Array
(
[0] => Array
(
[@id] => I & J: OK
[name] => K & L: OK
[sub3] => Array
(
[@id] => M & N: OK
[name] => O & P: OK
[sub4] => Array
(
[@id] => Q & R: OK
[@] => S & T: ERROR
)
)
)
)
)
)
)
)
)
De hecho, esto se debe a que los métodos DOMDocument quieren que los caracteres correctos se muestren en html; es decir, caracteres como &
dividirán el contenido y generarán un error de unterminated entity reference
solo htmlentities () antes de usarlo para crear elementos:
$dom = new DOMDocument;
$key = ''sub4'';
$childValue = htmlentities(''S & T: ERROR'');
$dom->createElement($key ,$childValue);
El problema parece ser que en los nodos que tienen tanto atributos como valores, es necesario utilizar la sintaxis @
:
''@id'' => ''A & B: OK'', // <-- Handled as plain text
''name'' => ''C & D: OK'', // <-- Handled as plain text
''@'' => ''S & T: ERROR'', // <-- Handled as raw XML
He escrito una pequeña función auxiliar:
protected function escapeXmlValue($value){
return is_null($value) ? null : htmlspecialchars($value, ENT_XML1, ''UTF-8'');
}
... y tenga cuidado de llamarlo manualmente cuando creo la matriz:
''@id'' => ''A & B: OK'',
''name'' => ''C & D: OK'',
''@'' => $this->escapeXmlValue(''S & T: NOW WORKS FINE''),
Es difícil decir si es un error o una característica, ya que la documentación no lo menciona.