Forum Moderators: coopster

Message Too Old, No Replies

reading and writing to xml files

         

surrealillusions

2:13 pm on Mar 18, 2008 (gmt 0)

10+ Year Member



Hi, i presume this is more of a php question than xml question. Anyway, I would like to know how to read and write to xml files.

Although i guess the writing part should come first ;)

Anyway, the idea is that i want to be able to create a guestbook system, but for different pages on a website. Instead of creating lots of different mysql database tables, i figured it might be easier to do it with xml files.

So, how do you go about getting the the data from the form (presuming its already been checked for naughty things), and write it to the xml file? I know xml doesn't really have any set tags, its down to the user to write those tags for what they want such as <name> <comments> <shoesize> etc etc..

But thats about all i know...

any pointers in the right direction would be very much appreciated

:)

coopster

9:50 pm on Mar 18, 2008 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



Instead of creating lots of different mysql database tables, i figured it might be easier to do it with xml files.

You may find otherwise ;)

If you are going to do any XML work at all using PHP you should get familiar with the SimpleXML functions [php.net] and the DOM Functions [php.net]

surrealillusions

10:57 pm on Mar 18, 2008 (gmt 0)

10+ Year Member



ok..thanks

Its nice knowing different ways of storing data though.

:)

coopster

1:07 am on Mar 19, 2008 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



Oh yes, knowledge is no doubt a wonderful thing. Learning XML, learning to read and write XML, is fun. It adds to the services you know and understand and ultimately will be able to deliver should you be asked to do so. I'm just saying that a guestbook app is not the most likely candidate for an XML data storage solution. One of the most demanding aspects of data storage is the fact that people will want to retrieve and display that very same information, and do so in many different ways quickly. A database is going to offer many more options to select and sort that data from a guestbook application where I believe you will find that XML falls short in this particular situation.

The best part of the exercise is gaining the knowledge of discernment. When and where to apply which solution. My advice in this particular instance would be to go ahead and dabble with the XML aspect, but know that a database is going to serve you much better in the case of a guestbook application.

Dave75

4:56 am on Mar 19, 2008 (gmt 0)

10+ Year Member



Hi,

I use XML for data storage and manipulation in PHP all the time. Here is a standard wrapper function for reading XML. NB: you might want to add error suppression(@) and or custom error handling depending upon your development and production enviroment. To save your XML simply use the DOMDocument->save() method which you can find in the DOM functions section of the PHP Manual.


// Wrapper function for DOMDocument->load().
// - $Str_FileName:XML file to retrieve as a DOMDocument object.
// * Return:DOMDocument object of the XML file.
public function LoadDomXmlFile($Str_FileName)
{
//Make sure XML file exists.
if (!file_exists($Str_FileName.'.xml'))
{
throw new Exception('Could not find XML file: '.$Str_FileName.'.xml! Check name and location of file.');
}

//Create and validate new dom object.
$Obj_DOMXMLFile = new DOMDocument('1.0', 'utf-8');
$Obj_DOMXMLFile->validateOnParse = TRUE;

//Read XML file and assign it locally.
if ($Obj_DOMXMLFile->load($Str_FileName.'.xml') === FALSE)
{
trigger_error('Error reading XML file: '.$Str_FileName.'.xml! Check file uses valid XML.', E_USER_ERROR);
}

return $Obj_DOMXMLFile;
}

Dave75

5:24 am on Mar 19, 2008 (gmt 0)

10+ Year Member



"I'm just saying that a guestbook app is not the most likely candidate for an XML data storage solution. One of the most demanding aspects of data storage is the fact that people will want to retrieve and display that very same information, and do so in many different ways quickly. A database is going to offer many more options to select and sort that data from a guestbook application where I believe you will find that XML falls short in this particular situation."

I was going to walk away from this but, I just can't.

XML is a great candidate for data storage options up to about 2MB in file size. At this point you may start experiencing performance problems depending upon your enviroment and scripting, can can always do split file storage for large datasets. The great thing about the DOMDocument object is that it is an object. That means easy caching and more manipulation options.

If you want your guestbook to behave like a database with same query options, simply design your XML file like a DB and write a query object.

Here's the guts of what I use to emulate DB queries in XML.

XML file:

<guestbook>
<record id="1">
<name>someusername</name>
<email>someuseremail</email>
<comment>someusercomment</comment>
</record>
<record id="2">
<name>anotherusername</name>
<email>anotheruseremail</email>
<comment>anotherusercomment</comment>
</record>
</guestbook>

PHP object:

<?php
/* xmlquery.php
* XML query object
*/

class XMLQuery
{
///////////////////////////////////////////////////////////////////////////////
// L O C A L O B J E C T V A R I A B L E S //
///////////////////////////////////////////////////////////////////////////////

public $Obj_LoadedDocument= NULL;
public $Str_LoadedDocument= '';
public $Str_DocumentPath= '';

///////////////////////////////////////////////////////////////////////////////
// C O R E F U N C T I O N S //
///////////////////////////////////////////////////////////////////////////////

//Constructor.
// * Return:VOID
public function __construct($Str_RootDir)
{
$this->Str_DocumentPath = $Str_RootDir.'monkeywrench/db/';
//$this->LoadDomXmlFile($Str_DataFileName);
}

//Sets variable locally by outside object.
// * Return:VOID
public function __set($Var_Name, $Var_Value)
{
$this->$Var_Name = $Var_Value;
}

//Gets xml file and creates dom document for furture processing.
public function LoadDataDocument($Str_FileName)
{
$this->Str_LoadedDocument = $Str_FileName;
$this->Obj_LoadedDocument = $this->LoadDomXmlFile($this->Str_DocumentPath.$Str_FileName);
}

//Gets record object from loaded docuemnt by its id attribute.
// - $Int_RecordId:Id of record to acquire
// * Return:Document record element
public function GetRecordElementById($Int_RecordId)
{
//Declare return variable.
$Obj_RecordNode = NULL;

//Get each record node.
$Arr_DataRecords = $this->Obj_LoadedDocument->getElementsByTagName('record');
foreach ($Arr_DataRecords as $Obj_DataRecord)
{
//Look for an id match.
if ($Obj_DataRecord->getAttribute('id') == $Int_RecordId)
{
$Obj_RecordNode = $Obj_DataRecord;
break;
}

}

return $Obj_RecordNode;
}

//Gets the tree within a DOM element as a string.
// - $Obj_Element:Element node being converted to a string
public function GetElementContentAsString($Obj_Element)
{
//Import element into new document.
$Obj_HolderDocument = new DOMDocument();
$Obj_HolderDocument->loadXML('<holder></holder>');
$Obj_DocumentElement = $Obj_HolderDocument->documentElement;
$Obj_ImportedElement = $Obj_HolderDocument->importNode($Obj_Element, true);
$Obj_DocumentElement->appendChild($Obj_ImportedElement);

//Convert document to string.
$Str_HolderNode = $Obj_HolderDocument->saveXML($Obj_ImportedElement);

//Cut content from element string.
$Str_OpenTagLength = strpos($Str_HolderNode, '>') + 1;
$Str_CloseTagLength = strrpos($Str_HolderNode, '<');
$Str_DataLength = $Str_CloseTagLength - $Str_OpenTagLength;

$Str_Element = substr($Str_HolderNode, $Str_OpenTagLength, $Str_DataLength);

return $Str_Element;
}

//Gets the text node of a data field element.
// - $Obj_DataField:Data field element being queried
// * Return:The text node object of $Obj_DataField
public function GetFieldTextNode($Obj_DataField)
{
//Declare return variable.
$Obj_TextNode = NULL;

//Get data field text node.
$Arr_DataFieldChildren = $Obj_DataField->childNodes;
foreach ($Arr_DataFieldChildren as $Obj_DataFieldChild)
{
if ($Obj_DataFieldChild->nodeType == XML_TEXT_NODE)
{
$Obj_TextNode = $Obj_DataFieldChild;
break;
}
}

return $Obj_TextNode;
}

// Wrapper function for DOMDocument->load().
// - $Str_FileName:XML file to retrieve as a DOMDocument object.
// * Return:DOMDocument object of the XML file.
public function LoadDomXmlFile($Str_FileName)
{
//Make sure XML file exists.
if (!file_exists($Str_FileName.'.xml'))
{
throw new Exception('Could not find XML file: '.$Str_FileName.'.xml! Check name and location of file.');
}

//Create and validate new dom object.
$Obj_DOMXMLFile = new DOMDocument('1.0', 'utf-8');
$Obj_DOMXMLFile->validateOnParse = TRUE;

//Read XML file and assign it locally.
if ($Obj_DOMXMLFile->load($Str_FileName.'.xml') === FALSE)
{
trigger_error('Error reading XML file: '.$Str_FileName.'.xml! Check file uses valid XML.', E_USER_ERROR);
}

return $Obj_DOMXMLFile;
}

///////////////////////////////////////////////////////////////////////////////
// D A T A M A N I P U L A T I O N F U N C T I O N S //
///////////////////////////////////////////////////////////////////////////////

//Gets all data records from a file and returns them as an id indexed array.
// * Return:Multidimensional array of data fields indexed to record id
public function GetAllRecordsFromFile()
{
//Declare return variable.
$Arr_DataTableRecords = array();

//Get data element nodes.
$Arr_DataRecords = $this->Obj_LoadedDocument->documentElement->childNodes;
foreach ($Arr_DataRecords as $Obj_DataRecord)
{
//If the child is a record node get its field values.
if (($Obj_DataRecord->nodeType == XML_ELEMENT_NODE) && ($Obj_DataRecord->nodeName == 'record'))
{
//Get record id.
$Int_RecordId = $Obj_DataRecord->getAttribute('id');

//Get record field values.
$Arr_DataRecordChildren = $Obj_DataRecord->childNodes;
foreach ($Arr_DataRecordChildren as $Obj_DataRecordChild)
{
if ($Obj_DataRecordChild->nodeType == XML_ELEMENT_NODE)
{
//Get record field names and values.
$Str_FieldValue = '';
$Str_FieldName = $Obj_DataRecordChild->nodeName;
$Arr_DataFieldChildren = $Obj_DataRecordChild->childNodes;
foreach ($Arr_DataFieldChildren as $Obj_DataFieldChild)
{
if ($Obj_DataFieldChild->nodeType == XML_TEXT_NODE)
{
$Str_FieldValue = $Obj_DataFieldChild->nodeValue;
break;
}
else
{
$Str_FieldValue = $this->Obj_LoadedDocument->saveXML($Obj_DataFieldChild);
}
}
//echo $Str_FieldValue;
$Arr_DataTableRecords[$Int_RecordId][$Str_FieldName] = $Str_FieldValue;
}
}
}
}

return $Arr_DataTableRecords;
}

//Gets every value of a data field from file as an array indexed to each record's id.
// - $Str_FieldNameName of field data is being collected from
// * Retrun:Array of feild values indexed to each field's id
public function GetDataFieldSetFromFile($Str_FieldName)
{
//Declare return variable.
$Arr_DataFeildSet = array();

//Get each data field node.
$Arr_DataFieldNodes = $this->Obj_LoadedDocument->getElementsByTagName($Str_FieldName);
foreach ($Arr_DataFieldNodes as $Obj_DataFieldNode)
{
//If record has an id attribute add it to the list.
if ($Obj_DataFieldNode->parentNode->hasAttribute('id'))
{
$Str_RecordId = $Obj_DataFieldNode->parentNode->getAttribute('id');
$Arr_DataFeildSet[$Str_RecordId] = $this->GetElementContentAsString($Obj_DataFieldNode);
}
}

return $Arr_DataFeildSet;
}

//Gets a data row in a file as an array indexed by field name.
// - $Int_RecordId:Unique identifier of the record being sort.
// * Return:Array of values indexed by field names.
public function GetDataRecordFromFile($Int_RecordId)
{
//Declare return variable.
$Arr_DataRecordSet = array('id' => $Int_RecordId);

//Get record element.
$Obj_DataRecord = $this->GetRecordElementById($Int_RecordId);

//Get data element nodes.
$Arr_DataRecordChildren = $Obj_DataRecord->childNodes;
foreach ($Arr_DataRecordChildren as $Obj_DataRecordChild)
{
//If this child is an element mine its value.
if ($Obj_DataRecordChild->nodeType == XML_ELEMENT_NODE)
{
$Str_FieldName = $Obj_DataRecordChild->nodeName;

//Get data text node.
$Obj_DataNode = $this->GetFieldTextNode($Obj_DataRecordChild);
//$Arr_DataRecordSet[$Str_FieldName] = $Obj_DataNode->nodeValue;
$Arr_DataRecordSet[$Str_FieldName] = $this->GetElementContentAsString($Obj_DataRecordChild);
}
}

return $Arr_DataRecordSet;
}

//Gets data value from a record field in a file
// - $Int_RecordId:Record identifier data is being retrieved from
// - $Str_FieldName:Name of data field value is being acquired from
// * Return:Data as a text node value
public function GetDataValueFromFile($Int_RecordId, $Str_FieldName)
{
//Declae return variable.
$Str_DataValue = '';

//Get record element.
$Obj_DataRecord = $this->GetRecordElementById($Int_RecordId);

//Get data field element.
$Obj_DataField = $Obj_DataRecord->getElementsByTagName($Str_FieldName)->item(0);
$Str_DataValue = $this->GetElementContentAsString($Obj_DataField);

return $Str_DataValue;
}

//Edits a data value of field $Str_DataField in record $Int_RecordId in the current loaded document.
// - $Int_RecordId:Identifier of the record being edited
// - $Str_FieldName:Data field of the record being edited
// - $Str_EditValue:New data value of the edited field
// * Return:VOID
public function EditDataValueInFile($Int_RecordId, $Str_FieldName, $Str_EditValue)
{
//Get record element.
$Obj_DataRecord = $this->GetRecordElementById($Int_RecordId);

//Get data field element.
$Obj_DataField = $Obj_DataRecord->getElementsByTagName($Str_FieldName)->item(0);

//Get data text node.
$Obj_DataNode = $this->GetFieldTextNode($Obj_DataField);

//Replace current data node with new value.
$Obj_NewDataNode = $this->Obj_LoadedDocument->createTextNode($Str_EditValue);
$Obj_DataNode->parentNode->replaceChild($Obj_NewDataNode, $Obj_DataNode);

//Save document.
$this->Obj_LoadedDocument->save($this->Str_DocumentPath.$this->Str_LoadedDocument.'.xml');

return;
}

//Adds a record to the data file
// - $Arr_DataRecord:Key/value array of record field values.
public function AddDataRecordToFile($Arr_DataRecord)
{
//Get number of records.
$Arr_RecordEntries = $this->Obj_LoadedDocument->getElementsByTagName('record');
$Int_NumOfRecords = 0;
foreach ($Arr_RecordEntries as $Obj_RecordEntry)
{
$Int_NumOfRecords++;
}

//Add record element.
$Obj_NewDataRecord = $this->Obj_LoadedDocument->createElement('record');
$Obj_NewDataRecord->setAttribute('id', $Int_NumOfRecords + 1);
$Obj_DocumentElement = $this->Obj_LoadedDocument->documentElement;
$Obj_DocumentElement->appendChild($Obj_NewDataRecord);

//Add field values.
foreach ($Arr_DataRecord as $Str_Field => $Str_Value)
{
$Obj_NewDataField = $this->Obj_LoadedDocument->createElement($Str_Field, $Str_Value);
$Obj_NewDataRecord->appendChild($Obj_NewDataField);
}

//Save document.
$this->Obj_LoadedDocument->save($this->Str_DocumentPath.$this->Str_LoadedDocument.'.xml');

return;
}

//Removes a data record from document currently loaded locally.
// - $Int_RecordId:Unique identifier of record being removed.
// * Return:VOID
public function RemoveDataRecordFromFile($Int_RecordId)
{
//Get record element.
$Obj_DataRecord = $this->GetDataRecordById($Int_RecordId);

//Remove record.
$Obj_DocumentElement = $this->Obj_LoadedDocument->documentElement;
$this->$Obj_DocumentElement->removeChild($Obj_DataRecord);

//Save document.
$this->$Obj_LoadedDocument->save($this->Str_DocumentPath.$this->Str_LoadedDocument.'.xml');

return;
}

}

?>

Mostly I use GetAllRecordsFromFile and GetDataFieldSetFromFile methods for broad data extraction and GetDataRecordFromFile and GetDataValueFromFile methods for narrow data extraction.

Natrually I have a whole host of other methods in the class but my intention is to demonstrate to type of approach available to you using the XML data storage option.

penders

2:30 pm on Mar 19, 2008 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Instead of creating lots of different mysql database tables...

Perhaps just one table with an extra field identifying the page?

Here's the guts of what I use to emulate DB queries in XML.

Good to see how this can work, and for sharing your code. However, although end-user performance may be OK for a certain data size and number of concurrent users I would imagine the server-load to be a lot higher than conventional DB access? Not that I'm all for DBs, as I do a lot of work with flat files that cope rather nicely.

surrealillusions

2:49 pm on Mar 19, 2008 (gmt 0)

10+ Year Member



dave75 - thanks for sharing the code. much appreciated. Not that i can understand it :p But you have commented it, so i will keep looking through it and see how it works.

Instead of the guestbook idea, (that was the first that came to my head) what other reasons might you use an xml file to store data instead of mysql? Or what reasons is there of using an xml file instead of mysql in some circumstances?

I know about reading and writing site content to mysql, seems easier to use mysql though going by dave's post ;)

:)

Dave75

5:55 am on Mar 20, 2008 (gmt 0)

10+ Year Member



However, although end-user performance may be OK for a certain data size and number of concurrent users I would imagine the server-load to be a lot higher than conventional DB access?

This very much depends upon your web application architecture and how you anticipate(from analysis) how your users are interacting with your data. If it's mostly a read situation like, for example, a blog post, you can keep the array from XMLQuery->GetAllRecordsFromFile() in cache, exactly like you would an SQL query.

Instead of the guestbook idea, (that was the first that came to my head) what other reasons might you use an xml file to store data instead of mysql? Or what reasons is there of using an xml file instead of mysql in some circumstances?

I created the XMLQuery object(I add to it every now and again when needed) for a small project allowing the community to find training and edcuational workshops in their local area. Because the site was small and low traffic XML was a natural choice. The main advantage was that, it fitted in nicely with my GoogleMaps code which delivered map pins to the browser's map using a 'locations.xml' file(table). Giving AJAX a more direct access to stored data can make for easier scripting, not to mention a big performance boost for your web2.0 application if data security is not an issue.

I know about reading and writing site content to mysql, seems easier to use mysql though going by dave's post ;)

The DOM is very important to learn. Taking the time to learn it will be a big feather in your cap for both front and back end scripting. We live in the 'web services' generation where most site to site communication is done in an xml format. The DOM plays a major role in web application plugins and, even if you never create a web service, chances are, sooner or later you will have to plug your application into one.