<?php

/**------------------------------------------------
*
* Rooty, 2018 <rooty@rooty.me>
*
*
* This software is protected by copyright, please
* read the file COPYRIGHT.
* This program is distributed in the hope that it
* will be useful, but without any warranty; without
* even the implied warranty of merchantability or
* fitness for a particular purpose. Please
* read the file LICENCE.
*
*Fichier xml.php
*
*Ce fichier contient la classe d'accès aux données de fichier XML
*
* @author Initiance <www.initiance.com|HEURTAUX Christophe|olivier@initiance.com>
* @link www.rooty.me
* @since 2004/04/07
* @version 0.3
* @copyright Copyright &copy; 2009-2018, Rooty
* @package system_data_source
* @subpackage xml
*/

#####################################################################################################
################################# Parametrage
#####################################################################################################
/**
* declaration du system... si cette variable n'est pas définie, les fichier inclus ne marche pas...
* cette variable est déclarée sur la page d'execution du script.
*<p> if ( !defined('SYSTEM_IN') )
* {
* die("Hacking attempt");
* }</p>
*/
if ( !defined('SYSTEM_IN') )
{
 die("Hacking attempt");
}

/**
*/
define('DEBUG_XML',0);

/**
* classe Xml
*
* classe de gestion de Fichiers XML
*
* @package system_data_source
* @subpackage xml
*/
class Xml
{

/**#@+
* @access public
*/

/**
* @var chaine
* @desc Correspond au chemin local (serveur) de fichier a analyser
*/
var $filePath;

/**
* @var chaine
* @desc Correspond au jeu de caract?res utilis? par le fichier XML
*
*/
var $encoding;

/**
* @var xml_parser
* @desc objet xml parser utilis? par la classe
*/
var $xml_parser;

/**
* @var chaine
* @desc Totalit? du fichier XML dans une chaine
*/
var $xml_stream;

/**
* @var flux
* @desc flux du fichier a ouvrir
*/
var $fileStream;

/**
* @var tableau
* @desc Structure (tableau d'indices) du fichier XML
*/
var $xmlStruct;

/**
* @var tableau
* @desc Valeurs du fichier XML
*/
var $xmlValues;

/**
* @var tableau
* @desc Tableau tableau format? contenant les valeurs du fichier.
*/
var $Params;

/**
* @var booléen
* @desc variable temporaire pour SetRecursive
*/
var $SetOpen = false;

/**#@-*/

/**
*Constructeur de la classe Xml
*
*<p>Initialise le parser Xml a partir d'un fichier et d'un type.</p>
*
* @param chaine chemin du fichier
* @param entier type d'ouverture (0 pour fichier= par defaut, 1 pour string)
* @param chaine (Facultatif) jeu de caractere (ISO-8859-1,US-ASCII,UTF-8)
*/

function __construct($fichier=null, $type=0,$encoding="UTF-8")
{
	$this->encoding = $encoding;

	# Cas Source de donn?es vide
	if ($fichier!=null)
	{
		# En fonction du Type
		if ($type==0)
			$this->LoadFromFile($fichier,$encoding);
		else
			$this->LoadFromString($fichier,$encoding);
	}
}

/**
*Initialisation des variables et du parser
*
*<p> Charge le fichier .</p>
*
* @param chaine chemin du fichier 
* @param chaine (Facultatif) jeu de caractere (ISO-8859-1,US-ASCII,UTF-8)
*/
function loadFromFile($fichier,$encoding=null)
{
	/* Initialisation des variables de la classe */
	$this->filePath = $fichier;
	$this->encoding = $encoding;

	/* Initialisation du parser XML */
	$this->xml_parser = xml_parser_create($this->encoding);

	/* Ouverture et lecture du fichier XML */
	if (file_exists($this->filePath))
	{
		$this->fileStream = fopen($this->filePath,"r");

		if ($this->fileStream == null)
			addError(1,"XML","Impossible d'ouvrir le fichier XML ".$this->filePath." en lecture");
	}
	else
	{
		addError(1,"XML","Le fichier n'existe pas!!!! ".$this->filePath." ");
	}
	$this->xml_stream = fread($this->fileStream, filesize($this->filePath));
	fclose($this->fileStream);	

	/* Parsage XML dans les variables xmlStruct et xmlValues */
	xml_parse_into_struct($this->xml_parser, $this->xml_stream,  $this->xmlValues, $this->xmlStruct);
	xml_parser_free($this->xml_parser);	
}

/**
*Initialisation des variables et du parser
*
*<p> Charge le fichier .</p>
*
* @param chaine chemin du fichier 
* @param chaine (Facultatif) jeu de caractere (ISO-8859-1,US-ASCII,UTF-8)
*/
function loadFromString($string,$encoding=null)
{
	$this->encoding = $encoding;

	/* Initialisation du parser XML */
	$this->xml_parser = xml_parser_create($this->encoding);
	$this->xml_stream = $string;

	/* Parsage XML dans les variables xmlStruct et xmlValues */
	xml_parse_into_struct($this->xml_parser, $this->xml_stream, $this->xmlValues, $this->xmlStruct);
	xml_parser_free($this->xml_parser);
}

/**
* Retourne la chaine XML
*
*<P>retourne simplement la chaine XML
*
* @return chaine
*/
function getXmlString()
{
	return $this->xml_stream;	
}

/**
*Retourne les valeurs du fichier XML parse
*
*<P>retourne sous forme de tableau multidimensionnel , les valeurs du fichier XML parsé.</p>
*
* @return tableau
*/
function returnVals()
{
	return $this->xmlValues;	
}

/**
*Retourne la structure du fichier XML parsé
*
*<P>retourne sous forme de tableau multidimensionnel , la structure d'index du fichier XML parsé.</p>
*
* @return tableau
*/
function returnStruct()
{
	return $this->xmlStruct;
}

/**
*Retourne un resultat sous forme de tableau
*
*<p>Retourne le resultat du parseur XML sous forme de tableau.</p>
* 
*<p>Permet l'exploitation rapide d'un resultat XML.</p>
*
* @return structure_tableau
*/
function getLinear()
{
	$Params = array();

	foreach ($this->xmlValues as $myXmlValue)
	{
		if ($myXmlValue['type'] == 'complete')
		{
			eval( '$Params[\''.$myXmlValue['tag'].'\'][\''.$myXmlValue['attributes']['NAME'].'\'] = "'.$myXmlValue['value'].'";' );
		}
	}
	$this->xmlparams = $Params;
	return $Params;
}

function setParams($params)
{
	$this->Params = $params;	
}

/**
* 
*
*/
function hasValues()
{
	if (isset($this->Params))
	{
		if ($this->Params == null)
		{
			return false;	
		}
		else
		{
			return true;
		}
	}
	else
	{
		return false;	
	}
}

/**
*Retourne un resultat sous forme de tableau de mani?re r?cursive 
*
*<p>Retourne le resultat du parseur XML sous forme de tableau.</p>
* 
*<p>Le tableau retourn? est de la forme suivante : </p>
*
*
* @return structure_tableau
*/
function getRecursive()
{
	/*echo "------------------XML::GetRecursive()----------------------\n";
	print_r($this->xml_stream);
	echo "----------------------------------------\n";*/
	$Params = array();
	$paramsindex = "";
	$level = 0;
	$open = array();
	$complete = array();
	$open[$level] = 0;
	$complete[$level] = 0;

	foreach ($this->xmlValues as $myXmlValue)
	{
		if ($myXmlValue['type'] == 'open')
		{
			if (!isset($open[$level]) || is_null($open[$level]))
			{
				$open[$level] = 0;	
			}
			$paramsindex .= '[\''.$myXmlValue["tag"].'\']['.$open[$level].']';

			/* cas normalement non valide */
			if (isset($myXmlValue['value']) && !is_null($myXmlValue['value'])&& $myXmlValue['value'] != "")
			{
				$evalcmd='$Params'.	$paramsindex .'[\'value\'] = "'. addslashes($myXmlValue['value']) .'";';
				if (DEBUG_XML==1)
						echo $evalcmd."X<br>\n";
				eval($evalcmd);
			}

			if (isset($myXmlValue['attributes']) && !is_null($myXmlValue['attributes']))
			{
				$attribute_table = array_keys($myXmlValue['attributes']);

				foreach($attribute_table as $attribute)
				{
					$evalcmd = '$Params'.	$paramsindex .'[\'attribute_'.$attribute.'\'] = "'.$myXmlValue['attributes'][$attribute].'";';

					# DEBUG !!
					if (DEBUG_XML==1)
						echo $evalcmd."0<br>\n";

					eval($evalcmd);
				}
			}
			$open[$level] ++;
			$level++;
		}
		elseif ($myXmlValue['type'] == 'complete')
		{
			//$complete[$level]=0;
			
			/* Cas réinitialisation des éléments */
			if (!isset($complete[$level]) || is_null($complete[$level])) { $complete[$level] = 0; }

			if ($complete[$level] > 0)
			{
				if ($tmpparamindex!=$paramsindex .'[\''.$myXmlValue['tag'].'\']['.($complete[$level]-1).']')
					$complete[$level] = 0;			
			}
			/* Fin remise à zero de l'indexation */

			if (isset($open[$level -1]))
				$oldopen = $open[$level - 1] - 1 ;

			$tmpparamindex = $paramsindex .'[\''.$myXmlValue['tag'].'\']['.$complete[$level].']';

			if (isset($myXmlValue['attributes']) && !is_null($myXmlValue['attributes']))
			{
				$attribute_table = array_keys($myXmlValue['attributes']);

				foreach($attribute_table as $attribute)
				{
					$evalcmd = '$Params'.	$tmpparamindex .'[\'attribute_'.$attribute.'\'] = "'.$myXmlValue['attributes'][$attribute].'";';

					# DEBUG !!
					if (DEBUG_XML==1)
						echo $evalcmd."1<br>\n";
					eval($evalcmd);
				}
			}

			if (isset($myXmlValue['value']) && !is_null($myXmlValue['value']))
			{
				$evalcmd = '$Params'.$tmpparamindex.'[\'value\'] = "'.htmlspecialchars($myXmlValue['value'], ENT_QUOTES ).'";';

					# DEBUG !!
					if (DEBUG_XML==1)
						echo $evalcmd."2<br>\n";

				eval($evalcmd);
			}
			$complete[$level]++;
		}
		elseif ($myXmlValue['type'] == 'close')
		{
			$listindex = split("\[",str_replace("]","",$paramsindex));
			$nblist = count ($listindex);
			$paramsindex = "";

			for ($i=1;$i<$nblist-2;$i++)
				$paramsindex .= '['. $listindex[$i] .']';

			$open[$level] = 0;
			$complete[$level] = 0;
			$level--;
		}
	}
	$this->Params = $Params;
	$returnvals = array_keys($this->Params);

	############## A VOIR ####################
	# $returnParams = $Params;
	# OU : 
	$returnParams = $Params[$returnvals[0]][0];
	##########################################

	return $returnParams;
}

/**
*Modifier la valeur d'un attribut
*
* @param chaine decrit l'emplacement dans le tableau
* @param chaine nom de l'attribut a modifier 
* @param chaine valeur a attribuer 
*/
function setAttribute($node_path,$attribute,$value)
{
	$node = $this->FormatPathVariable($node_path);

	if (!$this->HasValues())
	{
		$this->GetRecursive();	
	}
	$tableau = '$this->Params';
	$evalcmd = 'if (isset('.$tableau.$node.'[\'value\']))'."\n";
	$evalcmd.= '{'."\n";
	$evalcmd.= "\t".'$value_exists = true;'."\n";
	$evalcmd.= "\t".'$tempvalue = '.$tableau.$node.'[\'value\'];'."\n";
	$evalcmd.= "\t".'unset('.$tableau.$node.'[\'value\']);'."\n";
	$evalcmd.= '}'."\n";
	$evalcmd.= "\n";
	$evalcmd.= $tableau.$node.'[\'attribute_'.$attribute.'\'] = "'.$value.'";'."\n";
	$evalcmd.= 'if (isset($value_exists) && $value_exists == true)'."\n";
	$evalcmd.= $tableau.$node.'[\'value\'] = $tempvalue;'."\n";
	eval($evalcmd);	
}

/**
*Modifier la valeur d'une balise
*
* @param chaine decrit l'emplacement dans le tableau
* @param chaine valeur a attribuer 
*/
function setValue($node_path,$value)
{
	$node = $this->FormatPathVariable($node_path);

	if (!$this->HasValues())
	{
		$this->GetRecursive();
	}

	$tableau = '$this->Params';
	$evalcmd = $tableau.$node.'[\'value\'] = "'.$value.'";';
	eval($evalcmd);
}

/**
*obtenir la valeur d'un attribut
*
* @param chaine decrit l'emplacement dans le tableau
* @param chaine nom de l'attribute a modifier
* @return chaine valeur retourn?e
*/
function getAttribute($node_path,$attribute)
{
	$node = $this->FormatPathVariable($node_path);

	if (!$this->HasValues())
	{
		$this->GetRecursive();	
	}

	$tableau = $this->Params;
	$evalcmd  = 'if (is_array($tableau'.$node.') && isset($tableau'.$node.'[\'attribute_'.$attribute.'\']))'."\n"; 
	$evalcmd .= '$retour = $tableau'.$node.'[\'attribute_'.$attribute.'\'];'."\n";
	$evalcmd .= 'else'."\n";
	$evalcmd .= '$retour = null;'."\n";
	eval($evalcmd);
	return $retour;
}

/**
*obtenir la valeur d'une balise
*
* @param chaine decrit l'emplacement dans le tableau
* @return chaine valeur retourn?e
*/
function getValue($node_path)
{
	$node = $this->FormatPathVariable($node_path);

	if (!$this->HasValues())
	{
		$this->GetRecursive();	
	}

	$tableau = $this->Params;

	$evalcmd = 'if (isset($tableau'.$node.'))'."\n"; 
	$evalcmd .= '$retour = $tableau'.$node.'[\'value\'];'."\n";
	$evalcmd .= 'else'."\n";
	$evalcmd .= '$retour = null;';

	@eval($evalcmd);

	return $retour;
}

/**
*obtenir la valeur d'une balise
*
* @param chaine decrit l'emplacement dans le tableau
*/
function deleteNode($node_path)
{
	$node = $this->FormatPathVariable($node_path);

	if (!$this->HasValues())
	{
		$this->GetRecursive();	
	}
	$tableau = '$this->Params';
	eval('unset('.$tableau.$node.');');
}

function countElements ($node_path)
{
	$node = $this->FormatPathVariable($node_path);

	$tableau = $this->Params;

	$evalcmd = 'if (isset($tableau'.$node.')) { '."\n";
	$evalcmd .= '$retour = count($tableau'.$node.');'."\n";
	$evalcmd .= '} else {'."\n";
	$evalcmd .= '$retour = 0; }';

	eval($evalcmd);
	return $retour;
}

function openTag($tag)
{
	return "<".$tag;
}

function printAttribute($attribute,$value)
{
	return " ".$attribute."=\"".$value."\"";
}

function closeTag($tag)
{
	return 	"</".$tag.">\n";
}

function hasAValue($node)
{
	$tabkeys = array_keys($node);

	foreach($tabkeys as $tabkey)
	{
			if (strstr($tabkey,"value") != false)
			{
				return true;
			}
	}
	return false;
}

/**
* Ecrire dans le fichier XML a partir du tableau multi dimensionnel
*
* aucun des parametres n'est a spécifier lors de l'appel de cette fonction.
*
* @param tableau point de départ
* @param entier decrit la profondeur dans le fichier.
* @param bool?en balise ouverte
* @param chaine balise
*/
function setRecursive($node = null,$level=0)
{
	if (!$this->HasValues())
	{
		$this->GetRecursive();
	}
	$hasattributes = false;
	$racine = 0;
	$chaine = "";
	$tabulation = "";

	for ($l=0;$l<$level;$l++)
	{
		$tabulation .= "\t";
	}

	$mynode = array();

	if ($node == null)
	{
		$mynode = $this->Params;
		$racine = 1;
		$level=0;
		$chaine = "";
	}
	else
	{
		$racine = 0;
		$mynode = $node;
	}

	$tabkeys = array();
	$tabkeys = array_keys($mynode);

	$pass = 0;

	if ($racine == 1)
	{
		$chaine='<?xml version="1.0" encoding="'.$this->encoding.'" ?>'."\n\n";
	}

	foreach ($tabkeys as $tabkey)
	{
		if (strstr($tabkey,"attribute_")!= false)
		{
			$hasattributes = true;
			list($vide,$attribut) = split("_",$tabkey);
			$chaine.=$this->PrintAttribute($attribut,$mynode[$tabkey]);
		}
	}

	if (($hasattributes == true) || ($level > 0))
	{
		if ($this->HasAValue($node))
			$chaine.=">";
		else
			$chaine.=">\n";
	}

	$tabkeys = array_keys($mynode);
		foreach ($tabkeys as $tabkey)
	{
		if (strstr($tabkey,"value")!= false)
		{
			$chaine.=$mynode[$tabkey]."";
		}
	}

	$tabkeys = array_keys($mynode);

	foreach ($tabkeys as $tabkey)
	{
		if (strstr($tabkey,"value") == false && strstr($tabkey,"attribute_") == false)
		{
			$elements = count($mynode[$tabkey]);

			for ($i=0;$i<$elements;$i++)
			{
				$chaine.=$tabulation;
				$chaine.=$this->OpenTag($tabkey);
				$chaine.=$this->SetRecursive($mynode[$tabkey][$i],$level+1);

				if (!$this->HasAValue($mynode[$tabkey][$i]))
					$chaine.=$tabulation;
					$chaine.=$this->CloseTag($tabkey);
			}
		}
	}
	return $chaine;
}

/**
*
* Ajoute ou modifie de la structure xml
*
*
*/
function setLinear($element,$attributes,$value)
{
	$this->xmlparams[$element][$attributes[0]['value']] = $value;
}

/**
*function Effacement lineaire
*
*efface lineairement un element dans le fichier XML .
*
* @param chaine nom de la balise de l'element a supprimer
* @param tableau liste des parametres de la balise
*/
function deleteLinear($element,$attributes)
{
	if (isset($this->xmlparams[$element][$attributes[0]['value']]))
	{
		unset ($this->xmlparams[$element][$attributes[0]['value']]);
		return true;
	}
	else
	{
		return false;
	}
}

/**
*Ecriture de fichier XML
*
* Prend en compte les nouveaux parametres et regenere le fichier XML final.
*
*
*/
function writeFileLinear()
{
	$keys = array_keys($this->xmlparams);
	$chaine = "<global>\n";

	foreach($keys as $key)
	{
		$attributeskeys = array_keys($this->xmlparams[$key]);

		foreach($attributeskeys as $attributekey)
		{
			$chaine .= "\t<".$key." ";
			$chaine .= "name=\"".$attributekey."\" ";

			if ($this->xmlparams[$key][$attributekey] == "")
			{
				$chaine .= "/>";
			}
			else
			{
				$chaine .= ">".	$this->xmlparams[$key][$attributekey] ."</".$key.">\n";
			}
		}
	}

	$chaine .="</global>";

	$this->fileStream = @fopen($this->filePath,"w");

	if ($this->fileStream == null)

		addError(1,"XML","Impossible d'ouvrir le fichier XML ".$this->filePath." en ecriture");

	fputs($this->fileStream,$chaine);
	fclose($this->fileStream);

	$this->Load($this->filePath,$this->encoding);
}

/**
* FormatPathVariable: formate le nom de la variable en fonction des Lib Xml
*
* @param chaine nom/chemin de la variable au format "DataObject"
* @access public
* @return chaine chaine au format des libs Xml
*/
	function formatPathVariable($pathVariable)
	{
		# Les slashes nous donnent les variables
		$systemError=0; # ignore les anciennes erreurs
		$result="";

		# Ajout du noeud racine
		if (strtoupper(substr($pathVariable,0, 4))!='XML/')
		{
			$pathVariable="XML/".$pathVariable;
		}
		$pathVariable=str_replace("//","/",$pathVariable);
		$tabArg=split("/",$pathVariable);
		$nbArg=count($tabArg);

		for($i=0;$i<$nbArg;$i++)
		{
			# les Points donne le rang de l'objet
			$tabArgAndVersion=split("\.",$tabArg[$i]);

			if (Is_array($tabArgAndVersion))
			{
				$nbArgAndVersion=count($tabArgAndVersion);
				switch ($nbArgAndVersion)
				{
					case 2:
					$nom=new Chaine($tabArgAndVersion[0]);
					if (Is_numeric($tabArgAndVersion[1]))
					{
						$result.="['".$nom->NoSpecialChar()."']"."[".$tabArgAndVersion[1]."]";
					}
					else
					{
						$systemError=2;
						$classe="DataObject";
						$message="Format de chaine incompatible, (rang non numérique)<br>Veuiller regarder les spécificités de SetData et GetData";
						addError($systemError,$classe,$message, __line__, __file__);
						return "";
					}
					break;
					
					case 1:
					$nom=new Chaine($tabArgAndVersion[0]);
					$result.="['".$nom->NoSpecialChar()."']"."[0]";
					break;
					
					default:
					$systemError=2;
					$classe="DataObject";
					$message="Format de chaine incompatible, <br>Veuiller regarder les spécificités de SetData et GetData";
					addError($systemError,$classe,$message, __line__, __file__);
					return "";
				}
			}else{
				$systemError=2;
				$classe="DataObject";
				$message="Format de chaine incompatible, <br>Veuiller regarder les spécificités de SetData et GetData".$id;
				addError($systemError,$classe,$message, __line__, __file__);
				return "";
			}
		}
		return $result;
	}
}
?>
