Re: [xsl] Editing XML dynamically and using two input XML files

Subject: Re: [xsl] Editing XML dynamically and using two input XML files
From: Terence Kearns <terencek@xxxxxxxxxxxxxxxxxxx>
Date: Fri, 30 May 2003 17:48:59 +1000
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


Current Thread