はじめに
PHPでXMLを読み込むには、通常はSimpleXMLElementを使えば十分です。
ただし、XMLが巨大でメモリを節約して処理する必要がある場合は、XMLのパーサーであるXMLReaderを使って処理する方法があります。
XMLReaderを使って読み込む際はXMLの構造をどうとらえるかによって、プログラムの書き方が変わるのと、毎回読み込みの方法をプログラムしなければならないのが欠点です。
今回は下記のサンプルXMLのproduct部分を読み込むXMLReaderのプログラムのサンプルを、XMLReaderの機能紹介も兼ねて、いくつか示します。
<?xml version="1.0" encoding="UTF-8"?>
<products>
<date type="1">20200414</date>
<product>
<maker>AMD</maker>
<name>Ryzen 3400G</name>
</product>
<product>
<maker>Intel</maker>
<name>Core i9 9900K</name>
</product>
</products>
サンプルプログラムでは、下記のPHPの配列形式を取得することを目標にします。
実際の用途ではXMLReaderを使ってXMLを読み込むと同時に、CSVファイルに出力するなどといった処理が考えられます。
この場合、メモリはXMLReader部分と、読み込み途中で一時的に保持しているデータのみで利用されるので、メモリの使用量は最低限に抑えることができます。
[
[
"maker"=> "AMD",
"name"=> "Ryzen 3400G"
],
[
"maker"=> "Intel",
"name"=> "Core i9 9900K"
]
];
サンプル1) nameだけを使った方法
<?php
$path = ''; // 読み込むxmlファイルのパス
$reader = new \XMLReader();
$reader->open($path);
// 階層が深いXMLの場合は、stackを使って、現在の階層位置のデータを保存するやり方が便利です。
$inProduct = false;
$item = [];
// 結果を保存する配列
$items = [];
try {
while($reader->read()){
if($reader->name == 'product')
{
if($reader->nodeType == \XMLReader::ELEMENT)
{
$inProduct = true;
$item = [];
}
else if($reader->nodeType == \XMLReader::END_ELEMENT)
{
$inProduct = false;
$items[] = $item;
}
}
else
{
if($inProduct)
{
if($reader->nodeType == \XMLReader::ELEMENT)
{
$name = $reader->name;
}
else if($reader->nodeType == \XMLReader::TEXT)
{
$item[$name] = $reader->value;
}
}
}
}
} finally{
$reader->close();
}
サンプル2) nameとdepthを使ったサンプル
<?php
$path = ''; // 読み込むxmlファイルのパス
$reader = new \XMLReader();
$reader->open($path);
$item = [];
// 結果を保存する配列
$items = [];
try {
while($reader->read()){
if($reader->depth == 1 && $reader->name == 'product')
{
if($reader->nodeType == \XMLReader::ELEMENT)
{
$item = [];
}
else if($reader->nodeType == \XMLReader::END_ELEMENT)
{
$items[] = $item;
}
}
else
{
if($reader->depth == 2 && $reader->nodeType == \XMLReader::ELEMENT)
{
$name = $reader->name;
}
else if($reader->depth == 3 && $reader->nodeType == \XMLReader::TEXT)
{
$item[$name] = $reader->value;
}
}
}
} finally{
$reader->close();
}
サンプル3) 多重ループを使ったサンプル
<?php
$reader = new \XMLReader();
$reader->open($path);
// 結果を保存する配列
$items = [];
try {
while($reader->read()){
if($reader->name == 'product' && $reader->nodeType == \XMLReader::ELEMENT)
{
$item = [];
while($reader->read()){
if($reader->name == 'product' && $reader->nodeType == \XMLReader::END_ELEMENT)
{
$items[] = $item;
break;
}
if($reader->nodeType == \XMLReader::ELEMENT)
{
$name = $reader->name;
}
else if($reader->nodeType == \XMLReader::TEXT)
{
$item[$name] = $reader->value;
}
}
}
}
} finally{
$reader->close();
}
コメント