1<?php
2	/*******************************************************************\
3	* phpGroupWare API - XML tools                                      *
4	* Written by Dan Kuykendall <seek3r@phpgroupware.org>               *
5	* and Bettina Gille [ceb@phpgroupware.org]                          *
6	* and Ralf Becker <ralfbecker@outdoortraining.de>                   *
7	* Michael Totschnig: changed function export_var, so that
8	* Copyright (C) 2002 Dan Kuykendall, Bettina Gille, Ralf Becker     *
9	* ----------------------------------------------------------------- *
10	* This library is part of the phpGroupWare API                      *
11	* ----------------------------------------------------------------- *
12	* This library is free software; you can redistribute it and/or     *
13	* modify it under the terms of the GNU General Public License as    *
14	* published by the Free Software Foundation; either version 2 of    *
15	* the License, or (at your option) any later version.               *
16	*                                                                   *
17	* This program is distributed in the hope that it will be useful,   *
18	* but WITHOUT ANY WARRANTY; without even the implied warranty of    *
19	* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU  *
20	* General Public License for more details.                          *
21	*                                                                   *
22	* You should have received a copy of the GNU General Public License *
23	* along with this program; if not, write to the Free Software       *
24	* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.         *
25	\*******************************************************************/
26  /* $Id: class.xmltool2.inc.php 11698 2003-01-17 03:37:53Z totschnig $ */
27
28	// Michael Totschnig: this is a variant of xmltool where export_var returns subelements
29	// in a subarray, even if there is only one of them
30	// for example <dir><app>myapp</app></dir> is exported to
31	// array('dir' => array('app' => array('myapp'))
32	// instead of
33	// array('dir' => array('app' => 'myapp')
34	// this helps interpreting the data returned
35
36	function var2xml($name, $data)
37	{
38		$doc = new xmltool2('root','','');
39		return $doc->import_var($name,$data,True,True);
40	}
41
42	class xmltool2
43	{
44		/* for root nodes */
45		var $xmlversion = '1.0';
46		var $doctype = Array();
47		/* shared */
48		var $node_type = '';
49		var $name = '';
50		var $data_type;
51		var $data;
52		/* for nodes */
53		var $attributes = Array();
54		var $comments = Array();
55		var $indentstring = "\t";
56
57		/* start the class as either a root or a node */
58		function xmltool2 ($node_type = 'root', $name='',$indentstring="\t")
59		{
60			$this->node_type = $node_type;
61			$this->indentstring = $indentstring;
62			if ($this->node_type == 'node')
63			{
64				if($name !== '')
65				{
66					$this->name = $name;
67				}
68				else
69				{
70					echo 'You must name node type objects<br>';
71					exit;
72				}
73			}
74		}
75
76		function set_version ($version = '1.0')
77		{
78			$this->xmlversion = $version;
79			return True;
80		}
81
82		function set_doctype ($name, $uri = '')
83		{
84			if ($this->node_type == 'root')
85			{
86				$this->doctype[$name] = $uri;
87				return True;
88			}
89			else
90			{
91				return False;
92			}
93		}
94
95		function add_node ($node_object, $name = '')
96		{
97			switch ($this->node_type)
98			{
99				case 'root':
100					if (is_object($node_object))
101					{
102						$this->data = $node_object;
103					}
104					else
105					{
106						$this->data = $this->import_var($name, $node_object);
107					}
108					break;
109				case 'node':
110					if(!is_array($this->data))
111					{
112						$this->data = Array();
113						$this->data_type = 'node';
114					}
115					if (is_object($node_object))
116					{
117						if ($name != '')
118						{
119							$this->data[$name] = $node_object;
120						}
121						else
122						{
123							$this->data[] = $node_object;
124						}
125					}
126					else
127					{
128						$this->data[$name] = $this->import_var($name, $node_object);
129					}
130					return True;
131					break;
132			}
133		}
134
135		function get_node ($name = '')	// what is that function doing: NOTHING !!!
136		{
137			switch	($this->data_type)
138			{
139				case 'root':
140					break;
141				case 'node':
142					break;
143				case 'object':
144					break;
145			}
146
147		}
148
149		function set_value ($string)
150		{
151			$this->data = $string;
152			$this->data_type = 'value';
153			return True;
154		}
155
156		function get_value ()
157		{
158			if($this->data_type == 'value')
159			{
160				return $this->data;
161			}
162			else
163			{
164				return False;
165			}
166		}
167
168		function set_attribute ($name, $value = '')
169		{
170			$this->attributes[$name] = $value;
171			return True;
172		}
173
174		function get_attribute ($name)
175		{
176			return $this->attributes[$name];
177		}
178
179		function get_attributes ()
180		{
181			return $this->attributes;
182		}
183
184		function add_comment ($comment)
185		{
186			$this->comments[] = $comment;
187			return True;
188		}
189
190		function import_var($name, $value,$is_root=False,$export_xml=False)
191		{
192			//echo "<p>import_var: this->indentstring='$this->indentstring'</p>\n";
193			$node = new xmltool2('node',$name,$this->indentstring);
194			switch (gettype($value))
195			{
196				case 'string':
197				case 'integer':
198				case 'double':
199				case 'NULL':
200					$node->set_value($value);
201					break;
202				case 'boolean':
203					if($value == True)
204					{
205						$node->set_value('1');
206					}
207					else
208					{
209						$node->set_value('0');
210					}
211					break;
212				case 'array':
213					$new_index = False;
214					while (list ($idxkey, $idxval) = each ($value))
215					{
216						if(is_array($idxval))
217						{
218							while (list ($k, $i) = each ($idxval))
219							{
220								if (is_int($k))
221								{
222									$new_index = True;
223								}
224							}
225						}
226					}
227					reset($value);
228					while (list ($key, $val) = each ($value))
229					{
230						if($new_index)
231						{
232							$keyname = $name;
233							$nextkey = $key;
234						}
235						else
236						{
237							$keyname = $key;
238							$nextkey = $key;
239						}
240						switch (gettype($val))
241						{
242							case 'string':
243							case 'integer':
244							case 'double':
245							case 'NULL':
246								$subnode = new xmltool2('node', $nextkey,$this->indentstring);
247								$subnode->set_value($val);
248								$node->add_node($subnode);
249								break;
250							case 'boolean':
251								$subnode = new xmltool2('node', $nextkey,$this->indentstring);
252								if($val == True)
253								{
254									$subnode->set_value('1');
255								}
256								else
257								{
258									$subnode->set_value('0');
259								}
260								$node->add_node($subnode);
261								break;
262							case 'array':
263								list($first_key) = each($val); reset($val);
264								if($new_index && is_int($first_key))
265								{
266									while (list ($subkey, $subval) = each ($val))
267									{
268										$node->add_node($this->import_var($nextkey, $subval));
269									}
270								}
271								else
272								{
273									$subnode = $this->import_var($nextkey, $val);
274									$node->add_node($subnode);
275								}
276								break;
277							case 'object':
278								$subnode = new xmltool2('node', $nextkey,$this->indentstring);
279								$subnode->set_value('PHP_SERIALIZED_OBJECT&:'.serialize($val));
280								$node->add_node($subnode);
281								break;
282							case 'resource':
283								echo 'Halt: Cannot package PHP resource pointers into XML<br>';
284								exit;
285							default:
286								echo 'Halt: Invalid or unknown data type<br>';
287								exit;
288						}
289
290					}
291					break;
292				case 'object':
293					$node->set_value('PHP_SERIALIZED_OBJECT&:'.serialize($value));
294					break;
295				case 'resource':
296					echo 'Halt: Cannot package PHP resource pointers into XML<br>';
297					exit;
298				default:
299					echo 'Halt: Invalid or unknown data type<br>';
300					exit;
301			}
302
303			if($is_root)
304			{
305				$this->add_node($node);
306				if($export_xml)
307				{
308					$xml = $this->export_xml();
309					return $xml;
310				}
311				else
312				{
313					return True;
314				}
315			}
316			else
317			{
318				$this->add_node($node);
319				return $node;
320			}
321		}
322
323		function export_struct()
324		{
325			if($this->node_type == 'root')
326			{
327				return $this->data->export_struct();
328			}
329
330			$retval['tag'] = $this->name;
331			$retval['attributes'] = $this->attributes;
332			if($this->data_type != 'node')
333			{
334				$found_at = strstr($this->data,'PHP_SERIALIZED_OBJECT&:');
335				if($found_at != False)
336				{
337					$retval['value'] = unserialize(str_replace ('PHP_SERIALIZED_OBJECT&:', '', $this->data));
338				}
339				else
340				{
341					$retval['value'] = $this->data;
342				}
343				return $retval;
344			}
345			else
346			{
347				reset($this->data);
348				while(list($key,$val) = each($this->data))
349				{
350					$retval['children'][] = $val->export_struct();
351				}
352				return $retval;
353			}
354		}
355
356
357		function import_xml_children($data, &$i, $parent_node)
358		{
359			while (++$i < count($data))
360			{
361				switch ($data[$i]['type'])
362				{
363					case 'cdata':
364					case 'complete':
365						$node = new xmltool2('node',$data[$i]['tag'],$this->indentstring);
366						if(is_array($data[$i]['attributes']) && count($data[$i]['attributes']) > 0)
367						{
368							while(list($k,$v)=each($data[$i]['attributes']))
369							{
370								$node->set_attribute($k,$v);
371							}
372						}
373						$node->set_value($data[$i]['value']);
374						$parent_node->add_node($node);
375						break;
376					case 'open':
377						$node = new xmltool2('node',$data[$i]['tag'],$this->indentstring);
378						if(is_array($data[$i]['attributes']) && count($data[$i]['attributes']) > 0)
379						{
380							while(list($k,$v)=each($data[$i]['attributes']))
381							{
382								$node->set_attribute($k,$v);
383							}
384						}
385
386						$node = $this->import_xml_children($data, $i, $node);
387						$parent_node->add_node($node);
388						break;
389					case 'close':
390						return $parent_node;
391				}
392			}
393		}
394
395		function import_xml($xmldata)
396		{
397			$parser = xml_parser_create();
398			xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
399			xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE,   1);
400			xml_parse_into_struct($parser, $xmldata, $vals, $index);
401			xml_parser_free($parser);
402			unset($index);
403			$node = new xmltool2('node',$vals[0]['tag'],$this->indentstring);
404			if(isset($vals[0]['attributes']))
405			{
406				while(list($key,$value) = each($vals[0]['attributes']))
407				{
408					$node->set_attribute($key, $value);
409				}
410			}
411			switch ($vals[0]['type'])
412			{
413				case 'complete':
414					$node->set_value($vals[0]['value']);
415					break;
416				case 'cdata':
417					$node->set_value($vals[0]['value']);
418					break;
419				case 'open':
420					$node = $this->import_xml_children($vals, $i = 0, $node);
421					break;
422				case 'closed':
423					exit;
424			}
425			$this->add_node($node);
426		}
427
428
429		function export_var()
430		{
431			if($this->node_type == 'root')
432			{
433				return $this->data->export_var();
434			}
435
436			if($this->data_type != 'node')
437			{
438				$found_at = strstr($this->data,'PHP_SERIALIZED_OBJECT&:');
439				if($found_at != False)
440				{
441					return unserialize(str_replace ('PHP_SERIALIZED_OBJECT&:', '', $this->data));
442				}
443				return $this->data;
444			}
445			else
446			{
447				reset($this->data);
448				while(list($key,$val) = each($this->data))
449				{
450					$return_array[$val->name][] = $val->export_var();
451				}
452				return $return_array;
453			}
454		}
455
456		function export_xml($indent = 1)
457		{
458			if ($this->node_type == 'root')
459			{
460				$result = '<?xml version="'.$this->xmlversion.'" encoding="' . lang('charset') . '"?>'."\n";
461				if(count($this->doctype) == 1)
462				{
463					list($doctype_name,$doctype_uri) = each($this->doctype);
464					$result .= '<!DOCTYPE '.$doctype_name.' SYSTEM "'.$doctype_uri.'">'."\n";
465				}
466				if(count($this->comments) > 0 )
467				{
468					//reset($this->comments);
469					while(list($key,$val) = each ($this->comments))
470					{
471						$result .= "<!-- $val -->\n";
472					}
473				}
474				if(is_object($this->data))
475				{
476					$indent = 0;
477					$result .= $this->data->export_xml($indent);
478				}
479				return $result;
480			}
481			else /* For node objects */
482			{
483				for ($i = 0; $i < $indent; $i++)
484				{
485					$indentstring .= $this->indentstring;
486				}
487
488				$result = $indentstring.'<'.$this->name;
489				if(count($this->attributes) > 0 )
490				{
491					reset($this->attributes);
492					while(list($key,$val) = each ($this->attributes))
493					{
494						$result .= ' '.$key.'="'.htmlspecialchars($val).'"';
495					}
496				}
497
498				$endtag_indent = $indentstring;
499				if (empty($this->data_type))
500				{
501					$result .= '/>'."\n";
502				}
503				else
504				{
505					$result .= '>';
506
507					switch ($this->data_type)
508					{
509						case 'value':
510							if(is_array($this->data))
511							{
512								$type_error = True;
513								break;
514							}
515
516							/*if(preg_match("(&|<)", $this->data))	// this is unnecessary with htmlspecialchars($this->data)
517							{
518								$result .= '<![CDATA['.$this->data.']]>';
519								$endtag_indent = '';
520							}
521							else*/if(strlen($this->data) > 30 && !empty($this->indentstring))
522							{
523								$result .= "\n".$indentstring.$this->indentstring.htmlspecialchars($this->data)."\n";
524								$endtag_indent = $indentstring;
525							}
526							else
527							{
528								$result .= htmlspecialchars($this->data);
529								$endtag_indent = '';
530							}
531							break;
532						case 'node':
533							$result .= "\n";
534							if(!is_array($this->data))
535							{
536								$type_error = True;
537								break;
538							}
539
540							$subindent = $indent+1;
541							reset($this->data);
542							while(list($key,$val) = each ($this->data))
543							{
544								if(is_object($val))
545								{
546									$result .= $val->export_xml($subindent);
547								}
548							}
549							break;
550						default:
551						if($this->data != '')
552						{
553							echo 'Invalid or unset data type ('.$this->data_type.'). This should not be possible if using the class as intended<br>';
554						}
555					}
556
557					if ($type_error)
558					{
559						echo 'Invalid data type. Tagged as '.$this->data_type.' but data is '.gettype($this->data).'<br>';
560					}
561
562					$result .= $endtag_indent.'</'.$this->name.'>';
563					if($indent != 0)
564					{
565						$result .= "\n";
566					}
567				}
568				if(count($this->comments) > 0 )
569				{
570					reset($this->comments);
571					while(list($key,$val) = each ($this->comments))
572					{
573						$result .= $endtag_indent."<!-- $val -->\n";
574					}
575				}
576				return $result;
577			}
578		}
579	}
580
581	class xmlnode extends xmltool2
582	{
583		function xmlnode($name)
584		{
585			$this->xmltool2('node',$name);
586		}
587	}
588
589	class xmldoc extends xmltool2
590	{
591		function xmldoc($version = '1.0')
592		{
593			$this->xmltool2('root');
594			$this->set_version($version);
595		}
596
597		function add_root($root_node)
598		{
599			return $this->add_node($root_node);
600		}
601	}
602
603?>
604