Parsing Atom with sabre/xml
Today sabre/xml 1.3 was released and it brings a few new features to make it even easier to parse many XML documents.
1.3 in particular brings a new value objects feature that makes it extremely straightforward to map specific XML elements to PHP objects bi-directionally.
Here’s an example, given the following xml document:
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Example Feed</title>
<link href="http://example.org/"/>
<updated>2003-12-13T18:30:02Z</updated>
<author>
<name>John Doe</name>
</author>
<id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
<entry>
<title>Atom-Powered Robots Run Amok</title>
<link href="http://example.org/2003/12/13/atom03"/>
<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
<updated>2003-12-13T18:30:02Z</updated>
<summary>Some text.</summary>
</entry>
</feed>
You can imagine that you might want to map this to a PHP object structure such as this:
<?php
namespace My\Atom;
class Feed {
public $title;
public $link = [];
public $updated;
public $author;
public $id;
public $entry = [];
}
class Author {
public $name;
public $email;
}
class Entry {
public $title;
public $link = [];
public $id;
public $updated;
public $summary;
}
To achieve this, all you need to do is call the following:
<?php
$service = new Sabre\Xml\Service();
$service->namespaceMap['http//www.w3.org/2005/Atom'] = 'atom';
$service->mapValueObject('{http://www.w3.org/2005/Atom}feed', 'My\Atom\Feed');
$service->mapValueObject('{http://www.w3.org/2005/Atom}author', 'My\Atom\Author');
$service->mapValueObject('{http://www.w3.org/2005/Atom}entry', 'My\Atom\Entry');
?>
In PHP 5.5 you replace the last 3 lines with:
<?php
$service->mapValueObject('{http://www.w3.org/2005/Atom}feed', Feed::class);
$service->mapValueObject('{http://www.w3.org/2005/Atom}author', Author:class);
$service->mapValueObject('{http://www.w3.org/2005/Atom}entry', Entry::class);
?>
Now you have a fully setup Service
class, and to parse the earlier XML document
all you have to do is call:
<?php
$feed = $service->parse($xml);
// Feed is an instance of My\Atom\Feed;
?>
Or if you already have an instance of My\Atom\Feed
and you want to turn it
into an XML document:
$xml = $service->writeValueObject($feed);
Value objects work by inspecting the class you are mapping, and simply matching the XML element name (in the same XML namespace) to the class property.
If you initialized a class property with an array, like this:
class Feed {
public $entry = [];
}
This automatically signals the parser that you feed may have more than one
entry
XML element and always turn it into an array.
This system has some limitations, as it doesn’t for example work with XML
attributes, but for those cases you can still fall back on the other facilities
to get access to those. If you paid attention so far, then yes this implies
that the earlier example was not complete: the <link>
elements would not have
correctly been parsed.
For a full demonstration of this, we’re releasing a sabre/xml-atom
package that’s basically a full Atom parser. This acts both as a real package
for parsing atom, as well as a demonstration of how you would go about this.
This package does parse everything correctly, and thankfully it’s fairly
simple.
Anyway, I’m pretty excited about this project. It’s the #1 hit on Packagist when searching for ‘XML’, and install rates have increased quite a bit. I would really like the package to become the de-facto solution for people needing to map PHP data-models to XML documents, and we’re slowly getting to a point where the package addresses many of people’s use-cases.
Next time you need to write or parse XML, I hope you take a look!
Comments
Jorge López Pérez •
Glad to see sabre/xml gets even better.
Keep up the good work, Evert!
Alessandro Pellizzari •
The sabre/xml-atom link is broken. :)
https://packagist.org/packa...
Evert •
Thanks, just fixed this!