Skip to content

kalmas/sabre-xml

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

90 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sabre/xml

Build Status

The sabre/xml library is a specialized xml reader and writer.

I often found myself repeating the same pattern for xml manipulation over and over. This library implements that pattern.

At it's heart, the library maps xml elements to PHP value objects.

The following assumptions are made:

  • XML namespaces are used everywhere.
  • XML is written and read sequentially.
  • All XML elements map to PHP classes and scalars.
  • Elements generally contain either just text, just sub-elements or nothing all.
  • Elements are represented by classes. A class has a serializeXml() and a deserializeXml() method.
  • Namespace prefixes must be completely ignored by an xml reader.

This is not your average XML library. The intention is not to make this super simple, but rather very powerful for complex XML applications.

Installation

This library requires PHP 5.4 and the XMLReader and XMLWriter extensions. Installation is done using composer.

The general composer instructions can be found on the composer website.

After that, just declare the sabre-xml dependency as follows:

"require" : {
    "sabre/xml" : "master-dev"
}

Then, run composer.phar update and you should be good.

Sample XML document

All the following examples use an Atom xml document. The document can be found here:

atom.xml

Note that this sample was taken from wikipedia.

Reading XML documents

To read an XML document, simply instantiate the reader class. The reader class is a subclass of PHP's XMLReader.

Simple example:

include __DIR__ . '/vendor/autoload.php';

use Sabre\XML;

$reader = new XML\Reader();
$reader->open('atom.xml');

$output = $reader->parse();
var_dump($output);

The parse method reads the entire file, and the resulting data is returned from that method.

The result will look like this: atom.parsed1.txt

Quite ugly indeed, but we'll get to cleaning that up later.

Writing XML documents

To write that same XML document, we use the Writer class.

$writer = new XML\Writer();
$writer->openMemory();
$writer->setIndent(true); // for pretty indentation
$writer->write( [$output] );

echo $writer->outputMemory();

The output will look like this: atom.written1.xml

Ugly you say? This library will inline xml namespaces everywhere, unless they are specified in advance:

$writer = new XML\Writer();
$writer->namespaceMap = [
    'http://www.w3.org/2005/Atom' => 'a',
];
$writer->openMemory();
$writer->setIndent(true); // for pretty indentation
$writer->write([$output]);

echo $writer->outputMemory();

The output looks pretty normal now: atom.written2.xml

Mapping XML elements

Normally when writing an Atom parser using this tool, there will be a number of elements that make sense to create using classes for.

A great example would be the entry element:

class AtomEntry {

  public $title;
  public $links;
  public $id;
  public $updated;
  public $summary;

  /* etc.. */

}

Similarly we'd also create an element for the entire feed:

class AtomFeed {

    public $title;
    public $subTitle;
    public $links;
    public $id;
    public $updated;

}

Lets start with a simple one though, that recurs in a bunch of places: link.

The link element can have a href, rel and type attribute. There's actually a bunch more if you're going for a full parser.

We'll just focus on those three though..

Our base class:

class AtomLink {

    public $href;
    public $rel;
    public $type;

}

Now the SabreXML additions:

use Sabre\XML;

class AtomLink implements XML\Element {

    public $href;
    public $rel;
    public $type;

    /**
     * The serialize method is called during xml writing.
     *
     * It should use the $writer argument to encode this object into XML.
     *
     * Important note: it is not needed to create the parent element. The
     * parent element is already created, and we only have to worry about
     * attributes, child elements and text (if any).
     *
     * @param XML\Writer $reader
     * @return void
     */
    public function serializeXml(XML\Writer $writer) {

        $writer->writeAttribute('href', $this->href);
        $writer->writeAttribute('rel', $this->rel);
        $writer->writeAttribute('type', $this->type);

    }

    /**
     * The deserialize method is called during xml parsing.
     *
     * This method is called statictly, this is because in theory this method
     * may be used as a type of constructor, or factory method.
     *
     * Often you want to return an instance of the current class, but you are
     * free to return other data as well.
     *
     * @param XML\Reader $reader
     * @return mixed
     */
    static public function deserializeXml(XML\Reader $reader) {

        $attributes = $reader->parseAttributes();

        $link = new self();
        foreach($attributes as $name=>$value) {
            if (property_exists($link,$name)) {
                $link->$name = $value;
            }
        }
        $reader->next();

        return $link;

    }

}

To automatically map all link elements to the new AtomLink class, register it on the reader:

$reader = new XML\Reader();
$reader->elementMap = [
    '{http://www.w3.org/2005/Atom}link' => 'AtomLink'
];
$reader->open('samples/atom.xml');

$output = $reader->parse();

When inspecting the output, the link element will now properly be replaced with our newly created object, and sending this back to the Writer will also work as expected.

Support

Head over to the SabreDAV mailing list for any questions.

Made at fruux

This library is being developed by fruux. Drop us a line for commercial services or enterprise support.

About

sabre/xml is the only XML library that you may not hate.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • PHP 100.0%