| <?php |
| /** |
| * File containing the ezcTreeXml class. |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 |
| * @version //autogentag// |
| * @filesource |
| * @package Tree |
| */ |
| |
| /** |
| * ezcTreeXml is an implementation of a tree backend that operates on |
| * an XML file. |
| * |
| * Example: |
| * <code> |
| * <?php |
| * // Create a new tree in a new XML file |
| * $tree = ezcTreeXml::create( |
| * 'new-tree.xml', |
| * new ezcTreeXmlInternalDataStore() |
| * ); |
| * |
| * // Open an existing XML file containing a tree structure |
| * $tree = new ezcTreeXml( |
| * 'existing-tree.xml', |
| * new ezcTreeXmlInternalDataStore() |
| * ); |
| * ?> |
| * </code> |
| * |
| * See {@link ezcTree} for examples on how to operate on the tree. |
| * |
| * @property-read ezcTreeXmlDataStore $store |
| * The data store that is used for retrieving/storing data. |
| * @property-read string $prefix |
| * The prefix that is used to prefix node IDs with in the tree. |
| * @property string $nodeClassName |
| * Which class is used as tree node - this class *must* inherit |
| * the ezcTreeNode class. |
| * |
| * @package Tree |
| * @version //autogentag// |
| * @mainclass |
| */ |
| class ezcTreeXml extends ezcTree |
| { |
| /** |
| * Contains the relax-NG schema to validate the tree XML. |
| */ |
| const relaxNG = '<?xml version="1.0" encoding="UTF-8"?> |
| <grammar xmlns:etd="http://components.ez.no/Tree/data" ns="http://components.ez.no/Tree" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> |
| <start> |
| <element name="tree"> |
| <optional> |
| <attribute name="prefix"> |
| <data type="ID"/> |
| </attribute> |
| </optional> |
| <optional> |
| <attribute name="lastNodeId"> |
| <data type="string"/> |
| </attribute> |
| </optional> |
| <optional> |
| <ref name="node"/> |
| </optional> |
| </element> |
| </start> |
| <define name="node"> |
| <element name="node"> |
| <attribute name="id"> |
| <data type="ID"/> |
| </attribute> |
| <optional> |
| <element name="etd:data"> |
| <text/> |
| </element> |
| </optional> |
| <zeroOrMore> |
| <ref name="node"/> |
| </zeroOrMore> |
| </element> |
| </define> |
| </grammar>'; |
| |
| /** |
| * Contains the DOM Tree that all operations will be done on. |
| * |
| * When the tree object is constructed the XML is parsed with DOM and |
| * stored into this member variable. When the tree is modified the changes |
| * are then flushed to disk with the saveFile() method. |
| * |
| * @var DOMDocument $dom |
| */ |
| private $dom; |
| |
| /** |
| * The file name that contains the tree as XML string. |
| * |
| * @var string $xmlFile |
| */ |
| private $xmlFile; |
| |
| /** |
| * Stores the last auto generated ID that was used. |
| * |
| * @var integer $autoNodeId |
| */ |
| private $autoNodeId = 0; |
| |
| /** |
| * Constructs a new ezcTreeXml object from the XML data in $xmlFile and using |
| * the $store to retrieve data from. |
| * |
| * @param string $xmlFile |
| * @param ezcTreeXmlDataStore $store |
| */ |
| public function __construct( $xmlFile, ezcTreeXmlDataStore $store ) |
| { |
| if ( !file_exists( $xmlFile ) ) |
| { |
| throw new ezcBaseFileNotFoundException( $xmlFile, "XML" ); |
| } |
| |
| $previous = libxml_use_internal_errors( true ); |
| |
| $dom = new DomDocument(); |
| $dom->load( $xmlFile ); |
| $dom->formatOutput = true; |
| |
| $errors = libxml_get_errors(); |
| libxml_clear_errors(); |
| if ( count( $errors ) ) |
| { |
| throw new ezcTreeInvalidXmlException( $xmlFile, $errors ); |
| } |
| |
| $valid = $dom->relaxNGValidateSource( self::relaxNG ); |
| if ( !$valid ) |
| { |
| $errors = libxml_get_errors(); |
| libxml_clear_errors(); |
| throw new ezcTreeInvalidXmlFormatException( $xmlFile, $errors ); |
| } |
| |
| libxml_use_internal_errors( $previous ); |
| |
| // Associate the DOM tree with the data store |
| $store->setDomTree( $dom ); |
| |
| // Figure out the prefix - which is the "prefix" attribute on the root node. |
| $document = $dom->documentElement; |
| $prefix = $document->getAttribute( 'prefix' ); |
| |
| // Figure out the last auto generated ID |
| $autoId = false; |
| $this->autoNodeId = $document->getAttribute( 'lastNodeId' ); |
| if ( $this->autoNodeId !== "" ) |
| { |
| $autoId = true; |
| } |
| |
| // Set member variables |
| $this->dom = $dom; |
| $this->xmlFile = $xmlFile; |
| $this->properties['store'] = $store; |
| $this->properties['prefix'] = $prefix; |
| $this->properties['autoId'] = $autoId; |
| } |
| |
| /** |
| * Returns the value of the property $name. |
| * |
| * @throws ezcBasePropertyNotFoundException if the property does not exist. |
| * @param string $name |
| * @ignore |
| */ |
| public function __get( $name ) |
| { |
| switch ( $name ) |
| { |
| case 'prefix': |
| return $this->properties[$name]; |
| } |
| return parent::__get( $name ); |
| } |
| |
| /** |
| * Sets the property $name to $value. |
| * |
| * @throws ezcBasePropertyNotFoundException if the property does not exist. |
| * @throws ezcBasePropertyPermissionException if a read-only property is |
| * tried to be modified. |
| * @param string $name |
| * @param mixed $value |
| * @ignore |
| */ |
| public function __set( $name, $value ) |
| { |
| switch ( $name ) |
| { |
| case 'prefix': |
| throw new ezcBasePropertyPermissionException( $name, ezcBasePropertyPermissionException::READ ); |
| |
| default: |
| return parent::__set( $name, $value ); |
| } |
| } |
| |
| /** |
| * Returns true if the property $name is set, otherwise false. |
| * |
| * @param string $name |
| * @return bool |
| * @ignore |
| */ |
| public function __isset( $name ) |
| { |
| switch ( $name ) |
| { |
| case 'prefix': |
| return isset( $this->properties[$name] ); |
| |
| default: |
| return parent::__isset( $name ); |
| } |
| } |
| |
| /** |
| * Creates a new XML tree in the file $xmlFile using $store as data store. |
| * |
| * The $prefix option can be used to change the prefix that is used for IDs |
| * in the created tree. By default this is set to "id" so that numerical |
| * node IDs are not a problem. |
| * |
| * @param string $xmlFile |
| * @param ezcTreeXmlDataStore $store |
| * @param string $prefix |
| * @return ezcTreeXml |
| */ |
| public static function create( $xmlFile, ezcTreeXmlDataStore $store, $prefix = 'id' ) |
| { |
| $dom = new DomDocument( '1.0', 'utf-8' ); |
| $dom->formatOutput = true; |
| |
| $element = $dom->createElement( 'tree' ); |
| $element->setAttributeNode( new DOMAttr( 'xmlns', 'http://components.ez.no/Tree' ) ); |
| $element->setAttributeNode( new DOMAttr( 'xmlns:etd', 'http://components.ez.no/Tree/data' ) ); |
| if ( $prefix !== null ) |
| { |
| $element->setAttributeNode( new DOMAttr( 'prefix', $prefix ) ); |
| } |
| |
| $dom->appendChild( $element ); |
| |
| $dom->save( $xmlFile ); |
| return new ezcTreeXml( $xmlFile, $store, $prefix ); |
| } |
| |
| /** |
| * Saves the internal DOM representation of the tree back to disk. |
| */ |
| public function saveFile() |
| { |
| $this->dom->save( $this->xmlFile ); |
| } |
| |
| /** |
| * This method generates the next node ID. |
| * |
| * It uses the stored last generated ID, and also stores this in an |
| * attribute on the root node so that it can be correctly incremented |
| * without having to search for the last generated ID in the whole tree. |
| * |
| * @return integer |
| */ |
| protected function generateNodeID() |
| { |
| $this->autoNodeId++; |
| |
| $document = $this->dom->documentElement; |
| $document->setAttributeNode( new DOMAttr( 'lastNodeId', $this->autoNodeId ) ); |
| |
| return $this->autoNodeId; |
| } |
| |
| /** |
| * Returns whether the node with ID $nodeId exists. |
| * |
| * @param string $nodeId |
| * @return bool |
| */ |
| public function nodeExists( $nodeId ) |
| { |
| $elem = $this->dom->getElementById( "{$this->properties['prefix']}$nodeId" ); |
| return ( $elem !== null ) ? true : false; |
| } |
| |
| /** |
| * Retrieves a DOMElement containing the node with node ID $nodeId. |
| * |
| * @param string $nodeId |
| * @return DOMElement |
| */ |
| private function getNodeById( $nodeId ) |
| { |
| $node = $this->dom->getElementById( "{$this->properties['prefix']}$nodeId" ); |
| if ( !$node ) |
| { |
| throw new ezcTreeUnknownIdException( $nodeId ); |
| } |
| return $node; |
| } |
| |
| /** |
| * Fetches all the child "node" DOM elements of the node with ID $nodeId. |
| * |
| * @param string $nodeId |
| * @return array(string) |
| */ |
| private function fetchChildIds( $nodeId ) |
| { |
| $childNodes = array(); |
| $elem = $this->getNodeById( $nodeId ); |
| $children = $elem->childNodes; |
| foreach ( $children as $child ) |
| { |
| if ( $child->nodeType === XML_ELEMENT_NODE && $child->tagName == "node" ) |
| { |
| $nodeId = substr( $child->getAttribute( 'id' ), strlen( $this->properties['prefix'] ) ); |
| $childNodes[] = $nodeId; |
| } |
| } |
| return $childNodes; |
| } |
| |
| /** |
| * Returns all the children of the node with ID $nodeId. |
| * |
| * @param string $nodeId |
| * @return ezcTreeNodeList |
| */ |
| public function fetchChildren( $nodeId ) |
| { |
| $className = $this->properties['nodeClassName']; |
| $list = new ezcTreeNodeList; |
| foreach ( $this->fetchChildIds( $nodeId ) as $childId ) |
| { |
| $list->addNode( new $className( $this, $childId ) ); |
| } |
| return $list; |
| } |
| |
| /** |
| * Returns the parent node of the node with ID $nodeId. |
| * |
| * This method returns null if there is no parent node. |
| * |
| * @param string $nodeId |
| * @return ezcTreeNode |
| */ |
| public function fetchParent( $nodeId ) |
| { |
| $className = $this->properties['nodeClassName']; |
| $elem = $this->getNodeById( $nodeId ); |
| $elem = $elem->parentNode; |
| $parentId = $elem !== null ? substr( $elem->getAttribute( 'id' ), strlen( $this->properties['prefix'] ) ) : null; |
| if ( $parentId === false ) |
| { |
| return null; |
| } |
| return new $className( $this, $parentId ); |
| } |
| |
| /** |
| * Returns all the nodes in the path from the root node to the node with ID |
| * $nodeId, including those two nodes. |
| * |
| * @param string $nodeId |
| * @return ezcTreeNodeList |
| */ |
| public function fetchPath( $nodeId ) |
| { |
| $className = $this->properties['nodeClassName']; |
| |
| $nodes = array(); |
| $nodes[] = new $className( $this, $nodeId ); |
| |
| $elem = $this->getNodeById( $nodeId ); |
| $elem = $elem->parentNode; |
| |
| while ( $elem !== null && $elem->nodeType == XML_ELEMENT_NODE && $elem->tagName == 'node' ) |
| { |
| $id = substr( $elem->getAttribute( 'id' ), strlen( $this->properties['prefix'] ) ); |
| $nodes[] = new $className( $this, $id ); |
| $elem = $elem->parentNode; |
| } |
| |
| $list = new ezcTreeNodeList; |
| foreach ( array_reverse( $nodes ) as $node ) |
| { |
| $list->addNode( $node ); |
| } |
| return $list; |
| } |
| |
| /** |
| * Adds the children nodes of the node with ID $nodeId to the |
| * ezcTreeNodeList $list. |
| * |
| * @param ezcTreeNodeList $list |
| * @param string $nodeId |
| */ |
| private function addChildNodesDepthFirst( ezcTreeNodeList $list, $nodeId ) |
| { |
| $className = $this->properties['nodeClassName']; |
| foreach ( $this->fetchChildIds( $nodeId ) as $childId ) |
| { |
| $list->addNode( new $className( $this, $childId ) ); |
| $this->addChildNodesDepthFirst( $list, $childId ); |
| } |
| } |
| |
| /** |
| * Returns the node with ID $nodeId and all its children, sorted according to |
| * the {@link http://en.wikipedia.org/wiki/Depth-first_search Depth-first sorting} |
| * algorithm. |
| * |
| * @param string $nodeId |
| * @return ezcTreeNodeList |
| */ |
| public function fetchSubtreeDepthFirst( $nodeId ) |
| { |
| $className = $this->properties['nodeClassName']; |
| $list = new ezcTreeNodeList; |
| $list->addNode( new $className( $this, $nodeId ) ); |
| $this->addChildNodesDepthFirst( $list, $nodeId ); |
| return $list; |
| } |
| |
| /** |
| * Alias for fetchSubtreeDepthFirst(). |
| * |
| * @param string $nodeId |
| * @return ezcTreeNodeList |
| */ |
| public function fetchSubtree( $nodeId ) |
| { |
| return $this->fetchSubtreeDepthFirst( $nodeId ); |
| } |
| |
| /** |
| * Adds the children nodes of the node with ID $nodeId to the |
| * ezcTreeNodeList $list. |
| * |
| * @param ezcTreeNodeList $list |
| * @param string $nodeId |
| */ |
| private function addChildNodesBreadthFirst( ezcTreeNodeList $list, $nodeId ) |
| { |
| $className = $this->properties['nodeClassName']; |
| $childIds = $this->fetchChildIds( $nodeId ); |
| foreach ( $childIds as $childId ) |
| { |
| $list->addNode( new $className( $this, $childId ) ); |
| } |
| foreach ( $childIds as $childId ) |
| { |
| $this->addChildNodesDepthFirst( $list, $childId ); |
| } |
| } |
| |
| /** |
| * Returns the node with ID $nodeId and all its children, sorted according to |
| * the {@link http://en.wikipedia.org/wiki/Breadth-first_search Breadth-first sorting} |
| * algorithm. |
| * |
| * @param string $nodeId |
| * @return ezcTreeNodeList |
| */ |
| public function fetchSubtreeBreadthFirst( $nodeId ) |
| { |
| $className = $this->properties['nodeClassName']; |
| $list = new ezcTreeNodeList; |
| $list->addNode( new $className( $this, $nodeId ) ); |
| $this->addChildNodesBreadthFirst( $list, $nodeId ); |
| return $list; |
| } |
| |
| /** |
| * Returns the number of direct children of the node with ID $nodeId. |
| * |
| * @param string $nodeId |
| * @return int |
| */ |
| public function getChildCount( $nodeId ) |
| { |
| $count = 0; |
| $elem = $this->getNodeById( $nodeId ); |
| $children = $elem->childNodes; |
| foreach ( $children as $child ) |
| { |
| if ( $child->nodeType === XML_ELEMENT_NODE && $child->tagName == "node" ) |
| { |
| $count++; |
| } |
| } |
| return $count; |
| } |
| |
| /** |
| * Adds the number of children with for the node with ID $nodeId nodes to |
| * $count, recursively. |
| * |
| * @param int $count |
| * @param string $nodeId |
| */ |
| private function countChildNodes( &$count, $nodeId ) |
| { |
| foreach ( $this->fetchChildIds( $nodeId ) as $childId ) |
| { |
| $count++; |
| $this->countChildNodes( $count, $childId ); |
| } |
| } |
| |
| /** |
| * Returns the number of children of the node with ID $nodeId, recursively. |
| * |
| * @param string $nodeId |
| * @return int |
| */ |
| public function getChildCountRecursive( $nodeId ) |
| { |
| $count = 0; |
| $this->countChildNodes( $count, $nodeId ); |
| return $count; |
| } |
| |
| /** |
| * Returns the distance from the root node to the node with ID $nodeId. |
| * |
| * @param string $nodeId |
| * @return int |
| */ |
| public function getPathLength( $nodeId ) |
| { |
| $elem = $this->getNodeById( $nodeId ); |
| $elem = $elem->parentNode; |
| $length = -1; |
| |
| while ( $elem !== null && $elem->nodeType == XML_ELEMENT_NODE ) |
| { |
| $elem = $elem->parentNode; |
| $length++; |
| } |
| return $length; |
| } |
| |
| /** |
| * Returns whether the node with ID $nodeId has children. |
| * |
| * @param string $nodeId |
| * @return bool |
| */ |
| public function hasChildNodes( $nodeId ) |
| { |
| $elem = $this->getNodeById( $nodeId ); |
| $children = $elem->childNodes; |
| foreach ( $children as $child ) |
| { |
| if ( $child->nodeType === XML_ELEMENT_NODE && $child->tagName == "node" ) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Returns whether the node with ID $childId is a direct child of the node |
| * with ID $parentId. |
| * |
| * @param string $childId |
| * @param string $parentId |
| * @return bool |
| */ |
| public function isChildOf( $childId, $parentId ) |
| { |
| $elem = $this->getNodeById( $childId ); |
| $parentElem = $elem->parentNode; |
| $nodeId = $parentElem->getAttribute( 'id' ); |
| if ( $nodeId === "{$this->properties['prefix']}$parentId" ) |
| { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Returns whether the node with ID $childId is a direct or indirect child |
| * of the node with ID $parentId. |
| * |
| * @param string $childId |
| * @param string $parentId |
| * @return bool |
| */ |
| public function isDescendantOf( $childId, $parentId ) |
| { |
| $elem = $this->getNodeById( $childId ); |
| $elem = $elem->parentNode; |
| |
| while ( $elem !== null && $elem->nodeType == XML_ELEMENT_NODE ) |
| { |
| $nodeId = $elem->getAttribute( 'id' ); |
| if ( $nodeId === "{$this->properties['prefix']}$parentId" ) |
| { |
| return true; |
| } |
| $elem = $elem->parentNode; |
| } |
| return false; |
| } |
| |
| /** |
| * Returns whether the nodes with IDs $child1Id and $child2Id are siblings |
| * (ie, they share the same parent). |
| * |
| * @param string $child1Id |
| * @param string $child2Id |
| * @return bool |
| */ |
| public function isSiblingOf( $child1Id, $child2Id ) |
| { |
| $elem1 = $this->getNodeById( $child1Id ); |
| $elem2 = $this->getNodeById( $child2Id ); |
| return ( |
| ( $child1Id !== $child2Id ) && |
| ( $elem1->parentNode->getAttribute( 'id' ) === $elem2->parentNode->getAttribute( 'id' ) ) |
| ); |
| } |
| |
| /** |
| * Sets a new node as root node, this also wipes out the whole tree. |
| * |
| * @param ezcTreeNode $node |
| */ |
| public function setRootNode( ezcTreeNode $node ) |
| { |
| $document = $this->dom->documentElement; |
| |
| // remove old root node(s) |
| foreach ( $document->childNodes as $childNode ) |
| { |
| if ( $childNode->nodeType == XML_ELEMENT_NODE && $childNode->tagName === 'node' ) |
| { |
| $nodeId = substr( $childNode->getAttribute( 'id' ), strlen( $this->properties['prefix'] ) ); |
| $this->delete( $nodeId ); |
| } |
| } |
| $this->store->deleteDataForAllNodes(); |
| |
| // Create new root node |
| $root = $this->dom->createElement( 'node' ); |
| $root->setAttributeNode( new DOMAttr( 'id', "{$this->properties['prefix']}{$node->id}" ) ); |
| $root->setIdAttribute( 'id', true ); |
| $document->appendChild( $root ); |
| $this->store->storeDataForNode( $node, $node->data ); |
| $this->saveFile(); |
| } |
| |
| /** |
| * Returns the root node. |
| * |
| * This methods returns null if there is no root node. |
| * |
| * @return ezcTreeNode |
| */ |
| public function getRootNode() |
| { |
| $className = $this->properties['nodeClassName']; |
| $document = $this->dom->documentElement; |
| |
| foreach ( $document->childNodes as $childNode ) |
| { |
| if ( $childNode->nodeType == XML_ELEMENT_NODE && $childNode->tagName == 'node' ) |
| { |
| $nodeId = substr( $childNode->getAttribute( 'id' ), strlen( $this->properties['prefix'] ) ); |
| return new $className( $this, $nodeId ); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Adds the node $childNode as child of the node with ID $parentId. |
| * |
| * @param string $parentId |
| * @param ezcTreeNode $childNode |
| */ |
| public function addChild( $parentId, ezcTreeNode $childNode ) |
| { |
| if ( $this->inTransaction ) |
| { |
| $this->addTransactionItem( new ezcTreeTransactionItem( ezcTreeTransactionItem::ADD, $childNode, null, $parentId ) ); |
| return; |
| } |
| |
| // locate parent node |
| $elem = $this->getNodeById( $parentId ); |
| |
| // Create new DOM node |
| $child = $this->dom->createElement( 'node' ); |
| $child->setAttributeNode( new DOMAttr( 'id', "{$this->properties['prefix']}{$childNode->id}" ) ); |
| $child->setIdAttribute( 'id', true ); |
| |
| // Append to parent node |
| $elem->appendChild( $child ); |
| $this->store->storeDataForNode( $childNode, $childNode->data ); |
| |
| if ( !$this->inTransactionCommit ) |
| { |
| $this->saveFile(); |
| } |
| } |
| |
| /** |
| * Deletes the node with ID $nodeId from the tree, including all its children. |
| * |
| * @param string $nodeId |
| */ |
| public function delete( $nodeId ) |
| { |
| if ( $this->inTransaction ) |
| { |
| $this->addTransactionItem( new ezcTreeTransactionItem( ezcTreeTransactionItem::DELETE, null, $nodeId ) ); |
| return; |
| } |
| |
| // Delete all the associated data |
| $nodeList = $this->fetchSubtree( $nodeId ); |
| $this->store->deleteDataForNodes( $nodeList ); |
| |
| // locate node to move |
| $nodeToDelete = $this->getNodeById( $nodeId ); |
| |
| // Remove the ID on all children by hand as this would crash in PHP <= 5.2.3 |
| $nodeToDelete->removeAttribute( "id" ); |
| |
| $children = $nodeToDelete->getElementsByTagName( 'node' ); |
| foreach ( $children as $child ) |
| { |
| $child->removeAttribute( "id" ); |
| } |
| |
| // Use the parent to remove the child |
| $nodeToDelete->parentNode->removeChild( $nodeToDelete ); |
| |
| if ( !$this->inTransactionCommit ) |
| { |
| $this->saveFile(); |
| } |
| } |
| |
| /** |
| * Moves the node with ID $nodeId as child to the node with ID $targetParentId. |
| * |
| * @param string $nodeId |
| * @param string $targetParentId |
| */ |
| public function move( $nodeId, $targetParentId ) |
| { |
| if ( $this->inTransaction ) |
| { |
| $this->addTransactionItem( new ezcTreeTransactionItem( ezcTreeTransactionItem::MOVE, null, $nodeId, $targetParentId ) ); |
| return; |
| } |
| |
| // locate node to move |
| $nodeToMove = $this->getNodeById( $nodeId ); |
| |
| // locate new parent |
| $parent = $this->getNodeById( $targetParentId ); |
| |
| $parent->appendChild( $nodeToMove ); |
| |
| if ( !$this->inTransactionCommit ) |
| { |
| $this->saveFile(); |
| } |
| } |
| |
| /** |
| * Fixates the transaction (saves the XML file). |
| */ |
| public function fixateTransaction() |
| { |
| $this->saveFile(); |
| } |
| } |
| ?> |