3
\$\begingroup\$

I am using XMLReader and avoided SimpleXML because I must handle a huge file and for memory issues, SimpleXML is not the ideal solution. However, even I coded the below script in SimpleXML, it gives me the result really much faster.

Because speed and memory is a must for this project, is there any way to speed up this code? What it does, it filters the books and show those that contains the word jQuery.

error_reporting(~0);

$xml = <<<EOD
<?xml version="1.0" encoding="ISO-8859-1"?>
<library>
    <book isbn="781">
        <name>SCJP 1.5</name>
        <info>Sun Certified Java Programmer book</info>
    </book>
    <book isbn="194">
        <name>jQuery is Awesome!</name>
        <info>jQuery Reference Book</info>
    </book> 
    <book isbn="199">
        <name>jQuery 101</name>
        <info>All you need to know about jQuery</info>
    </book> 
</library>
EOD;

$file = 'data://text/plain;base64,'.base64_encode($xml);

class XMLBookIterator implements iterator
{
    private $file;
    private $reader;
    private $state;
    private $key = 0;
    private $book;
    private $valid;
    public function __construct($file)
    {
        $this->file = $file;
    }
    public function current()
    {
        return $this->book;
    }
    public function key()
    {
        return $this->key;
    }
    public function next()
    {
        $reader = $this->reader;
        while ($next = $reader->read())
        {
            switch ($reader->nodeType)
            {
                case (XMLREADER::ELEMENT):
                    $case = $reader->localName.'|'.$this->state;
                    switch($case)
                    {
                        case 'library|0':
                            break;
                        case 'book|1':
                            $this->book = new stdClass;
                            $this->book->isbn = $reader->getAttribute('isbn');                
                            break;
                        case 'name|2':
                            $this->book->name = $reader->readInnerXML();
                            break;
                        case 'info|3':
                            $this->book->info = $reader->readInnerXML();
                            $this->state=0;
                            break;
                        default:
                            throw new Exception(sprintf('Invalid State: %s.', $case));
                    }
                    $this->state++;
                    if ($this->state === 1) break 2;
            }
        }
        $this->valid = $next;
        $this->key++;
    }
    public function rewind()
    {
        if ($this->reader) $this->reader->close();
        $this->reader = new XMLReader();
        $this->reader->open($this->file);
        $this->state = 0;
        $this->next(); // move to first element or invalidate
        if ($this->valid) $this->next(); // get first book
        $this->key = 0;
    }
    public function valid()
    {
        return $this->valid;
    }
}

class BookFilterIterator extends FilterIterator
{
    private $term;
    public function __construct($iterator, $term)
    {
        parent::__construct($iterator);
        $this->term = $term;
    }
    public function accept() {
        $book = parent::current();
        $name = $book->name;
        return false !== stripos($name, $this->term);
    }
}

// filtered books:

$books = new XMLBookIterator($file);

$filtered = new BookFilterIterator($books, 'jQuery');

foreach($filtered as $key => $book)
{
    echo 'book (', $key, '):', "\n";
    print_r($book);
}
\$\endgroup\$
2
\$\begingroup\$

Your code seems quite tidy and easy to understand, congratulations. But maybe you can check for built in functionality. XPath can search items for you:

http://www.php.net/manual/en/simplexmlelement.xpath.php
http://www.w3schools.com/xpath/xpath_syntax.asp

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.