Got me thinking so I wrote a little class that would handle the building of the file for you, indent it nicely (not needed but does make it nicer to read) and run some checks. I'm sure this has been done already, and a lot better at that but it was a nice little lunch time project for me 🙂
Here's the code
<?php
class xmlCreate {
var $INVALID_CHARS = array('<','>','&'); //There are more but I don't have my XML book with me and I can't be bothered to check on the internet
var $INVALID_REPLACE = array('<','>','&');
var $err=null;
var $stack=array();
var $depth=0;
var $content;
/* xmlCreate - Constructor
* @return null - It's a constructor :rolleyes:
* @desc - It just adds the xml declaration to the top of the contents (assumes Latin 1)
*/
function xmlCreate() {
$this->content='<?xml version="1.0" encoding="ISO-8859-1"?>'."\n";
}
/* openElement - Open a new element at the correct indent depth
* @return bool
* @param? String $name - The element name
* @param? Array $att_names - An array of attribute names (must be the same size as the $att_vals array)
* @param Array $att_vals - An array of attribute vals (must be the same size as the $att_names array)
* @desc - This method adds an extra element to the output at the correct depth. The element and attribute names are checked for illegal characters
* - and return false they contain them. The attribute values have any illegal values replaced with their ASCII code. The element name is added
* - to the element stack and the depth is incremented.
*/
function openElement($name, $att_names=array(), $att_vals=array()) {
//Check the two arrays' lengths match up
if(count($att_names) != count($att_vals)) {
$this->err='Attribute names and values mismatch';
return false;
} else {
//Check each invalid char against the name and then each attribute name. I'm sure there's a much more efficient way of doing this but I can't
//think of it right now
for($i=0; $i<count($INVALID_CHARS); $i++) {
if(strpos($name, $INVALID_CHARS[$i]) !==false) {
$this->err='The element name contains bad characters';
return false;
}
for($j=0; $j<count($att_names); $j++) {
if(strpos($att_names, $INVALID_CHARS[$i]) !== false) {
$this->err='One or more of the attribute names contains bad characters';
return false;
}
}
}
//I've never used str_replace with all parameters as arrays so I'm not quite sure how well this will work.
$att_vals=str_replace($INVALID_CHARS, $INVALID_REPLACE, $att_vals);
//Add the name to the stack, we don't really need the depth var as we can just do a count on stack but what the hell ;)
$this->stack[]=$name;
//indent and add the element
$this->content.=str_repeat(' ', $this->depth*4).'<'.$name;
for($i=0;$i<count($att_names);$i++) {
$this->content.=' '.$att_names[$i].'="'.$att_vals[$i].'"';
}
$this->content.=">\n";
$this->depth++;
return true;
}
}
/* addContent - Add raw content
* @return bool
* @param String $content - The raw contents which is to be appended to the current content
* @desc - Not much to say here, it's a prtty simple method
*/
function addContent($content) {
//replace invalid characters and append to the content
$this->content.=str_replace($INVALID_CHARS, $INVALID_REPLACE);
return true;
}
/* closeElement - Close one or more open elements
* @return bool
* @param? int $num - Optionally add this paramters if you wish to close more than one element at a time
* @desc - Again, not much to say really, it closes elements. The element name is in the stack so it just pops it off the end,
* - decrements the depth and appends the closing tag. Note, if you try to close more elements than are open it will return false
*/
function closeElement($num=1) {
if($num>$this->depth) {
$this->err='Closing more elements than are open :/';
return false;
}
for($i=0;$i<$num;$i++) {
$this->depth--;
$element=array_pop($this->stack);
$this->content.=str_repeat(' ',$this->depth*4).'<'.$element.">\n";
}
return true;
}
/* writeToFile - Write the XML to a file
* @return bool
* @param String $file - This is the path (full or relative) of the file which the output is to be written to
* @desc - These output methods (both this one and the writeToOutput method) use the checkWrite method which checks whether
* - or not all elements have been closed
*/
function writeToFile($file) {
if(!$this->checkWrite()) {
return false;
}
if(!$fh=fopen($file, 'w')) {
$this->err='Could not open handle on file '.$file;
return false;
} else {
if(fwrite($fh, $this->content) === false) {
$this->err='Could not write to file '.$file;
fclose($fh);
return false;
} else {
fclose($fh);
return true;
}
}
}
/* writeToOutput - Write the XML to Stdout
* @return bool
* @desc - Essentially the same as writeToFile but it writes to the output using echo()
*/
function writeToStdout() {
if(!$this->checkWrite()) {
return false;
}
echo($this->content);
return true;
}
/* checkWrite - Check all elements are closed
* @return bool
* @desc - Does exactly what is says on the tin. It checks whether or not all the elements have been closed
*/
function checkWrite() {
if($this->depth>0 || count($this->stack)>0) {
$this->err='Elements still open';
return false;
} else {
return true;
}
}
/* getErr - Return the last error which occured */
function getErr() {
return $this->err;
}
}
?>
and here's an example of it in use
$xml=new xmlCreate();
if(!$xml->openElement('person', array('fname','sname'), array('bubble','ichous'))) {
echo($xml->getErr()."<br />\n");
exit();
}
if(!$xml->openElement('biography')) {
echo($xml->getErr()."<br />\n");
exit();
}
$xml->addContent('Bubble was a little boy with 500 toes, one day his toes began to doze while all around were watching, the embarasemtent of this affair made poor Bubble glow, his cheeks went red, his brow did sweat and he appeared at his own nose');
if(!$xml->closeElement(2)) {
echo($xml->getErr()."<br />\n");
exit();
}
if(!$xml->writeToFile('./out1')) {
echo($xml->getErr()."<br />\n");
exit();
}
//Converting a multi-dim array into an xml file
//I haven't tested this so it may not work
$array= //I can't be bothered to write out an example array
$xml=new xmlCreate();
$xml=xmlize(new xmlCreate(), $array);
$xml->writeToOutput();
function xmlize($xml, $array) {
foreach($array as $key => $value) {
if($xml->openElement($key)) {
echo($xml->getErr()."<br />\n");
exit();
}
if(is_array($value)) {
$xml=xmlize($xml, $value);
} else {
$xml->addContent($value);
}
$xml->closeElement();
}
return $xml;
}
HTH
Bubble