User Guide for FluentDOM
Bastian Feder
Thomas Weinert
Table of Contents
FluentDOM
FluentDOM ist a jQuery like fluent XML interface for the DOMDocument in PHP.
The idea was born in a workshop of Tobias Schlitt,
about the PHP XML extensions at the IPC Spring, in Berlin. He used this idea to show
XPath samples in the session.
Many thanks to the jQuery people for their work,
who did an exceptional job describing their interfaces and providing examples.
This saved us a lot of work.
We implemented most of the jQuery methods into FluentDOM, but there are
differences. Most important: we use XPath for expressions, not CSS selectors.
Since XPath is supported by the ext/xml extension, no extra parsing need
to be done. This should be faster processing the selectors and btw. it was
easier for us to implement. And as a nice topping it supports namespaces, too.
We implemented several php interfaces: Countable, IteratorAggregate,
SeekableIterator and RecursiveItrerator. Even ArrayAccess is supported.
Since FluentDOM works on XML documents, there is no method 'html()', but 'xml()'.
We support the string conversion using the magic __toString() method. It
will output the xml or html of the associated DOMDocument.
FluentDOM needs a document. If you do not provide a valid source, an empty one
will be created.
XPath do not only match element nodes (nodes with a tag name and maybe children),
but text nodes, too. Which implicitly enhances FluentDOM to support them.
To be able to write phpUnit Tests and develop FluentDOM a lot of examples
where written. Most of them are copied and adapted from or are deeply
inspired by the jQuery documentation. They are located in the 'examples'
folder and linked in the documentation.
Once again many thanks to the jQuery team.
Basic Usage
This section shows some of the basic operation.
FluentDOM Function
The FluentDOM function is a little wrapper. It creates a new FluentDOM object and loads
the source if it was provided. Possible sources are local XML or HTML files, XML or
HTML strings, DOMDocument DOMNode objects or a FluentDOM object. The following two samples
do the same.
Function Sample
<?php
require('../../FluentDOM.php');
$xmlFile = 'hello.xml';
// create and load
// find root node
// replace text content
Object Sample
<?php
require('../../FluentDOM.php');
$xmlFile = 'hello.xml';
// create object
// load file
echo $fd
Loading Data
You can use different ways to load a source into a FluentDOM object. The simplest way
is the FluentDOM function. But sometimes you need more. The default loaders
do not support URLs for example. But here are other ways:
Prepare A DOMDocument
The first way is to create a DOMDocument with custom logic and load this into the
FluentDOM object.
<?php
require('../../FluentDOM.php');
$xmlFile = 'hello.xml';
// prepare
$dom = new DOMDocument();
// load
Use The "document" Attribute
The FluentDOM has an read only attribute document. You can not
assign a value to this attribute directly. But you have access to all its methods and
attributes. This example uses DOMDocument::load() to load the XML file.
<?php
require('../../FluentDOM.php');
$xmlFile = 'hello.xml';
// create object
// use document attribute
$fd->document->load($xmlFile);
echo $fd
Custom Loader
FluentDOM allows to use custom loaders. This are simple classes implementing the
FluentDOMLoader interface. The interface has a single function load().
<?php
require('../../FluentDOM.php');
$xmlFile = 'hello.xml';
class ExampleLoader implements FluentDOMLoader {
// this could implement checks, error handling, ...
public function load($source, $type) {
$dom = new DOMDocument();
$dom->load($source);
return $dom;
}
}
// set loader(s)
$fd->setLoaders(
array(
new ExampleLoader
)
);
// load data using custom loader
$fd->load($xmlFile);
echo $fd
->find('/message')
->text('Hello World!');
Find Elements
The selector language used in FluentDOM is XPath, not CSS.
The method find() is used to select some elements from the document.
The selector parameter can be an XPath query or a callback function. XPath is a little
different from CSS but more powerful. More important it is implemented in the PHP extension
and faster any PHP based CSS selector implementation could be.
<?php
require('../../FluentDOM.php');
// find the document element <root>
//find the first <child> in <root>
//find the all <child>s anywhere in the document
foreach ($fd->find('//child') as $child) {
}
//find the <root> first then the second element in it
If the FluentDOM object already contains a selection, the selector will be used with each
selected element. If you this is the first action on the object, the document context is
used. You can force the document context with the second parameter.
Tutorial: Create A HTML Menu
Step 1: The Root Element
After the FluentDOM class is included an instance of it is created.
The default content type of a FluentDOM object is XML.
For this tutorial it is changed to HTML.
<?php
require('../../FluentDOM.php');
// create a FluentDOM
// we generate html
$fd->contentType = 'html';
//add the base menu node
// output the created document
echo $fd;
?>
The method append() appends elements to the current selection and returns a FluentDOM
with the appended elements. For later use this is assigned to a variable.
The example uses a string parameter to define the tag. This has to be some text
or a valid xml tree with a single root node.
The parameter can be an DOMNode, too. It is up to you which way you prefer.
<?php
require('../../FluentDOM.php');
// create a FluentDOM
// we generate html
$fd->contentType = 'html';
//add the base menu node
//create a dom element node
$fd->document->createElement('ul')
);
// output the created document
echo $fd;
?>
Step 2: Add Menu Items
A menu without items is useless. Using the chaining a listitem is appended and a hyperlink
into it. The link target is set using attr() and with text()
a link caption is provided. These two methods do not change the selected elements.
<?php
require('../../FluentDOM.php');
// create a FluentDOM
// we generate html
$fd->contentType = 'html';
//add the base menu node
// add the first menu item
$menu
// add the <li>
// add an <a> into the <li>
// set the href attribute of the <a>
->attr('href', '/sample.php')
// set the text content of the <a>
// add the second menu item
$menu
->attr('href', 'http://fluentdom.org')
// output the created document
echo $fd;
?>
Step 3: Improve Menu Items
Of course menu items can get classes. The method addClass() will
add a class to all selected elements.
Javascript links do not need to have a href attribute, attr() can set other
parameters like "id" or "onclick", too.
<?php
require('../../FluentDOM.php');
$fd->contentType = 'html';
// first menu item
$menu
->attr('href', '/sample.php')
// second menu item
$menu
->attr('href', 'http://fluentdom.org')
// add a class to the menu item <a>
// third menu item
$menu
// set the id attribute
->attr('id', 'alertSample')
// set the js ionclick handler
->attr('onclick', "alert('Hi');")
echo $fd;
?>
Step 4: Mark Menu Items
Often the first and/or the last element in a list need special threatment. To add classes
to them find() is used to select them and addClass()
to mark them.
FluentDOM uses XPath for selectors. In XPath "//" selects elements at
an level (from root) and "[1]" means that it has to be the first
element in it's parent node.
<?php
require('../../FluentDOM.php');
$fd->contentType = 'html';
$menu
->attr('href', '/sample.php')
->attr('href', 'http://fluentdom.org')
$menu
->attr('id', 'alertSample')
->attr('onclick', "alert('Hi');")
// mark first siblings
$fd
// find all first <li>
// set a class
// mark last siblings
$fd
// find all last <li>
->find('//li[position() = last()]')
// set a class
echo $fd;
?>
Step 5: Add A Submenu
In the last step a submenu is added. The submenu is appended to the <li> tag.
The method end() is used to jump to the previous selection.
<?php
require('../../FluentDOM.php');
$fd->contentType = 'html';
$menu
->attr('href', '/sample.php')
// remember the <li> so we cann add a submenu
$item = $menu
->attr('href', 'http://fluentdom.org')
// <a> is selected, jump to parent selection
// add the submenu
$subMenu =
$item->append('<ul/>');
// add the submenu item
$subMenu
->attr('href', 'http://nightly.fluentdom.org/documentation')
$menu
->attr('id', 'alertSample')
->attr('onclick', "alert('Hi');")
$fd
$fd
->find('//li[position() = last()]')
echo $fd;
?>
XPath 101
This is a little introduction into XPath. For further information see the links at the
end of this section.
Syntax elements
/ - Level separator
// - Multi-level separator
* - Element wildcard
@ - Attribute prefix
@* - Attribute wildcard
[ ] - Conditions
. - Current element
.. - Parent element
Simple XPath Selector Samples
-match the document element with the name "element-name".
/element-name
Match all direct childs of the document element with the name "child".
/*/child
Match all elements with name "a" and attribute "href" anywhere in the document.
//a[@href]
Match all elements with name "strong" that are child (direct or not) of the current
element.
.//strong
Match the first direct child of the current element.
*[1]
Match all elements with the name "div" in the current element with the attribute "id" set
to "sampleOne".
div[@id = "sampleOne"]
Functions
name() - Element name
local-name() - Element name wihtout namespace prefix
position() - Position in parent element (starts with 1)
last() - Position of last element
count(...) - Element count
normalize-space() - Normalize withespaces
concat() - Concatinate strings
contains() - Check if a string contains anther string
This is only a selection of functions uses in the following samples. XPath knows some more.