<?php


/****************
 * Todo
 ****************
 * Test this code!
 * 
 * Add support of processing instructions (<?....?>, e.g. xslt support)
 * Add a shortcut for adding an xslt transformation stylesheet
 * 
 * Allow customisation of the characters used for indentation
 * Allow customisation of the newline character(s)
 */

class simplexmlwriter {
	/*
	 * 
	 * Constants
	 * 
	 */
	
	//the XML doctype
	const xml_doctype = '<?xml version="1.0" encoding="utf-8"?>';
	
	//the generator information
	const generator_info = "<!-- Generated by simplexmlwriter.php, which was built by Starbeamrainbowlabs -->";
	
	/*
	 * 
	 * Properties
	 * 
	 */
	
	//whether we should prettyprint the xml - remember to set this first!
	public $prettyprint = true;
	
	//the currenttly generated XML - don't reference this directly as it may have unclosed tags and therefore invalid XML
	protected $xml = "";
	
	//the currently open tags
	protected $opentags = [];
	
	/*
	 * 
	 * Functions
	 * 
	 */
	
	//begin constructing XMl
	protected function start()
	{
		$this->add($this::xml_doctype);
		$this->add($this::generator_info);
	}
	
	public function add_xslt($uri = "")
	{
		$this->add("<?xml-stylesheet type=\"text/xsl\" href=\"$uri\"?>");
	}
	
	//open a tag
	public function open($tagname = "root", $attributes = [])
	{
		$attrtext = "";
		foreach($attributes as $name => $value)
			$attrtext .= "$name=\"$value\" ";
		
		$attrtext = " " . trim($attrtext);
		
		if(strlen($attrtext) === 1)
			$attrtext = "";
		
		$this->add("<$tagname$attrtext>");
		
		$this->opentags[] = $tagname; //add the tag name to the list of currently open tags
	}
	
	//add some plain text
	public function addtext($text = "", $newline = true)
	{
		$this->add($text, $newline);
	}
	
	//close a tag
	public function close()
	{
		$this->add("</" . array_pop($this->opentags) . ">");
	}
	
	//close all open tags
	public function closeall()
	{
		while(count($this->opentags) > 0)
		{
			$this->close();
		}
	}
	
	//shortcut to add a whole tag
	public function addtag($tagname = "root", $attributes = [], $text = "")
	{
		$attrtext = "";
		foreach($attributes as $name => $value)
			$attrtext .= "$name=\"$value\" ";
		
		$attrtext = " " . trim($attrtext);
		
		if(strlen($attrtext) === 1)
			$attrtext = "";
		
		$this->add("<$tagname$attrtext>$text</$tagname>");
	}
	
	//add a comment
	public function addcomment($text = "")
	{
		$this->add("<!-- $text -->");
	}
	
	//get a rendered version of the currently generated XML
	public function render()
	{
		$result = $this->xml;
		
		$revopentags = array_reverse($this->opentags);
		foreach($revopentags as $tagname)
		{
			if($this->prettyprint)
				$result .= str_repeat("\t", count($revopentags)) . "</$tagname>\n";
			else
				$result .= "</$tagname>";
				
		}
		
		return $result;
	}
	
	//get the appropriate number of tab characters for indentation purposes
	protected function getindent()
	{
		return str_repeat("\t", count($this->opentags));
	}
	
	//function to add to the rendered XML string
	protected function add($text = "", $newline = true)
	{
		
		if($newline and $this->prettyprint)
			$this->xml .= $this->getindent();
		
		$this->xml .= $text;
		
		if($newline and $this->prettyprint)
			$this->xml .= "\n";
	}
	
	public function __toString() {
		return $this->render();
	}
	
	/*
	 * 
	 * Constuctor
	 * 
	 */
	
	function __construct() {
		$this->start();
	}
}


if(basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"]))
{
	//we were not included
	
	$xml = new simplexmlwriter();
	$xml->add_xslt("https://example.com/test.xslt");
	$xml->open("root");
	$xml->addcomment("- ------------------------------ -");
	$xml->addcomment("- -- simplexmlwriter.php test -- -");
	$xml->addcomment("- ------------------------------ -");
	foreach(range(1, 10) as $i)
	{
		$xml->open("number");
			$xml->addtag("type", [], "integer");
			$xml->open("value");
				$xml->addtext($i);
			$xml->close();
		$xml->close();
	}
	$xml->closeall();
	header("content-type: application/xml");
	echo($xml->render());
}
?>