PHP Разбор огромного XML файла - PullRequest
0 голосов
/ 02 марта 2020

У меня есть XML документ, подобный этому, это файл> 400 МБ.

Моя проблема в том, что я не могу заставить XMLReader не работать с ограничением памяти, у меня есть сервер 512 МБ 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, оба должны быть в l oop, как решить проблему - если возможно, поставить больше оперативной памяти на сервер (или 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');
      }
    }

1 Ответ

3 голосов
/ 02 марта 2020

Вместо того, чтобы использовать Simple XML для извлечения всех узлов в <UpdatedProducts>, вы можете вложить один и тот же код, чтобы он читался внутри этого узла для `узлов. Это будет означать, что внутренний l oop будет получать по 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');
}

Для обоих типов я попытался уменьшить его до одного l oop. Это не идеально, но, кажется, работает ...

$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). Вам придется скачать его, так как версии composer пока нет. Но скопируйте скрипт 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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...