Создание XML-документа в PHP (escape-символы)

Я создаю XML-документ из PHP-скрипта, и мне нужно избежать специальных символов XML. Я знаю список символов, которые должны быть экранированы; но каков правильный способ сделать это?

Должны ли символы сбежать только с помощью обратного слэша (\) или что это правильный способ? Есть ли встроенная функция PHP, которая может справиться с этим для меня?

Используйте классы DOM для создания всего XML-документа. Он будет обрабатывать кодировки и декодирования, о которых мы даже не хотим заботиться.


Редактировать: Это критиковали @ Чалвак:

Объект DOM создает полный XML-документ, он не просто поддается просто кодированию строки на своем собственном.

Что не так, DOMDocument может корректно выводить только фрагмент, а не весь документ:

$doc->saveXML($fragment); 

который дает:

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

как в:

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

См. Демонстрацию

Я создал простую функцию, которая скрывается с пятью «предопределенными сущностями», которые находятся в XML:

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

Пример использования Демонстрация :

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

Вывод:

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

Подобный эффект может быть достигнут с помощью str_replace но он хрупкий из-за двойной замены (непроверенный, не рекомендуется):

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

Как насчет функции htmlspecialchars() ?

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

Примечание: флаг ENT_XML1 доступен только в том случае, если у вас есть PHP 5.4.0 или выше.

htmlspecialchars() с этими параметрами заменяет следующие символы:

  • & (амперсанд) становится &amp;
  • " (двойная кавычка) становится &quot;
  • ' (одинарная кавычка)
  • < (меньше) становится &lt;
  • > (больше) становится &gt;

Вы можете получить таблицу переводов, используя get_html_translation_table() .

С трудом справлялись с проблемой XML-сущности, решайте таким образом:

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

Чтобы иметь действительный окончательный XML-текст, вам нужно убрать все сущности XML и написать текст, написанный в той же кодировке, что и инструкция обработки XML-документа, указывая его («кодирование» в строке <?xml ). Акцентированным символам не нужно экранировать, пока они закодированы как документ.

Тем не менее, во многих ситуациях просто избегать ввода с помощью htmlspecialchars может привести к двойным закодированным объектам (например, &eacute; , станет &amp;eacute; ), поэтому я предлагаю сначала декодировать html-объекты:

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

Теперь вам нужно убедиться, что все символы с акцентом действительны в кодировке XML-документа. Я настоятельно рекомендую всегда кодировать вывод XML в UTF-8, поскольку не все синтаксические анализаторы XML уважают кодировку XML-обработки документов. Если ваш вход может возникнуть из другой кодировки, попробуйте использовать utf8_encode() .

Есть специальный случай, который может быть вызван одним из этих кодировок: ISO-8859-1, ISO-8859-15, UTF-8, cp866, cp1251, cp1252 и KOI8-R – PHP обрабатывает их все но есть некоторые незначительные различия в них – некоторые из которых даже iconv() не могут справиться. Я мог бы решить эту проблему только путем дополнения поведения utf8_encode() :

 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; } 

Если вам нужен правильный вывод xml, Simplexml – это путь:

http://www.php.net/manual/en/simplexmlelement.asxml.php

Правильное экранирование – это способ получить правильный вывод XML, но вам нужно обрабатывать различие по атрибутам и элементам . (То есть ответ Томаса неверен).

Я написал / украл некоторый код Java некоторое время назад, который различает атрибут и escape-элемент. Причина в том, что синтаксический анализатор XML рассматривает все специальные пробелы, особенно в атрибутах.

Это должно быть тривиально, чтобы передать это на PHP (вы можете использовать подход Томаса Янчика с вышеупомянутым соответствующим экранированием). Вам не нужно беспокоиться об экранировании расширенных объектов, если вы используете UTF-8 .

Если вы не хотите переносить мой код Java, вы можете посмотреть на XMLWriter, который основан на потоке и использует libxml, поэтому он должен быть очень эффективным.

Вы можете использовать следующие методы: http://php.net/manual/en/function.htmlentities.php

Таким образом, все объекты (html / xml) экранированы, и вы можете поместить свою строку в теги XML

  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; } 

На основе решения sadeghj для меня работал следующий код:

 /** * @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; }