WebReference.com - Part 2 of Chapter 10 from Professional PHP4 XML, from Wrox Press Ltd (3/4) | WebReference

WebReference.com - Part 2 of Chapter 10 from Professional PHP4 XML, from Wrox Press Ltd (3/4)

To page 1To page 2current pageTo page 4
[previous] [next]

Professional PHP4 XML, Chapter 10: Putting It Together

Using SAX To Modify a Document

If we could use SAX to transform a document we can surely use it to modify it. In fact, the approach used to modify a document with SAX should be the same as used when transforming the document - write filters that perform some basic transformations, and then chain the filters if needed, to perform a more complex modification. When modifying a document, chaining will be less frequent but the filter approach is good because each transformation is a separate class that can be maintained and updated easily.

Using SAX filters we can write the following filter to add a product to our XML cart example:

class FilterAddProduct extends AbstractFilter 
{
    var $products = Array();
    function AddProduct($id, $name, $desc, $quantity, $unit_price) 
    {
        $elem = Array();
        $elem["pid"] = $id;
        $elem["name"] = $name;
        $elem["desc"] = $desc;
        $elem["quantity"] = $quantity;
        $elem["unit_price"] = $unit_price;
        $this->products[] = $elem;
    }
    function StartElementHandler($name, $attribs) 
    {
        $this->listener->StartElementHandler($name, $attribs);
        if ($name == "products") {
            // Perform insertions
            foreach ($this->products as $product) {
                $this->listener->StartElementHandler("product", 
                                          Array("pid" => $product["pid"]));
                // Name
                $this->listener->StartElementHandler("name", Array());
                $this->listener->CharacterDataHandler($product["name"]);
                $this->listener->EndElementHandler("name");
                // Desc
                $this->listener->StartElementHandler("desc", Array());
                $this->listener->CharacterDataHandler($product["desc"]);
                $this->listener->EndElementHandler("desc");
                // Quantity
                $this->listener->StartElementHandler("quantity", Array());
                $this->listener->CharacterDataHandler($product["quantity"]);
                $this->listener->EndElementHandler("quantity");
                // Unit_price
                $this->listener->StartElementHandler("unit_price", Array());
                $this->
                     listener->characterDataHandler($product["unit_price"]);
                $this->listener->EndElementHandler("unit_price");
                $this->listener->EndElementHandler("product");
            }
        }
    }
    function EndElementHandler($name) 
    {
        $this->listener->EndElementHandler($name);
    }
    function CharacterDataHandler($data) 
    {
        $this->listener->CharacterDataHandler($data);
    }
}

This filter lets us prepare the filter to add many products when parsing the document; we store the products to be added in a member array of the filter. When parsing the document, if the filter sees that a <products> element has started, it traverses the list of products to be added, generating the events needed to insert the elements on the XML file.

We can use the filter this way:

$f1 = new ExpatParser("cart.xml");
$f1->ParserSetOption(XML_OPTION_CASE_FOLDING, 0);
$f2 = new FilterAddProduct();
$f2->AddProduct("15", "foo", "foo bar", "1", "6");
$f2->AddProduct("17", "Goo", "GOO", "2", "16");
$f3 = new FilterOutput();
$f2->SetListener($f3);
$f1->SetListener($f2);
$f1->Parse();

To remove elements from an XML document using SAX, we can use the following filter:

class FilterRemoveProduct extends AbstractFilter 
{
    var $products = Array();
    var $ignore = 0;
    function RemoveProduct($id) 
    {
        $this->products[] = $id;
    }
    function StartElementHandler($name, $attribs) 
    {
        if ($name == "product") {
           if (in_array($attribs["pid"], $this->products)) {
               $this->ignore = 1;
           } else {
               $this->listener->StartElementHandler($name, $attribs);
           }
       } 
       if (!$this->ignore) {
           $this->listener->StartElementHandler($name, $attribs);
       }
    }
    function EndElementHandler($name) 
    {
        if (!$this->ignore) {
            $this->listener->EndElementHandler($name);
        } else {
            if ($name == "product") {
                $this->ignore = 0;
            }
        }
    }
    function CharacterDataHandler($data) 
    {
        if (!$this->ignore) {
            $this->listener->CharacterDataHandler($data);
        }
    }
} 

This filter can also store IDs of products to be deleted; we can use the removeProduct() method as many times as we want before parsing the document, preparing our chain to remove as many elements as we want.

We use a flag in the filter, and we turn the flag on each time a <product> element with an ID to be deleted starts. While the flag is on, we don't propagate events to the following filter, thus eliminating the element's text and subelements. When a <product> element ends, we turn the flag off (if it was on), to let further products pass to the filter. Ignoring SAX event propagation is a common way to eliminate elements from an XML document using SAX.

In the following example we add some products to the cart and then remove some others:

$f1 = new ExpatParser("cart.xml");
$f1->ParserSetOption(XML_OPTION_CASE_FOLDING, 0);
$f2 = new FilterAddProduct();
$f2->AddProduct("15", "foo", "foo bar", "1", "6");
$f2->AddProduct("17", "Goo", "GOO", "2", "16");
$f3 = new FilterRemoveProduct();
$f3->RemoveProduct("13");
$f3->RemoveProduct("15");
$f4 = new FilterOutput();
  
$f3->SetListener($f4);
$f2->SetListener($f3);
$f1->SetListener($f2);
$f1->Parse();

SAX scales very well for huge documents. Its resource consumption is constant for documents of any size, and it is fast. However, every modification needs coding, and at times it is complex. The same modifications are easy using DOM.


To page 1To page 2current pageTo page 4
[previous] [next]

Created: August 19, 2002
Revised: August 19, 2002

URL: http://webreference.com/programming/php/php4xml/chap10/2/3.html