簡體   English   中英

PHP 解析巨大的 XML 文件

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM