2010-10-18 27 views
45

我從PHP腳本生成XML文檔,我需要轉義XML特殊字符。 我知道應該逃避的字符列表;但是做到這一點的正確方法是什麼?在PHP中生成XML文檔(轉義字符)

字符是否應該用反斜槓(\')轉義或者正確的方式是什麼? 是否有任何內置的PHP函數可以爲我處理?

+0

@Tchalvak:你錯了很多你在你的賞金描述批評點。我試圖用現有的答案使其可見,希望這是有幫助的。 – hakre 2013-02-16 07:53:38

+0

我沒有在我的答案中建議只使用DOM API來進行字符串轉義。我建議你使用該API生成整個XML文檔。這是爲了迴應你在賞金描述中提到的問題。 – 2013-02-16 20:02:18

回答

33

使用DOM類來生成你的整個XML文件。它將處理我們甚至不想關心的編碼和解碼。


編輯:這是批評@Tchalvak:

的DOM對象創建一個完整的XML文檔,它不輕易出借自己剛剛編碼它自己的字符串。

哪項是錯誤的,DOM文檔可以正常輸出只是一個片段,而不是整個文件:

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

Demo

+5

根據https://bugs.php.net/bug.php?id=31191你特別想使用[createTextNode](http://www.php.net/manual/en/domdocument.createtextnode.php)函數以獲得適當的自動轉義。 – Jonathan 2012-01-03 14:43:23

+0

我認爲@Tchalvak問題是,它不是基於流。這是使用DOM將創建一堆對象。正如我在我的[回覆](http://stackoverflow.com/a/15010355/318174)中提到的,他可以使用XMLWriter或將我的Java代碼移植到PHP中,以正確轉義(Tomas Jancik方式不正確)。 – 2013-02-22 16:53:28

+0

Whoops,http://eval.in/10980是使用本機處理的'xmlentities()'函數的正確實現。可能有一些開銷,但是,可能值得使用本機/面向未來功能的安心。 – Kzqai 2013-02-22 23:54:26

0

您可以使用此方法: http://php.net/manual/en/function.htmlentities.php

這樣一來所有的實體(HTML/XML)被轉義,並且你可以把你的字符串XML標籤內

+5

這是一個糟糕的解決方案,因爲HTML實體比XML實體更大,大多數XML解析器不會識別XML實體列表中不存在的許多HTML實體。 – 2012-05-02 10:52:04

35

我創建簡單的函數,與five "predefined entities"是在XML逸出:

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

用法例子Demo

$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 
    ); 
} 
+11

您需要將''&「'和'」&「'數組元素移至開頭,否則所有其他創建的實體都將替換其&符號。另外,'strtr'解決方案似乎根本不起作用。 – Ryan 2011-11-05 20:47:23

+2

5個XML實體?我希望這是簡單的... – NDM 2012-12-12 17:42:52

+1

對於OP回答他自己的問題,這裏真的存在嚴重的缺陷。我試圖編輯它的好處,但我不知道是否使用了原始代碼;) - @Nicky De Maeyer:是的,XML中有五個預定義實體,我放置了一個鏈接。 – hakre 2013-02-18 17:28:08

12

努力地處理XML實體問題,解決了這種方式:

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

這隻適用於以XML定義這些實體的情況,請參閱http://www.w3.org/TR/xml-entity-names/ – hakre 2013-02-16 08:03:07

16

怎麼樣htmlspecialchars()功能?

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

注:如果您對PHP 5.4.0或更高版本的ENT_XML1標誌纔可用。

htmlspecialchars()與這些參數替換下列字符:

  • &(符號)變成&amp;
  • "(雙引號)變得&quot;
  • '(單引號)變得&apos;
  • <(小於)變成&lt;
  • >(大於)成爲&gt;

您可以通過使用get_html_translation_table()功能得到轉換表。

+1

對於兼容XML的編碼,沒有特別需要使用ENT_XML1 - 至少適用於PHP版本4.3.0至5.5.0alpha4。一個簡單的'htmlspecialchars($ input,ENT_QUOTES,$ encoding)''也可以完成這項工作,如果你可以使用數字而不是命名實體。 – hakre 2013-02-16 07:59:49

5

爲了有一個有效的最終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文檔編碼中都是有效的。我強烈建議始終使用UTF-8編碼XML輸出,因爲並非所有XML解析器都遵守XML文檔處理指令編碼。如果您的輸入可能來自不同的字符集,請嘗試使用utf8_encode()

有一種特殊情況,您的輸入可能來自以下編碼之一:ISO-8859-1,ISO-8859-15,UTF-8,cp866,cp1251,cp1252和KOI8-R - PHP對待他們都是一樣的,但是他們之間有一些細微的差異 - 其中一些甚至不能處理iconv()

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

正確的轉義得到正確的方式,我只能通過補充utf8_encode()行爲來解決這個編碼問題XML輸出但您需要以不同方式處理轉義對於屬性元素。 (這是托馬斯的回答不正確)。

我寫/偷了一些​​一段時間後,區分屬性和元素轉義。原因在於XML解析器認爲所有的空白空間都是特別特殊的。

將它移植到PHP應該很簡單(可以使用Tomas Jancik的方法進行上述適當的轉義)。如果您使用UTF-8,則不必擔心轉義擴展實體。

如果您不想移植我的Java代碼,您可以查看基於流的XMLWriter,並使用libxml,因此它應該非常高效。

+0

+1,因爲我不知道XMLWriter會自動爲你做這件事。 – Shackrock 2013-05-23 19:39:12

-1
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; 
    }  
+4

這在很多層面上都很糟糕: **(1)**無需使用正則表達式進行啞搜索和替換。 **(2)**替換值不是正確的實體(它們不以分號結尾)。 **(3)**您將分別獲得每個替換版本的數組。 **(4)**這種策略甚至不是面向未來的;或者每當規範發生變化時你都會維護它? 我不知道如何得到4票。 – Christian 2015-10-29 11:00:50

-1

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