2014-04-09 15 views
7

sto generando XML in una vista con Xml core library di CakePHP:libreria di utilità CakePHP Xml innesca DOMDocument avvertimento

$xml = Xml::build($data, array('return' => 'domdocument')); 
echo $xml->saveXML(); 

View è alimentata dal controller con una serie:

$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', 
            ), 
           ), 
          ), 
         ), 
        ), 
       ), 
      ), 
     ), 
    ) 
); 

Per qualunque sia la ragione , CakePHP sta emettendo una chiamata interna come questa:

$dom = new DOMDocument; 
$key = 'sub4'; 
$childValue = 'S & T: ERROR'; 
$dom->createElement($key, $childValue); 

... che attiva un warning PHP:

Warning (2): DOMDocument::createElement(): unterminated entity reference    T [CORE\Cake\Utility\Xml.php, line 292 

... perché (as documented), DOMDocument::createElement non scappare valori. Tuttavia, lo fa solo in alcuni nodi, come illustra il caso di test.

Sto facendo qualcosa di sbagliato o ho appena colpito un bug in CakePHP?

+0

valore involucro come quello '' '$ dom-> createElement ($ key, htmlspecialchars ($ childValue)); '' 'farà il trucco – Alliswell

+0

@Alliswell - Si prega di leggere di nuovo la domanda. Questa è una domanda di CakePHP e non sto chiamando direttamente le funzioni DOM, ma semplicemente costruendo un array. E non posso applicare patch al core CakePHP in questo modo perché alcuni elementi sono già stati salvati, altri no. (Vedere la risposta accettata per alcuni dettagli aggiuntivi.) –

risposta

-1

Il problema sembra essere in nodi che hanno entrambi gli attributi e valori così necessario utilizzare il @ sintassi:

'@id' => 'A & B: OK', // <-- Handled as plain text 
'name' => 'C & D: OK', // <-- Handled as plain text 
'@' => 'S & T: ERROR', // <-- Handled as raw XML 

ho scritto un po 'di funzione di supporto:

protected function escapeXmlValue($value){ 
    return is_null($value) ? null : htmlspecialchars($value, ENT_XML1, 'UTF-8'); 
} 

... e prendersi cura di chiamare manualmente quando creo la matrice:

'@id' => 'A & B: OK', 
'name' => 'C & D: OK', 
'@' => $this->escapeXmlValue('S & T: NOW WORKS FINE'), 

e 'difficile dire se si tratta di bug o funzionalità dal momento che il 012.347.non lo menziona.

0

è a causa di questo carattere: & È necessario sostituirlo con l'entità HTML pertinente. &amp; Per eseguire la traduzione, è possibile utilizzare la funzione htmlspecialchars. Devi sfuggire al valore quando scrivi la scrittura sulla proprietà nodeValue. Come citato da un bug report nel 2005 situato here

e commerciali sono correttamente codificati quando si imposta la textContent proprietà. Sfortunatamente non sono codificati quando la stringa di testo viene passata come seconda argomentazione opzionale a DOMElement :: createElement È necessario creare un nodo di testo, impostare il testoContent, quindi aggiungere il nodo di testo al nuovo elemento.

htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); 

Questa è la tabella di traduzione:

'&' (ampersand) becomes '&amp;' 
'"' (double quote) becomes '&quot;' when ENT_NOQUOTES is not set. 
"'" (single quote) becomes '&#039;' (or &apos;) only when ENT_QUOTES is set. 
'<' (less than) becomes '&lt;' 
'>' (greater than) becomes '&gt;' 

Questo script farà le traduzioni in modo ricorsivo:

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

uscita

Array 
(
    [data] => Array 
     (
      [root] => Array 
       (
        [0] => Array 
         (
          [@id] => A &amp; B: OK 
          [name] => C &amp; D: OK 
          [sub1] => Array 
           (
            [@id] => E &amp; F: OK 
            [name] => G &amp; H: OK 
            [sub2] => Array 
             (
              [0] => Array 
               (
                [@id] => I &amp; J: OK 
                [name] => K &amp; L: OK 
                [sub3] => Array 
                 (
                  [@id] => M &amp; N: OK 
                  [name] => O &amp; P: OK 
                  [sub4] => Array 
                   (
                    [@id] => Q &amp; R: OK 
                    [@] => S &amp; T: ERROR 
                   ) 

                 ) 

               ) 

             ) 

           ) 

         ) 

       ) 

     ) 

) 
+0

L'OP afferma chiaramente che si tratta di un avvertimento, ma vuole capirne il motivo. Il semplice ignorare l'avvertimento è una pessima idea. –

+0

La soppressione del messaggio non risolve il problema sottostante. È come infilare le dita nelle orecchie e cantare a squarciagola. –

+0

Lo aggiusta in alcuni contesti. –

15

Questo potrebbe essere un bug in PHP DOMDocument::createElement() metodo. Puoi evitarlo.Crea il textnode separatamente e aggiungilo al nodo dell'elemento.

$dom = new DOMDocument; 
$dom 
    ->appendChild($dom->createElement('element')) 
    ->appendChild($dom->createTextNode('S & T: ERROR')); 

var_dump($dom->saveXml()); 

uscita: https://eval.in/134277

string(58) "<?xml version="1.0"?> 
<element>S &amp; T: ERROR</element> 
" 

Questo è il modo previsto per aggiungere i nodi di testo a un DOM. Crei sempre un nodo (elemento, testo, cdata, ...) e lo aggiungi al suo nodo genitore. È possibile aggiungere più di un nodo e un diverso tipo di nodi a un genitore. Come nel seguente esempio:

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

uscita:

<?xml version="1.0"?> 
<p>Hello <b>World!</b></p> 
+0

Non ho ancora testato se è possibile inserire oggetti 'DOMDocument' nell'array di dati ma, se sai in anticipo quali valori devono essere risolti¹, questa è una soluzione abbastanza complicata :) - (¹) Non sapevo quando ho chiesto la domanda. –

+0

In realtà questa non è una soluzione alternativa. Il secondo argomento in createElement() interrompe la specifica del DOM W3C. L'esempio sopra è il modo standard per aggiungere nodi di testo. L'argomento nel metodo è solo una scorciatoia - una rotta. – ThW

4

Questo è infatti perché i metodi DOMDocument vuole caratteri corretti per essere emessi in html; cioè, personaggi come & si romperà contenuti e generare un errore unterminated entity reference

solo htmlentities() prima di utilizzarlo per creare elementi:

$dom = new DOMDocument; 
$key = 'sub4'; 
$childValue = htmlentities('S & T: ERROR'); 
$dom->createElement($key ,$childValue); 
+0

La funzione '' 'htmlspecialchars''' ha funzionato nel mio caso. – Alliswell