xsl-list
[Top] [All Lists]

Re: Editing XML dynamically and using two input XML files

2003-05-30 00:48:59
thei wrote:
Hello,

I have two questions.

Firstly, I want to be able to take two XML files as input, and transform
them into one XML (actually XHTML) output file. I'm using XSLT
(specifically Sablotron under PHP/Apache/Linux). Is this possible?

Secondly, I wish to be able to grab a specific section of an XML file,
load it into a web-form, allow it to be edited, and then save it back to
the original XML file in the same location. It has to be UTF-8
throughout.


<snip/>

I discovered that there was no DOM method in PHP for "grafting" an XML fragment into another XML document. I also got no response from comp.lang.php on the issue. I imagine it is easy to do in XSLT but I wanted to import XML fragements into my DOM object instance. Basically I wrote a wrapper class for PHP's DOM functionality and included a method called ImportChildFrag($ndStub,$ndNew)

check out the last three methods in this class definition. (this code will eventually be release when it is actually tested). Generally you would use this class by inheriting it.



<?php
include_once "class_xmlErrorLog.php";

                                        // Each of the following constants
                                        // represent different ways to
                                        // interperet the first constructor
                                        // argument.

                                        // creates a new dom document from
                                        // scratch
define("DOMDOC_AS_NEW",10);
// creates a dom object from file URI.
                                        // uses non-exclusive read locking.
define("DOMDOC_AS_READFILE",20);
// creates a dom object from file URI.
                                        // uses exclusive write locking.
define("DOMDOC_AS_WRITEFILE",30);
// creates a dom object from remote
                                        // file URI (read-only).
define("DOMDOC_AS_REMOTEFILE",40);
// uses start param as a reference to
                                        // and existing DOM document object
define("DOMDOC_AS_REFERENCE",50);
// uses start param as data of new dom
                                        // document.
define("DOMDOC_AS_DATA",60);

/**
* General purpose DOM class
*
* This class provides three forms of functionality. 1) shortcut functions to * operations made tedious by the DOM API. 2) additional features not supported
* by the DOM API. 3) a thread-safe way of interacting with files associated
* with the class' DOM document.
*
* @author       Terence Kearns
* @version      0.0.1
* @copyright    Terence Kearns
* @license      LGPL
* @homepage     http://terencekearns.com
*/
class domDoc {

    /**
    * An instance of xmlErrorLog used by this class.
    *
    * @access   private
    * @var      object
    */
    var $objErr;

    /**
    * An instance of a DOM XML object.
    *
    * @access   public
    * @var      object
    */
    var $objDoc;

    /**
    * Document root
    *
    * @access   public
    * @var      object
    */
    var $ndRoot;

    /**
    * Is the document read-only
    *
    * @access   public
    * @var      boolean
    */
    var $blnIsReadOnly;

    /**
    * How has the document been instantiated. What mode.
    *
    * @access   private
    * @var      integer from constant
    */
    var $intMode;

    /**
    * File Pointer
    *
* This is used if the mode is WRITABLE so that the file resource is opened
    * (and locked) in the contructor the unlocked and closed in a sepaerate
    * member function (commit()).
    *
    * @access   private
    * @var      integer from constant
    */
    var $fp;

    /**
    * Constructor method
    *
* Create the objDoc instance property and associated ndRoot property based * on the user-selected mode of document creation. In the process, establish
    * the blnIsReadOnly property.
    *
    * @param    mixed   information required to create a DOM document
* @param int constant specifying how the document is to be created
    * @return   void
    */
    function domDoc(&$Starter,$intUse = DOMDOC_AS_NEW) {
        $this->objErr = new xmlErrorLog($this);
        $this->intMode = $intUse;
        $this->blnIsReadOnly = true;
// for more info on each case block, see
                                    // comments in the constant definitions
                                    // at the top of this file.
        switch ($intUse) {
            case DOMDOC_AS_NEW:
                $this->blnIsReadOnly = false;
                $this->objDoc = domxml_new_doc("1.0");
                $elRoot = $this->objDoc->create_element($Starter);
                $this->ndRoot = $this->objDoc->append_child($elRoot);
            break;
            case DOMDOC_AS_READFILE:
                $fp = fopen($Starter,"r")
                    OR $this->throw(
                        "could not open ".$Starter." for reading."
                    );
                                    // attempt thread safety by using flock
                flock($fp,LOCK_SH);
                $xmlData = fread($fp,filesize($Starter));
                flock($fp,LOCK_UN);
                fclose($fp);
                                    // finish working with the file as
                                    // quickly as possible and THEN worry
                                    // about making a DOM doc from it.
                $this->_domOpenFromData($xmlData);
            break;
            case DOMDOC_AS_WRITEFILE:
                $this->blnIsReadOnly = false;
                $this->fp = fopen($Starter,"r+b")
                    OR $this->throw(
                        "could not open ".$Starter." for writing."
                    );
                flock($this->fp,LOCK_EX);
                $xmlData = fread($this->fp,filesize($Starter));
                $this->_domOpenFromData($xmlData,$Starter);
            break;
            case DOMDOC_AS_REMOTEFILE:
                $fp = fopen($Starter,"r")
                    OR $this->throw(
                        "could not open ".$Starter." for reading."
                    );
                $xmlData = fread($fp,filesize($Starter));
                fclose($fp);
                $this->_domOpenFromData($xmlData);
            break;
            case DOMDOC_AS_REFERENCE:
                $this->objDoc =& $Starter;
            break;
            case DOMDOC_AS_DATA:
                $this->_domOpenFromData($Starter);
            break;
            default:
                $this->throw(
                    "second argument to domDoc constructor is invalid"
                );
        }
    }

    /**
* Serialise and return the entire document object as a stand-alone XML.
     *
     * @return  xml     well formed ascii
     * @access  private
     */
    function _domOpenFromData(&$xmlData,$Starter = null) {
        if(!trim($xmlData)) {
            $this->objDoc = domxml_new_doc("1.0");
            $ndRoot = $this->objDoc->create_element("emptyDocument");
            $this->ndRoot = $this->objDoc->append_child($ndRoot);
            if($this->intMode == DOMDOC_AS_WRITEFILE) {
                                        // clean up the dangling resource
                flock($this->fp,LOCK_UN);
                fclose($this->fp);
                                        // register an error
                $this->throw($Starter." is empty.");
            }
            else {
                                        // register an error
                $this->throw("requested document is empty.");
            }
                                        // do not continue this function
            return;
        }

        $this->objDoc = domxml_open_mem($xmlData);
        if(!is_object($this->objDoc)) {
$this->throw("Could not create an XML document using:\n\n".$xmlData);
            return;
        }
        $this->ndRoot = $this->objDoc->document_element();
    }

    function throw($strErrMsg) {
        $this->objErr->trap($strErrMsg);
        $this->ConsumeDoc($this->objErr);
    }

    function _AbortDocument($strErrMsg) {
                                        // populate the DOM doc with an
                                        // error message.
        $this->objDoc = domxml_new_doc("1,0");
        $nd = $this->objDoc->create_element("error");
        $this->ndRoot = $this->objDoc->append_child($nd);
        $this->ndRoot->set_content($strErrMsg);
        $this->ndRoot->set_attribute("ts",time());
    }

    /**
* Serialise and return the entire document object as a stand-alone XML.
     *
     * @return  xml     document
     * @access  public
     */
    function xmlGetDoc() {
        return $this->objDoc->dump_mem(true);
    }

    /**
* Serialise and return the entire document as a well-balenced XML fragment.
     *
     * @return  xml     fragment
     * @access  public
     */
    function xmlGetFrag() {
        return "\n\n".$this->objDoc->dump_node($this->ndRoot,true)."\n\n";
    }

    /**
    * Insert stylesheet processing instruction
    *
    *
    * @param    uri     path to XSL stylesheet.
    * @return   bool    success
    */
    function AddStylePI($uriStylesheet) {
                $strPiCont = ' type="text/xsl" href="'.$uriStylesheet.'"';
                $strPiTarget = 'xml-stylesheet';
$piNode = $this->objDoc->create_processing_instruction($strPiTarget, $strPiCont);
                $this->objDoc->insert_before($piNode, $this->ndRoot);
        return true;
    }

    /**
    * mass storage serialisation
    *
* This function will dump the textual contents of the DOM document (in it's
    * current state to) file.
    *
    * @param    uri     path to destination file
    * @return   bool    success or failure
    */
    function Commit() {
        if($this->intMode == DOMDOC_AS_WRITEFILE) {
            rewind($this->fp);
            ftruncate($this->fp,0);
            fwrite($this->fp,trim($this->xmlGetDoc()));
            flock($this->fp,LOCK_UN);
            fclose($this->fp);
        }
        else {
            return $this->objErr->throw(
                "Commit() can only be used when domDoc is invoked in "
                ."DOMDOC_AS_WRITEFILE mode"
            );
        }
        return true;
    }

    /**
    * mass storage serialisation
    *
* This function will dump the textual contents of the DOM document (in it's
    * current state to) a specified file.
    *
    * @param    uri     path to destination file
    * @return   bool    success or failure
    */
    function CommitToFile($uriDestination) {
        $fp = fopen($uriDestination,"w+")
or $this->throw("CommitToFile: could not open ".$uriDestination." for writing");
        flock($fp,LOCK_EX);
        fwrite($fp,$this->xmlGetDoc())
or $this->throw("CommitToFile: could write to ".$uriDestination);
        flock($fp,LOCK_UN);
        fclose($fp);
        return true;
    }

    function ndGetOneEl($strName,$intIdx=0) {
        $arrNds = $this->objDoc->get_elements_by_tagname($strName);
        // header("Content-Type: text/plain;"); var_dump($arrNds);die();
        if(isset($arrNds[$intIdx])) return $arrNds[$intIdx];
        return false;
    }

    function ndAppendToRoot($strElName,$strCont = "") {
        $elNew = $this->objDoc->create_element($strElName);
        $ndNew = $this->ndRoot->append_child($elNew);
        $ndNew->set_content($strCont);
        return $ndNew;
    }

    function ndAppendToNode($ndStub,$strElName,$strCont = "") {
        $elNew = $this->objDoc->create_element($strElName);
        $ndNew = $ndStub->append_child($elNew);
        $ndNew->set_content($strCont);
        return $ndNew;
    }

    function ImportChildFrag($ndStub,$ndNew) {
        $ndTmp = $this->objDoc->create_element("tmp");
        $ndTmp = $ndStub->append_child($ndTmp);
        $ndTmp->replace_node($ndNew);
        return $ndNew;
    }
    function ConsumeDoc($objDoc) {
if(!is_object($objDoc)) $this->throw("ConsumeDoc: Non object given"); elseif(!is_object(@$objDoc->ndRoot)) $this->throw("ConsumeDoc: No root node.");
        else $this->ImportChildFrag($this->ndRoot,$objDoc->ndRoot);
    }

    function ConsumeFile($uri) {
        $objDoc = new domDoc($uri,DOMDOC_AS_READFILE);
if(!is_object($objDoc)) $this->throw("ConsumeFile: $uri is not an XML document."); elseif(!is_object(@$objDoc->ndRoot)) $this->throw("ConsumeFile: No root node.");
        else $this->ImportChildFrag($this->ndRoot,$objDoc->ndRoot);
    }
}

?>


example

include_once "class_appDoc.php";
$objApp = new appDoc();// appDoc class inherits domDoc
$objDoc = new myCustomData($yadda,$yadda);//myCustomData inherits domDoc
$objApp->ConsumeDoc($objDoc);   //////////////  TADA!!!!!!!!!!!!!!!!!!!!
echo $objApp->xmlGetDoc(); // transform or whatever...



--
Terence Kearns: Web Developer
University of Canberra: +61 2 6201 5516



XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list



<Prev in Thread] Current Thread [Next in Thread>