Библиотека утилит CakePHP Xml вызывает предупреждение DOMDocument

я генерирую XML в представлении с помощью CakePHP библиотека ядра Xml:

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

вид подается от контроллера с массивом:

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

по какой-то причине CakePHP выдает внутренний вызов следующим образом:

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

... что вызывает предупреждение PHP:

Warning (2): DOMDocument::createElement(): unterminated entity reference               T [CORECakeUtilityXml.php, line 292

... потому что (как документально),DOMDocument::createElement не убежать значения. Однако он делает это только в определенных узлах, как тестовый случай иллюстрирует.

я делаю что-то неправильно или я просто попал в ошибку в CakePHP?

4 ответов


Это может ошибка в PHPs DOMDocument::createElement() метод. Вы можете избежать этого. Создайте textnode отдельно и добавьте его в узел элемента.

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

var_dump($dom->saveXml());

выход:https://eval.in/134277

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

это предполагаемый способ добавления текстовых узлов в DOM. Вы всегда создаете узел (элемент, текст, cdata,...) и добавьте его к родительскому узлу. Вы можете добавить несколько узлов и различные типы узлов к одному родителю. Как в следующем пример:

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

выход:

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

это на самом деле потому, что методы DOMDocument хотят, чтобы правильные символы выводились в html; то есть символы, такие как & нарушит содержимое и создаст unterminated entity reference

просто htmlentities () перед его использованием для создания элементов:

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

именно из-за этого персонажа: & вам нужно заменить это соответствующим HTML-объектом. &amp; для выполнения перевода, вы можете использовать htmlspecialchars. Вы должны избежать значения при записи записи в свойство nodeValue. Как цитируется из отчета об ошибке в 2005 году расположен здесь

амперсанды правильно закодированы при установке текстового содержимого собственность. К сожалению, они не кодируются, когда текстовая строка передается в качестве необязательного второго аргумента Параметр domelement::метод createElement Необходимо создать текстовый узел, задать textContent, а затем добавить текст узел для нового элемента.

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

это таблица переводов:

'&' (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;'

этот скрипт будет делать перевод рекурсивно:

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

выход

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
                                                                )

                                                        )

                                                )

                                        )

                                )

                        )

                )

        )

)

проблема, похоже, в узлах, которые имеют как атрибуты, так и значения, поэтому должны использовать @ синтаксис:

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

Я написал небольшую вспомогательную функцию:

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

... и позаботьтесь о вызове его вручную, когда я создаю массив:

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

трудно сказать, если это ошибка или функция с документация не упоминает об этом.