PHP開発では、特に非同期リクエスト(AjaxやCurlを使用して非同期に取得するなど)を作成する場合、XML形式でデータを受信し、 XML_Parseで解析しようとすることがあります。ただし、多くの開発者は、実際のアプリケーションで、 XML_PARSEが常に期待どおりに機能するとは限らないことを発見しており、エラーを報告したり、空のデータを返したりすることさえあります。それで、なぜこれが起こっているのですか?
この記事では、 XML_PARSEを使用して非同期要求でXMLデータを処理するための一般的な問題とソリューションを分析します。
PHPでは、 XML_PARSEはイベントベースの解析を介してXMLを処理します。これは通常、 XML_PARSER_CREATE 、 XML_SET_ELEMENT_HANDLER 、およびXML_PARSE自体で使用されます。例えば:
$xml_parser = xml_parser_create();
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData");
$data = file_get_contents("https://m66.net/api/data.xml");
if (!xml_parse($xml_parser, $data, true)) {
die(sprintf("XML Error: %s at line %d",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser)));
}
xml_parser_free($xml_parser);
同期環境では、このコードはうまく機能しますが、多くの場合、非同期呼び出しで発生します。
非同期リクエストは、データを完全に返信せずに処理を開始することが多く、XML文字列がXML_PARSEに渡され、不完全で解析障害が発生します。
非同期応答を処理するときは、データの整合性を確保し、コールバック関数を使用するなど、必要に応じてキャッシュまたは遅延処理メカニズムを使用します。
function onXmlDataReceived($data) {
if (strpos($data, '</root>') === false) {
// XML たぶん完全に返されません
return;
}
$parser = xml_parser_create();
xml_parse($parser, $data, true);
xml_parser_free($parser);
}
多くのXMLインターフェイスは、UTF-8エンコードされたコンテンツを返しますが、PHPのデフォルトの内部エンコードはUTF-8ではない場合があります。エンコーディングが均一でない場合、 XML_PARSEはエラーを報告する場合があります。
パーサーを作成するときにエンコーディングを指定するか、XML自体が正しいエンコード形式を宣言していることを確認してください。
$parser = xml_parser_create('UTF-8');
または、最初にエンコードと変換を行います:
$data = mb_convert_encoding($data, 'UTF-8', 'auto');
Curlを使用したマルチスレッド(curl_multi_* functionファミリー)などの非同期リクエストでは、必要なパーサーまたはコンテキスト情報がコールバック関数に合格するのを忘れてXML_PARSEを適切に動作させない可能性があります。
curl_multi_add_handle($mh, $ch);
// コールバックでパーサーやその他のコンテキストを渡すのを忘れた
class XmlParserContext {
public $parser;
public $data = '';
public function __construct() {
$this->parser = xml_parser_create();
xml_set_element_handler($this->parser, "startElement", "endElement");
xml_set_character_data_handler($this->parser, "characterData");
}
public function parse() {
xml_parse($this->parser, $this->data, true);
xml_parser_free($this->parser);
}
}
XML_PARSE自体は例外をスローせず、ブール値のみを返し、 XML_Get_Error_CodeおよびXML_ERROR_STRINGを使用してエラーメッセージを取得する必要があります。エラーの不明確な処理は、問題を簡単に追跡するのが難しくなる可能性があります。
if (!xml_parse($parser, $data, true)) {
error_log("XML Parse Error: " . xml_error_string(xml_get_error_code($parser)));
}
XML_PARSEは、下部のXMLを処理するための典型的な方法ですが、非同期リクエストでSimplexMLまたはDomDocumentを使用すると、より堅牢で簡潔になります。
$xml = simplexml_load_string($data);
foreach ($xml->item as $item) {
echo $item->title;
}
または:
$dom = new DOMDocument();
$dom->loadXML($data);
$items = $dom->getElementsByTagName('item');
XML_PARSEを使用して非同期要求でXMLデータを処理する場合、不完全なデータ、エンコードの不一致、コンテキストの喪失などの問題に遭遇することがよくあります。 XML_PARSEが必要な場合、データの整合性検出とコンテキスト管理を強化する必要があります。それ以外の場合は、 SimplexMLやDomdocumentなどの高レベルのXML解析ツールを使用することをお勧めします。