XML / XPath using PHP

Having some spare time on my hands during the easter holyday, I decided to have a look at the PHP DOM XML extension and see how easy it was to play with. The result is a small XML based movie database where users can search and add movies.

First thing you will have to do is install the DOM XML extension, on my Mandrake this was done using urpmi php-domxml

Loading the XML document

The XML document containing the movie database is stored in a file, and it can be loading using the [domxml_open_file|object domxml_open_file ( string filename)] method which will return a DOM object that the rest of the code will use. Futhermore we need to create a new xpath parser object, so we can use xpath to query the DOM tree.


<?php
define
("XMLSOURCE""movies.xml");

// Load in xml data from file or create a new dom.
if (is_file(XMLSOURCE)) {
    
$doc domxml_open_file(XMLSOURCE);
} else {
    
$doc domxml_open_mem('<?xml version="1.0"?><movies></movies>');
}
$xpath $doc->xpath_new_context();
?>

XML document

The movie database XML documents structure will be as the shown in the example below. Currently only storing the movie title and url to the IMDB movie page.


<?xml version="1.0"?>
<movies
  <movie id="1">
    <title>Comming to America</title>
    <imdb>http://imdb.com/title/tt0094898/</imdb>
  </movie>
  <movie id="1">
    ...
  </movie>
</movies>

Listing movies in the XML document

When we have the XML document loaded we can easily extract information from it using xpath. In the same maner as you would normally write select statements for a SQL database, xpath allows you to select different parts of the DOM tree. In this example we will allow users to both show all movies and search for movies by entering parts of their title. If we were only to show all movies ("//movie") would work, but by using the below xpath expression we search for movie elements containing the search string in the title sub element.


<?php
// Case insensitive search.
$movies $xpath->xpath_eval("//movie[contains(translate(title,'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), '".strtoupper($_REQUEST["search"])."')]");
?>

Executing the xpath expression will give of a set of movie element nodes that we can extract information from and display. [DomElement->get_attribute|object DomElement->get_attribute ( string name)] will return the value of an attribute where as [DomNode->get_content|string DomNode->get_content ( void )] will return the content inside an element.


<?php
print "<strong>Movie list</strong><br>\n";
print 
"<table>\n";
foreach (
$movies->nodeset as $movie) {
    
$title "";
    
$imdb "";
    
$id $movie->get_attribute("id");

    foreach (
$movie->child_nodes() as $child) {
        if (
$child->node_name() == "title"$title $child->get_content();
        if (
$child->node_name() == "imdb"$imdb $child->get_content();
    }

    print 
"  <tr>\n";
    print 
"    <td><a href=\"$imdb\">$title</a></td>\n";
    print 
"  </tr>\n";
}
print 
"</table><br>\n";
print 
"Movies found: ".count($movies->nodeset)."\n";
?>

Adding movies to the database

Now that the script can display movies in the database, the next step is to allow users to add movies to the database.

To do this we need to extract the present maximum id number (id number of the last movie element in the document), increment it and assign it to the new movie element. Then set the child element and attributes and finally add it to the document and write it to the database file again.


<form action="<?php print $_SERVER["PHP_SELF"]; ?>" method="post">
    Title:<br>
    <input type="text" name="title" value="" size="50"><br><br>

    IMDB url:<br>
    <input type="text" name="imdb" value="" size="50"><br><br>

    <input type="submit">
</form>

<?php
// Check if input data validates
if (isset($_REQUEST["title"]) &&
    isset(
$_REQUEST["imdb"]) &&
    
preg_match("#[a-zA-Z ',:\.!&]+#"$_REQUEST["title"]) &&
    
preg_match("|^http://(www.)?imdb.com/title/[a-z0-9]+/?$|"$_REQUEST["imdb"])
) {
    
$_REQUEST["title"] = stripslashes($_REQUEST["title"]);
    
$movies $xpath->xpath_eval("//movie");

    
// Extract last entry
    
$lastmovie $xpath->xpath_eval("//movie[last()]");
    if (
count($lastmovie->nodeset) > 0) {
        
$lastmovie $lastmovie->nodeset[0];
        
$id $lastmovie->get_attribute("id") + 1;
    } else {
        
$id 1;
    }

    
// Create a new movie element
    
$root $doc->document_element();
    
$movie $doc->create_element("movie");
    
$titlenode $doc->create_element("title");
    
$title $doc->create_text_node($_REQUEST["title"]);
    
$titlenode->append_child($title);
    
$imdbnode $doc->create_element("imdb");
    
$imdb $doc->create_text_node($_REQUEST["imdb"]);
    
$imdbnode->append_child($imdb);
    
$movie->set_attribute("id"$id);
    
$movie->append_child($titlenode);
    
$movie->append_child($imdbnode);

    
// Add movie to tree
    
$root->append_child($movie);

    
// Save xml string to disk
    
$doc->dump_file(XMLSOURCE);
}
?>

End

I have placed two script version, one that allow users to list and search the movie database, the second contains the movie adding functionality. Finally I have placed a small sample file movies.xml with 40-50 movies to test on.

Unfortunality the web server hosting this site has not compiled in support for the XML DOM extension so I can not supply a running demo.