Announcing XMLegant for PHP

Update: I've also written a Python version of XMLegant

While SimpleXML has made reading XML relatively painless, no analogous tool exists for generating XML from scratch. To fill this need, I've created XMLegant (github, download latest as zip).

To see how XMLegant works, let's take a simple example. Suppose we want to generate the following document:

<books>
    <book>
        <title>Title 1</title>
        <author>Author 1</author>
        <isbn>isbn 1</isbn>
    </book>
    <book>
        <title>Title 2</title>
        <author>Author 2</author>
        <isbn>isbn 2</isbn>
    </book>
    <book>
        <title>Title 3</title>
        <author>Author 3</author>
        <isbn>isbn 3</isbn>
    </book>
</books>

We can use the following code:

$x = new XMLegant();

for($i=0;$i<5;$i++)
    $x->books->book()->title("Title $i")
                     ->author("Author $i")
                     ->isbn("isbn $i");

echo $x->toXML();

And to generate this document:

<a>
    <b>c</b>
    <b>d</b>
    <e>f</e>
    <b>g</b>
    <h>
        <i j="k"/>
        <l>
            <m/>
        </l>
        <n>o</n>
    </h>
<a>

We can use either:

$x = new XMLegant();

$x->a->b = 'c';
$x->a->b[] = 'd';
$x->a->e = 'f';
$x->a->b[] = 'g';
$x->a->h->i['j'] = 'k';
$x->a->h->l->m;
$x->a->h->n = 'o';

or:

$x = new XMLegant();

$x->a()
    ->b('c')
    ->b('d')
    ->e('f')
    ->b('g')
    ->h()
      ->i('j', 'k')
      ->l()
        ->m('')
        ->getParent()
      ->n('o');

How does this work?

XMLegant uses PHP's overloading and the SPL ArrayAccess interface. This allows interception of calls to unnamed functions or member variables as well as subscript access to the object. Using this, we can construct an XML access through manipulation of these anonymous calls.

Take this code:

$x = new XMLegant();
$x->a->b->c->d = 'e';
$x->a->b['f'] = 'g';
$x->toXML();

Which produces this:

<a>
    <b f="g">
        <c>
            <d>e</d>
        </c>
    </b>
</a>

This code first starts with the root object $x.

  1. $x->a is called, which looks for the most recently created a child. Since none exists, an a element is created and added as a child of $x.

  2. ->b is called, which looks for the most recently created b child of a. Since none exists, a b element is created and added as a child of a.
  3. ->c is called, which looks for the most recently created c child of b. Since none exists, a c element is created and added as a child of b.
  4. ->d is called, which looks for the most recently created d child of c. Since none exists, a d element is created and added as a child of c.
  5. d = 'e' is called, which assigns 'e' as a text node of d.

The second line finds the already created a element, finds the already created b child of a, and then sets the attribute f of b to 'g'.

Many more examples are available in demo.php. Also, XMLegant_Tests.php consists of a number of unit tests that may be useful as a guide.