[英]PHP Parse huge XML file
我有一個這樣的 XML 文檔,它的文件大於 400 MB。
我的問題是我無法讓 XMLReader 不遇到內存限制,有一個 512 Mb PHP 7.2 服務器。
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetModifiedResponse xmlns="http://host.com">
<ProductList>
<UpdatedProducts>
<ProductId>1</ProductId>
<ProductId>2</ProductId>
<ProductId>3</ProductId>
<ProductId>4</ProductId>
</UpdatedProducts>
<RemovedProducts>
<ProductId>5</ProductId>
<ProductId>6</ProductId>
<ProductId>7</ProductId>
<ProductId>8</ProductId>
</RemovedProducts>
</ProductList>
..
這有點像我的腳本,這里的問題是整個“ UpdatedProducts
”已加載並使內存最大化。 並且需要一個類似的 RemovedProducts ,兩者都需要在循環中,如何解決問題 - 如果可能的話我們在服務器中放置更多內存(或memory_limit(-1)
)?
while ($xml->name == 'UpdatedProducts') {
$elm = new \SimpleXMLElement($xml->readOuterXml());
foreach ($elm->ProductId as $product) {
$this->saveToDb($product);
}
$xml->next('UpdatedProducts');
}
代碼是現在
$xml = new \XMLReader();
$xml->open(__DIR__ . '/../../var/tmp/out.xml');
while ($xml->read()) {
while ($xml->name == 'UpdatedProducts') {
while ($xml->read() && $xml->name != 'ProductId');
while ($xml->name == 'ProductId') {
$this->saveToDb($xml->readInnerXml(), 'update');
$xml->next('ProductId');
}
$xml->next('UpdatedProducts');
}
while ($xml->name == 'RemovedProducts') {
while ($xml->read() && $xml->name != 'ProductId');
while ($xml->name == 'ProductId') {
$this->saveToDb($xml->readInnerXml(), 'remove');
$xml->next('ProductId');
}
$xml->next('RemovedProducts');
}
}
與其使用 SimpleXML 來獲取<UpdatedProducts>
所有節點, <UpdatedProducts>
嵌套相同的代碼,使其在此節點內為 ` 節點讀取。 這意味着內循環將一次獲得 1 個節點......
while ($xml->name == 'UpdatedProducts') {
while ($xml->read() && $xml->name !== 'ProductId');
while ($xml->name == 'ProductId') {
echo $xml->readOuterXml().PHP_EOL;
$xml->next('ProductId');
}
$xml->next('UpdatedProducts');
}
對於這兩種類型,我都嘗試將其減少為一個循環。 這並不理想,但似乎有效......
$xml = new \XMLReader();
$xml->open(__DIR__ . '/../../var/tmp/out.xml');
while ($xml->read() && $xml->name != 'UpdatedProducts');
$type = "update";
while ($xml->read() && $xml->name != 'ProductId');
while ($xml->name == 'ProductId') {
$id = $xml->readInnerXml();
if ( !empty($id) ) {
$this->saveToDb($xml->readInnerXml(), $type);
}
while ($xml->read() && $xml->name != 'ProductId'
&& $xml->name != 'RemovedProducts');
if ( $xml->name == 'RemovedProducts' ) {
$type = "remove";
while ($xml->read() && $xml->name != 'ProductId');
}
}
還有一種替代方法,使用我編寫的庫來包裝 XMLReader(在https://github.com/NigelRel3/XMLReaderReg )。 您必須下載它,因為還沒有作曲家版本。 但是將 XMLReaderReg.php 腳本復制到您的項目中
require_once "XMLReaderReg.php";
那么你可以使用...
$reader = new XMLReaderReg();
$reader->open(__DIR__ ."/../../var/tmp/out.xml");
$reader->process([
'.*/UpdatedProducts/ProductId' => function (SimpleXMLElement $data): void {
$this->saveToDb((string)$data, "update");
},
'.*/RemovedProducts/ProductId' => function (SimpleXMLElement $data): void {
$this->saveToDb((string)$data, "remove");
},
]);
$reader->close();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.