Библиотека утилит 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 & 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-объектом. &
для выполнения перевода, вы можете использовать htmlspecialchars. Вы должны избежать значения при записи записи в свойство nodeValue. Как цитируется из отчета об ошибке в 2005 году расположен здесь
амперсанды правильно закодированы при установке текстового содержимого собственность. К сожалению, они не кодируются, когда текстовая строка передается в качестве необязательного второго аргумента Параметр domelement::метод createElement Необходимо создать текстовый узел, задать textContent, а затем добавить текст узел для нового элемента.
htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
это таблица переводов:
'&' (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 '>'
этот скрипт будет делать перевод рекурсивно:
<?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 & 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
)
)
)
)
)
)
)
)
)
проблема, похоже, в узлах, которые имеют как атрибуты, так и значения, поэтому должны использовать @
синтаксис:
'@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'),
трудно сказать, если это ошибка или функция с документация не упоминает об этом.