Read XML files efficiently in PHP

PHP

Over the past 4 years I have worked with many XML providers (interconnecting B2B applications) and some of these providers distribute big XML files, some of them have more than 2GB, as you may already suspect we should not dump 2GB of information into memory because PHP will generate a memory overflow fatal error and your application will not be able to recover by itself, so in this post I will explains how to solve this problem, using:

* Generators: Generators provide an easy way to build simple iterators without implementing the Iterator interface for more information read: New features of PHP 5.5

* XMLReader: is a PHP implementation on the shoulder of libxml that allows to read XML files effeciently since it acts as a forward cursor.

* SimpleXMLElement: Create an XML object from an XML string.

Using these 3 concepts you will achieve better performance and scalability in your application, the logic would be:

1. Create the XMLReader object from the XML file you want to analyze.

2. Find the nodes with which we want to work.

3. : Create SimpleXMLElement object from the found node, as the node found is a small subset of all available information (in XML) does not represent an excessive memory consumption.

4. Return a generator. The generator will load into memory just a node of information.

Precedural implementation

Fork me on Github
<?php
/**
 * Get an XML representation from an XML node using XMLReader, Generator and SimpleXMLElement
 *
 * @param XMLReader $reader
 * @param string $path XML Path of the node
 * @return Generator
 */
function read(XMLReader $reader, $path) {
    // Set the path traversed by the reader
    $pathNode = '';
    // Start to read from the first node
    while ($reader->read()) {
        // Name and type of the current node
        $nodeName = $reader->name;
        $nodeType = $reader->nodeType;
        /**
         * Checks if the node is a "start element"
         * @see https://secure.php.net/manual/es/class.xmlreader.php
         */
        if (XMLReader::ELEMENT == $nodeType) {
            if (empty($pathNode) ) {
                $pathNode = $nodeName;
            } else {
                $newPath = implode('/', [$pathNode, $nodeName]);
                /**
                 * Add the name of the node to the traversed path
                 */
                if (false !== strpos($path, $newPath)) {
                    $pathNode = $newPath;
                }
            }
            // Compare traversed path with current node
            if ($pathNode == $path) {
                // Delete the node name from the traversed path
                $pathNode = preg_replace("/\/?{$nodeName}$/", '', $pathNode);
                /**
                 * Get the XML string representation from the found node, node tags are included,
                 * SimpleXMLElement object is created and returns a Generator
                 */
                yield (new SimpleXMLElement($reader->readOuterXML()));
            }
        }
    }
}

XML example

Fork me on Github
<?xml version="1.0" encoding="UTF-8"?>
<records>
  <record>
    <Name>Nicole</Name>
    <Company>Ac Mattis LLC</Company>
    <Address>Ap #823-8881 Adipiscing Avenue</Address>
    <City>Fontanellato</City>
    <Country>Puerto Rico</Country>
    <Phone>(444) 834-6922</Phone>
    <Geo>5.84145, -13.30146</Geo>
    <Description>auctor ullamcorper, nisl arcu iaculis enim, sit amet ornare lectus justo eu arcu. Morbi sit amet massa. Quisque porttitor eros nec tellus. Nunc lectus pede, ultrices a, auctor non, feugiat nec, diam. Duis mi enim, condimentum eget, volutpat ornare, facilisis eget, ipsum. Donec sollicitudin adipiscing ligula. Aenean gravida nunc sed pede. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin vel arcu eu odio tristique pharetra. Quisque ac libero nec ligula consectetuer rhoncus. Nullam velit dui, semper et, lacinia vitae, sodales at, velit. Pellentesque ultricies dignissim lacus. Aliquam rutrum lorem ac risus. Morbi metus. Vivamus euismod urna. Nullam lobortis quam a felis ullamcorper viverra. Maecenas iaculis aliquet diam. Sed diam lorem, auctor quis, tristique ac, eleifend vitae, erat. Vivamus nisi. Mauris nulla. Integer urna. Vivamus molestie dapibus ligula. Aliquam erat volutpat. Nulla dignissim. Maecenas ornare egestas ligula. Nullam feugiat placerat velit. Quisque varius. Nam porttitor scelerisque neque. Nullam nisl. Maecenas malesuada fringilla est. Mauris eu turpis. Nulla aliquet. Proin velit. Sed malesuada augue ut lacus. Nulla tincidunt, neque vitae semper egestas, urna justo faucibus lectus, a sollicitudin orci sem eget massa. Suspendisse eleifend. Cras sed leo. Cras vehicula aliquet libero. Integer in magna. Phasellus dolor elit, pellentesque a, facilisis non, bibendum sed, est. Nunc laoreet lectus quis massa. Mauris vestibulum, neque sed dictum eleifend, nunc risus varius orci, in consequat enim diam vel arcu. Curabitur ut odio vel est tempor bibendum. Donec felis orci, adipiscing non, luctus sit amet, faucibus ut, nulla. Cras eu tellus eu augue porttitor interdum. Sed auctor odio a purus. Duis elementum, dui quis accumsan convallis, ante lectus convallis est, vitae sodales nisi magna sed dui. Fusce aliquam, enim nec tempus scelerisque, lorem ipsum sodales purus, in molestie tortor nibh sit amet orci. Ut sagittis lobortis mauris. Suspendisse aliquet molestie tellus. Aenean egestas hendrerit neque. In ornare sagittis felis. Donec tempor, est ac mattis semper, dui lectus rutrum urna, nec luctus felis purus ac tellus. Suspendisse sed dolor. Fusce mi lorem, vehicula et, rutrum eu, ultrices sit amet, risus. Donec nibh enim, gravida sit amet, dapibus id, blandit at, nisi. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin vel nisl. Quisque fringilla euismod enim. Etiam gravida molestie arcu. Sed eu nibh vulputate mauris sagittis placerat. Cras dictum ultricies ligula. Nullam enim. Sed nulla ante, iaculis nec, eleifend non, dapibus rutrum, justo. Praesent luctus. Curabitur egestas nunc sed libero. Proin sed turpis nec mauris blandit mattis. Cras eget nisi dictum augue malesuada malesuada. Integer id magna et ipsum cursus vestibulum. Mauris magna. Duis dignissim tempor arcu. Vestibulum ut eros non enim commodo hendrerit. Donec porttitor tellus non magna. Nam ligula elit, pretium et, rutrum non, hendrerit id, ante. Nunc mauris sapien, cursus in, hendrerit consectetuer, cursus et, magna. Praesent interdum ligula eu enim. Etiam imperdiet dictum magna. Ut tincidunt orci quis lectus. Nullam suscipit, est ac facilisis facilisis, magna tellus faucibus leo, in lobortis tellus justo sit amet nulla. Donec non justo. Proin non massa non ante bibendum ullamcorper. Duis cursus, diam at pretium aliquet, metus urna convallis erat, eget tincidunt dui augue eu tellus. Phasellus elit pede, malesuada vel, venenatis vel, faucibus id, libero. Donec consectetuer mauris id sapien. Cras dolor dolor, tempus non, lacinia at, iaculis quis, pede. Praesent eu dui. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean eget magna. Suspendisse tristique neque venenatis lacus. Etiam bibendum fermentum metus. Aenean sed pede nec ante blandit viverra. Donec tempus, lorem fringilla ornare placerat, orci lacus vestibulum lorem, sit amet ultricies sem magna nec quam. Curabitur vel lectus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec dignissim magna a tortor. Nunc commodo auctor velit. Aliquam nisl. Nulla eu neque pellentesque massa lobortis ultrices. Vivamus rhoncus. Donec est. Nunc ullamcorper, velit in aliquet lobortis, nisi nibh lacinia orci, consectetuer euismod est arcu ac orci. Ut semper pretium neque. Morbi quis urna. Nunc quis arcu vel quam dignissim pharetra. Nam ac nulla. In tincidunt congue turpis. In condimentum. Donec at arcu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae Donec tincidunt. Donec vitae erat vel pede blandit congue. In scelerisque scelerisque dui. Suspendisse ac metus vitae velit egestas lacinia. Sed congue, elit sed consequat auctor, nunc nulla vulputate dui, nec tempus mauris erat eget ipsum. Suspendisse sagittis. Nullam vitae diam. Proin dolor. Nulla semper tellus id nunc interdum feugiat. Sed nec metus facilisis lorem tristique aliquet. Phasellus fermentum convallis ligula. Donec luctus aliquet odio. Etiam ligula tortor, dictum eu, placerat eget, venenatis a, magna. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Etiam laoreet, libero et tristique pellentesque, tellus sem mollis dui, in sodales elit erat vitae risus. Duis a mi fringilla mi lacinia mattis. Integer eu lacus. Quisque imperdiet, erat nonummy ultricies ornare, elit elit fermentum risus, at fringilla purus mauris a nunc. In at pede. Cras vulputate velit eu sem. Pellentesque ut ipsum ac mi eleifend egestas. Sed pharetra, felis eget varius ultrices, mauris ipsum porta elit, a feugiat tellus lorem eu metus. In lorem. Donec elementum, lorem ut aliquam iaculis, lacus pede sagittis augue, eu tempor erat neque non quam. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aliquam fringilla cursus purus. Nullam scelerisque neque sed sem egestas blandit. Nam nulla magna, malesuada vel, convallis in, cursus et, eros. Proin ultrices. Duis volutpat nunc sit amet metus. Aliquam erat volutpat. Nulla facilisis. Suspendisse commodo tincidunt nibh. Phasellus nulla. Integer vulputate, risus a ultricies adipiscing, enim mi tempor lorem, eget mollis lectus pede et risus. Quisque libero lacus, varius et, euismod et, commodo at, libero. Morbi accumsan laoreet ipsum. Curabitur consequat, lectus sit amet luctus vulputate, nisi sem semper erat, in consectetuer ipsum nunc id enim. Curabitur massa. Vestibulum accumsan neque et nunc. Quisque ornare tortor at risus. Nunc ac sem ut dolor dapibus gravida. Aliquam tincidunt, nunc ac mattis ornare, lectus ante dictum mi, ac mattis velit justo nec ante. Maecenas mi felis, adipiscing fringilla, porttitor</Description>
  </record>
  <record>
    <Name>Brady</Name>
    <Company>Nullam Enim Institute</Company>
    <Address>818-4904 Lectus Av.</Address>
    <City>Jennersdorf</City>
    <Country>Virgin Islands, British</Country>
    <Phone>(583) 930-1188</Phone>
    <Geo>-24.42546, 145.2476</Geo>
    <Description>et netus et malesuada fames ac turpis egestas. Fusce aliquet magna a neque. Nullam ut nisi a odio semper cursus. Integer mollis. Integer tincidunt aliquam arcu. Aliquam ultrices iaculis odio. Nam interdum enim non nisi. Aenean eget metus. In nec orci. Donec nibh. Quisque nonummy ipsum non arcu. Vivamus sit amet risus. Donec egestas. Aliquam nec enim. Nunc ut erat. Sed nunc est, mollis non, cursus non, egestas a, dui. Cras pellentesque. Sed dictum. Proin eget odio. Aliquam vulputate ullamcorper magna. Sed eu eros. Nam consequat dolor vitae dolor. Donec fringilla. Donec feugiat metus sit amet ante. Vivamus non lorem vitae odio sagittis semper. Nam tempor diam dictum sapien. Aenean massa. Integer vitae nibh. Donec est mauris, rhoncus id, mollis nec, cursus a, enim. Suspendisse aliquet, sem ut cursus luctus, ipsum leo elementum sem, vitae aliquam eros turpis non enim. Mauris quis turpis vitae purus gravida sagittis. Duis gravida. Praesent eu nulla at sem molestie sodales. Mauris blandit enim consequat purus. Maecenas libero est, congue a, aliquet vel, vulputate eu, odio. Phasellus at augue id ante dictum cursus. Nunc mauris elit, dictum eu, eleifend nec, malesuada ut, sem. Nulla interdum. Curabitur dictum. Phasellus in felis. Nulla tempor augue ac ipsum. Phasellus vitae mauris sit amet lorem semper auctor. Mauris vel turpis. Aliquam adipiscing lobortis risus. In mi pede, nonummy ut, molestie in, tempus eu, ligula. Aenean euismod mauris eu elit. Nulla facilisi. Sed neque. Sed eget lacus. Mauris non dui nec urna suscipit nonummy. Fusce fermentum fermentum arcu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae Phasellus ornare. Fusce mollis. Duis sit amet diam eu dolor egestas rhoncus. Proin nisl sem, consequat nec, mollis vitae, posuere at, velit. Cras lorem lorem, luctus ut, pellentesque eget, dictum placerat, augue. Sed molestie. Sed id risus quis diam luctus lobortis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Mauris ut quam vel sapien imperdiet ornare. In faucibus. Morbi vehicula. Pellentesque tincidunt tempus risus. Donec egestas. Duis ac arcu. Nunc mauris. Morbi non sapien molestie orci tincidunt adipiscing. Mauris molestie pharetra nibh. Aliquam ornare, libero at auctor ullamcorper, nisl arcu iaculis enim, sit amet ornare lectus justo eu arcu. Morbi sit amet massa. Quisque porttitor eros nec tellus. Nunc lectus pede, ultrices a, auctor non, feugiat nec, diam. Duis mi enim, condimentum eget, volutpat ornare, facilisis eget, ipsum. Donec sollicitudin adipiscing ligula. Aenean gravida nunc sed pede. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin vel arcu eu odio tristique pharetra. Quisque ac libero nec ligula consectetuer rhoncus. Nullam velit dui, semper et, lacinia vitae, sodales at, velit. Pellentesque ultricies dignissim lacus. Aliquam rutrum lorem ac risus. Morbi metus. Vivamus euismod urna. Nullam lobortis quam a felis ullamcorper viverra. Maecenas iaculis aliquet diam. Sed diam lorem, auctor quis, tristique ac, eleifend vitae, erat. Vivamus nisi. Mauris nulla. Integer urna. Vivamus molestie dapibus ligula. Aliquam erat volutpat. Nulla dignissim. Maecenas ornare egestas ligula. Nullam feugiat placerat velit. Quisque varius. Nam porttitor scelerisque neque. Nullam nisl. Maecenas malesuada fringilla est. Mauris eu turpis. Nulla aliquet. Proin velit. Sed malesuada augue ut lacus. Nulla tincidunt, neque vitae semper egestas, urna justo faucibus lectus, a sollicitudin orci sem eget massa. Suspendisse eleifend. Cras sed leo. Cras vehicula aliquet libero. Integer in magna. Phasellus dolor elit, pellentesque a, facilisis non, bibendum sed, est. Nunc laoreet lectus quis massa. Mauris vestibulum, neque sed dictum eleifend, nunc risus varius orci, in consequat enim diam vel arcu. Curabitur ut odio vel est tempor bibendum. Donec felis orci, adipiscing non, luctus sit amet, faucibus ut, nulla. Cras eu tellus eu augue porttitor interdum. Sed auctor odio a purus. Duis elementum, dui quis accumsan convallis, ante lectus convallis est, vitae sodales nisi magna sed dui. Fusce aliquam, enim nec tempus scelerisque, lorem ipsum sodales purus, in molestie tortor nibh sit amet orci. Ut sagittis lobortis mauris. Suspendisse aliquet molestie tellus. Aenean egestas hendrerit neque. In ornare sagittis felis. Donec tempor, est ac mattis semper, dui lectus rutrum urna, nec luctus felis purus ac tellus. Suspendisse sed dolor. Fusce mi lorem, vehicula et, rutrum eu, ultrices sit amet, risus. Donec nibh enim, gravida sit amet, dapibus id, blandit at, nisi. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin vel nisl. Quisque fringilla euismod enim. Etiam gravida molestie arcu. Sed eu nibh vulputate mauris sagittis placerat. Cras dictum ultricies ligula. Nullam enim. Sed nulla ante, iaculis nec, eleifend non, dapibus rutrum, justo. Praesent luctus. Curabitur egestas nunc sed libero. Proin sed turpis nec mauris blandit mattis. Cras eget nisi dictum augue malesuada malesuada. Integer id magna et ipsum cursus vestibulum. Mauris magna. Duis dignissim tempor arcu. Vestibulum ut eros non enim commodo hendrerit. Donec porttitor tellus non magna. Nam ligula elit, pretium et, rutrum non, hendrerit id, ante. Nunc mauris sapien, cursus in, hendrerit consectetuer, cursus et, magna. Praesent interdum ligula eu enim. Etiam imperdiet dictum magna. Ut tincidunt orci quis lectus. Nullam suscipit, est ac facilisis facilisis, magna tellus faucibus leo, in lobortis tellus justo sit amet nulla. Donec non justo. Proin non massa non ante bibendum ullamcorper. Duis cursus, diam at pretium aliquet, metus urna convallis erat, eget tincidunt dui augue eu tellus. Phasellus elit pede, malesuada vel, venenatis vel, faucibus id, libero. Donec consectetuer mauris id sapien. Cras dolor dolor, tempus non, lacinia at, iaculis quis, pede. Praesent eu dui. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean eget magna. Suspendisse tristique neque venenatis lacus. Etiam bibendum fermentum metus. Aenean sed pede nec ante blandit viverra. Donec tempus, lorem fringilla ornare placerat, orci lacus vestibulum lorem, sit amet ultricies sem magna nec quam. Curabitur vel lectus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec dignissim magna a tortor. Nunc commodo auctor velit. Aliquam nisl. Nulla eu neque pellentesque massa lobortis ultrices. Vivamus rhoncus. Donec est. Nunc ullamcorper, velit in aliquet lobortis, nisi nibh lacinia orci, consectetuer euismod est arcu ac orci. Ut semper</Description>
  </record>
</records>

Using the reader

Fork me on Github
<?php
// Create the reader
$reader = new XMLReader();
// Open the reader passing the XML path
if ($reader->open(__DIR__ . '/data.xml', LIBXML_NOBLANKS|LIBXML_COMPACT)) {
    $i = 0;
    /**
     * Iterate over the found values, this is possible because
     * the Generator class implements the Iterator interface.
     * @var SimpleXMLElement $node
     */
    foreach (read($reader, 'records/record') as $node) {
        echo ++$i, ' - ', $node->Name,', ', $node->Country, PHP_EOL;
    }
    // Also it is possible to specify an internal node
    foreach (read($reader, 'records/record/Name') as $node) {
        echo ++$i, ' - ', $node, PHP_EOL;
    }
} else {
    echo "Failed to open";
}

Object oriented implementation

Fork me on Github
<?php
namespace Util\XML;

use XMLReader;
use SimpleXMLElement;
use InvalidArgumentException

/**
 * This class allows to traverse an XML file in efficient way.
 *
 * @package Util\XML
 *
 */
class Reader
{
    /**
     * @property XMLReader $reader
     */
    private $reader;

    /**
     * Create a new instance of the Reader
     *
     * @param string $file The XML file full path
     */
    public function __construct($file)
    {
        if (!is_readable($file)) {
            throw new InvalidArgumentException("Failed to open file:
                {$file} Permission denied " . __CLASS__ . ' ' .
                __FUNCTION__ . ' method');
        }

        $this->reader = new XMLReader();
        // Open the reader
        $this->reader->open($file, LIBXML_NOBLANKS|LIBXML_COMPACT);
    }

    /**
     * Get an XML representation from an XML node using XMLReader, Generator and SimpleXMLElement
     *
     * @path string $path node XPath 
     * @return Generator
     */ 
    public function read($path)
    {
         if (empty($path)) {
            throw new InvalidArgumentException("Node path can't be empty" .
                __CLASS__ . ' ' .
                __FUNCTION__ . ' method');
        }

        // Set the path traversed by the reader
        $pathNode = '';
        // Start to read from the first node
        while ($this->reader->read()) {
            // Name and type of the current node
            $nodeName = $this->reader->name;
            $nodeType = $this->reader->nodeType;
           /**
            * Checks if the node is a "start element"
            * @see https://secure.php.net/manual/es/class.xmlreader.php
            */
            if (XMLReader::ELEMENT == $nodeType) {
                if (empty($pathNode) ) {
                    $pathNode = $nodeName;
                } else {
                    $newPath = implode('/', [$pathNode, $nodeName]);
                    // Add the name of the node to the traversed path
                    if (false !== strpos($path, $newPath)) {
                        $pathNode = $newPath;
                    }
                }
                // Compare traversed path with current node
                if ($pathNode == $path) {
                    // Delete the node name from the traversed path
                    $pathNode = preg_replace("/\/?{$nodeName}$/", '', $pathNode);
                    /**
                     * Get the XML string representation from the found node, node tags are included,
                     * SimpleXMLElement object is created and returns a Generator
                     */
                    yield (new SimpleXMLElement($this->reader->readOuterXML()));
                }
            }
        }
    }
}

Using the Reader class

Fork me on Github
<?php
use Util\XML\Reader as Reader;
 $i = 0;
/**
 * Iterate over the found values, this is possible because
 * the Generator class implements the Iterator interface.
 * @var SimpleXMLElement $node
 */
foreach ((new Reader(__DIR__ . '/data.xml'))->read('records/record') as $node) {
    echo ++$i, ' - ', $node->Name,', ', $node->Country, PHP_EOL;
}

Note: This implementation has been tested with an XML file of 875MB of size, generated with: generatedata.com




Leave a Comment

Your email address will not be published. Required fields are marked *