]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/XmlElement.php
prevent some warnings in corner cases
[SourceForge/phpwiki.git] / lib / XmlElement.php
1 <?php rcs_id('$Id: XmlElement.php,v 1.30 2004-06-19 12:33:25 rurban Exp $');
2 /**
3  * Code for writing XML.
4  * @author: Jeff Dairiki
5  *
6  */
7
8 /**
9  * A sequence of (zero or more) XmlElements (possibly interspersed with
10  * plain strings (CDATA).
11  */
12 class XmlContent
13 {
14     function XmlContent (/* ... */) {
15         $this->_content = array();
16         $this->_pushContent_array(func_get_args());
17     }
18
19     function pushContent ($arg /*, ...*/) {
20         if (func_num_args() > 1)
21             $this->_pushContent_array(func_get_args());
22         elseif (is_array($arg))
23             $this->_pushContent_array($arg);
24         else
25             $this->_pushContent($arg);
26     }
27
28     function _pushContent_array ($array) {
29         foreach ($array as $item) {
30             if (is_array($item))
31                 $this->_pushContent_array($item);
32             else
33                 $this->_pushContent($item);
34         }
35     }
36
37     function _pushContent ($item) {
38         if (get_class($item) == 'xmlcontent')
39             array_splice($this->_content, count($this->_content), 0,
40                          $item->_content);
41         else
42             $this->_content[] = $item;
43     }
44
45     function unshiftContent ($arg /*, ...*/) {
46         if (func_num_args() > 1)
47             $this->_unshiftContent_array(func_get_args());
48         elseif (is_array($arg))
49             $this->_unshiftContent_array($arg);
50         else
51             $this->_unshiftContent($arg);
52     }
53
54     function _unshiftContent_array ($array) {
55         foreach (array_reverse($array) as $item) {
56             if (is_array($item))
57                 $this->_unshiftContent_array($item);
58             else
59                 $this->_unshiftContent($item);
60         }
61     }
62
63     function _unshiftContent ($item) {
64         if (get_class($item) == 'xmlcontent')
65             array_splice($this->_content, 0, 0, $item->_content);
66         else
67             array_unshift($this->_content, $item);
68     }
69     
70     function getContent () {
71         return $this->_content;
72     }
73
74     function setContent ($arg /* , ... */) {
75         $this->_content = array();
76         $this->_pushContent_array(func_get_args());
77     }
78
79     function printXML () {
80         foreach ($this->_content as $item) {
81             if (is_object($item)) {
82                 if (method_exists($item, 'printxml'))
83                     $item->printXML();
84                 elseif (method_exists($item, 'asxml'))
85                     echo $item->asXML();
86                 elseif (method_exists($item, 'asstring'))
87                     echo $this->_quote($item->asString());
88                 else
89                     printf("==Object(%s)==", get_class($item));
90             }
91             else
92                 echo $this->_quote((string) $item);
93         }
94     }
95
96     function asXML () {
97         $xml = '';
98         foreach ($this->_content as $item) {
99             if (is_object($item)) {
100                 if (method_exists($item, 'asxml'))
101                     $xml .= $item->asXML();
102                 elseif (method_exists($item, 'asstring'))
103                     $xml .= $this->_quote($item->asString());
104                 else
105                     $xml .= sprintf("==Object(%s)==", get_class($item));
106             }
107             else
108                 $xml .= $this->_quote((string) $item);
109         }
110         return $xml;
111     }
112
113     function asPDF () {
114         $pdf = '';
115         foreach ($this->_content as $item) {
116             if (is_object($item)) {
117                 if (method_exists($item, 'aspdf'))
118                     $pdf .= $item->asPDF();
119                 elseif (method_exists($item, 'asstring'))
120                     $pdf .= $this->_quote($item->asString());
121                 else
122                     $pdf .= sprintf("==Object(%s)==", get_class($item));
123             }
124             else
125                 $pdf .= $this->_quote((string) $item);
126         }
127         return $pdf;
128     }
129
130     function asString () {
131         $val = '';
132         foreach ($this->_content as $item) {
133             if (is_object($item)) {
134                 if (method_exists($item, 'asstring'))
135                     $val .= $item->asString();
136                 else
137                     $val .= sprintf("==Object(%s)==", get_class($item));
138             }
139             else
140                 $val .= (string) $item;
141         }
142         return trim($val);
143     }
144
145
146     /**
147      * See if element is empty.
148      *
149      * Empty means it has no content.
150      * @return bool True if empty.
151      */
152     function isEmpty () {
153         if (empty($this->_content))
154             return true;
155         foreach ($this->_content as $x) {
156             if (is_string($x) ? strlen($x) : !empty($x))
157                 return false;
158         }
159         return true;
160     }
161     
162     function _quote ($string) {
163         if (!$string) return $string;
164         if (check_php_version(4,1) and isset($GLOBALS['charset']))
165             return htmlspecialchars($string,ENT_COMPAT,$GLOBALS['charset']);
166         else
167             return htmlspecialchars($string);
168     }
169 };
170
171 /**
172  * An XML element.
173  *
174  * @param $tagname string Tag of html element.
175  */
176 class XmlElement extends XmlContent
177 {
178     function XmlElement ($tagname /* , $attr_or_content , ...*/) {
179         //FIXME: php5 incompatible
180         $this->XmlContent();
181         $this->_init(func_get_args());
182     }
183
184     function _init ($args) {
185         if (!is_array($args))
186             $args = func_get_args();
187
188         assert(count($args) >= 1);
189         //assert(is_string($args[0]));
190         $this->_tag = array_shift($args);
191         
192         if ($args && is_array($args[0]))
193             $this->_attr = array_shift($args);
194         else {
195             $this->_attr = array();
196             if ($args && $args[0] === false)
197                 array_shift($args);
198         }
199
200         $this->setContent($args);
201     }
202     
203     /** Methods only needed for XmlParser,
204      *  to be fully compatible to perl Html::Element
205      */
206     function __destruct () {
207         foreach ($this->getChildren() as $node) {
208             $node->__destruct();
209         }
210         unset($this->_tag);
211         unset($this->_attr);
212         unset($this->_content);
213     }
214
215     function getChildren () {
216         return $this->_children;
217     }
218
219     function hasChildren () {
220         return !empty($this->_children);
221     }
222     /* End XmlParser Methods
223      */
224
225     function getTag () {
226         return $this->_tag;
227     }
228     
229     function setAttr ($attr, $value = false) {
230         if (is_array($attr)) {
231             assert($value === false);
232             foreach ($attr as $a => $v) {
233                 $this->_attr[strtolower($a)] = $v;
234                 //$this->set($a, $v);
235             }
236             return;
237         }
238
239         assert(is_string($attr));
240             
241         if ($value === false) {
242             unset($this->_attr[$attr]);
243         }
244         else {
245             if (is_bool($value))
246                 $value = $attr;
247             $this->_attr[$attr] = (string) $value;
248         }
249
250         if ($attr == 'class')
251             unset($this->_classes);
252     }
253
254     function getAttr ($attr) {
255         if ($attr == 'class')
256             $this->_setClasses();
257
258         if (isset($this->_attr[$attr]))
259             return $this->_attr[$attr];
260         else
261             return false;
262     }
263
264     function _getClasses() {
265         if (!isset($this->_classes)) {
266             $this->_classes = array();
267             if (isset($this->_attr['class'])) {
268                 $classes = explode(' ', (string) $this->_attr['class']);
269                 foreach ($classes as $class) {
270                     $class = trim($class);
271                     if ($class)
272                         $this->_classes[$class] = $class;
273                 }
274             }
275         }
276         return $this->_classes;
277     }
278
279     function _setClasses() {
280         if (isset($this->_classes)) {
281             if ($this->_classes)
282                 $this->_attr['class'] = join(' ', $this->_classes);
283             else
284                 unset($this->_attr['class']);
285         }
286     }
287
288     /**
289      * Manipulate the elements CSS class membership.
290      *
291      * This adds or remove an elements membership
292      * in a give CSS class.
293      *
294      * @param $class string
295      *
296      * @param $in_class bool
297      *   If true (the default) the element is added to class $class.
298      *   If false, the element is removed from the class.
299      */
300     function setInClass($class, $in_class=true) {
301         $this->_getClasses();
302         $class = trim($class);
303         if ($in_class)
304             $this->_classes[$class] = $class;
305         else 
306             unset($this->_classes[$class]);
307     }
308
309     /**
310      * Is element in a given (CSS) class?
311      *
312      * This checks for the presence of a particular class in the
313      * elements 'class' attribute.
314      *
315      * @param $class string  The class to check for.
316      * @return bool True if the element is a member of $class.
317      */
318     function inClass($class) {
319         $this->_parseClasses();
320         return isset($this->_classes[trim($class)]);
321     }
322
323     function startTag() {
324         $start = "<" . $this->_tag;
325         $this->_setClasses();
326         foreach ($this->_attr as $attr => $val) {
327             if (is_bool($val)) {
328                 if (!$val)
329                     continue;
330                 $val = $attr;
331             }
332             $qval = str_replace("\"", '&quot;', $this->_quote((string)$val));
333             $start .= " $attr=\"$qval\"";
334         }
335         $start .= ">";
336         return $start;
337     }
338
339     function emptyTag() {
340         return substr($this->startTag(), 0, -1) . "/>";
341     }
342
343     
344     function endTag() {
345         return "</$this->_tag>";
346     }
347     
348         
349     function printXML () {
350         if ($this->isEmpty())
351             echo $this->emptyTag();
352         else {
353             echo $this->startTag();
354             // FIXME: The next two lines could be removed for efficiency
355             if (!$this->hasInlineContent())
356                 echo "\n";
357             XmlContent::printXML();
358             echo "</$this->_tag>";
359         }
360         if (!$this->isInlineElement())
361             echo "\n";
362     }
363
364     function asXML () {
365         if ($this->isEmpty()) {
366             $xml = $this->emptyTag();
367         }
368         else {
369             $xml = $this->startTag();
370             // FIXME: The next two lines could be removed for efficiency
371             if (!$this->hasInlineContent())
372                 $xml .= "\n";
373             $xml .= XmlContent::asXML();
374             $xml .= "</$this->_tag>";
375         }
376         if (!$this->isInlineElement())
377             $xml .= "\n";
378         return $xml;
379     }
380
381     /**
382      * Can this element have inline content?
383      *
384      * This is a hack, but is probably the best one can do without
385      * knowledge of the DTD...
386      */
387     function hasInlineContent () {
388         // This is a hack.
389         if (empty($this->_content))
390             return true;
391         if (is_object($this->_content[0]))
392             return false;
393         return true;
394     }
395     
396     /**
397      * Is this element part of inline content?
398      *
399      * This is a hack, but is probably the best one can do without
400      * knowledge of the DTD...
401      */
402     function isInlineElement () {
403         return false;
404     }
405     
406 };
407
408 class RawXml {
409     function RawXml ($xml_text) {
410         $this->_xml = $xml_text;
411     }
412
413     function printXML () {
414         echo $this->_xml;
415     }
416
417     function asXML () {
418         return $this->_xml;
419     }
420
421     function isEmpty () {
422         return empty($this->_xml);
423     }
424 }
425
426 class FormattedText {
427     function FormattedText ($fs /* , ... */) {
428         if ($fs !== false) {
429             $this->_init(func_get_args());
430         }
431     }
432
433     function _init ($args) {
434         $this->_fs = array_shift($args);
435
436         // PHP's sprintf doesn't support variable width specifiers,
437         // like sprintf("%*s", 10, "x"); --- so we won't either.
438         $m = array();
439         if (! preg_match_all('/(?<!%)%(\d+)\$/x', $this->_fs, $m)) {
440             $this->_args  = $args;
441         }
442         else {
443             // Format string has '%2$s' style argument reordering.
444             // PHP doesn't support this.
445             if (preg_match('/(?<!%)%[- ]?\d*[^- \d$]/x', $this->_fs)) // $fmt
446                 // literal variable name substitution only to keep locale
447                 // strings uncluttered
448                 trigger_error(sprintf(_("Can't mix '%s' with '%s' type format strings"),
449                                       '%1\$s','%s'), E_USER_WARNING);
450         
451             $this->_fs = preg_replace('/(?<!%)%\d+\$/x', '%', $this->_fs);
452
453             $this->_args = array();
454             foreach($m[1] as $argnum) {
455                 if ($argnum < 1 || $argnum > count($args))
456                     trigger_error(sprintf("%s: argument index out of range", 
457                                           $argnum), E_USER_WARNING);
458                 $this->_args[] = $args[$argnum - 1];
459             }
460         }
461     }
462
463     function asXML () {
464         // Not all PHP's have vsprintf, so...
465         $args[] = XmlElement::_quote((string)$this->_fs);
466         foreach ($this->_args as $arg)
467             $args[] = AsXML($arg);
468         return call_user_func_array('sprintf', $args);
469     }
470
471     function printXML () {
472         // Not all PHP's have vsprintf, so...
473         $args[] = XmlElement::_quote((string)$this->_fs);
474         foreach ($this->_args as $arg)
475             $args[] = AsXML($arg);
476         call_user_func_array('printf', $args);
477     }
478
479     function asString() {
480         $args[] = $this->_fs;
481         foreach ($this->_args as $arg)
482             $args[] = AsString($arg);
483         return call_user_func_array('sprintf', $args);
484     }
485 }
486
487 function PrintXML ($val /* , ... */ ) {
488     if (func_num_args() > 1) {
489         foreach (func_get_args() as $arg)
490             PrintXML($arg);
491     }
492     elseif (is_object($val)) {
493         if (method_exists($val, 'printxml'))
494             $val->printXML();
495         elseif (method_exists($val, 'asxml')) {
496             echo $val->asXML();
497         }
498         elseif (method_exists($val, 'asstring'))
499             echo XmlContent::_quote($val->asString());
500         else
501             printf("==Object(%s)==", get_class($val));
502     }
503     elseif (is_array($val)) {
504         // DEPRECATED:
505         // Use XmlContent objects instead of arrays for collections of XmlElements.
506         trigger_error("Passing arrays to PrintXML() is deprecated: (" . AsXML($val, true) . ")",
507                       E_USER_NOTICE);
508         foreach ($val as $x)
509             PrintXML($x);
510     }
511     else
512         echo (string)XmlContent::_quote((string)$val);
513 }
514
515 function AsXML ($val /* , ... */) {
516     static $nowarn;
517
518     if (func_num_args() > 1) {
519         $xml = '';
520         foreach (func_get_args() as $arg)
521             $xml .= AsXML($arg);
522         return $xml;
523     }
524     elseif (is_object($val)) {
525         if (method_exists($val, 'asxml'))
526             return $val->asXML();
527         elseif (method_exists($val, 'asstring'))
528             return XmlContent::_quote($val->asString());
529         else
530             return sprintf("==Object(%s)==", get_class($val));
531     }
532     elseif (is_array($val)) {
533         // DEPRECATED:
534         // Use XmlContent objects instead of arrays for collections of XmlElements.
535         if (empty($nowarn)) {
536             $nowarn = true;
537             trigger_error("Passing arrays to AsXML() is deprecated: (" . AsXML($val) . ")",
538                           E_USER_NOTICE);
539             unset($nowarn);
540         }
541         $xml = '';
542         foreach ($val as $x)
543             $xml .= AsXML($x);
544         return $xml;
545     }
546     else
547         return XmlContent::_quote((string)$val);
548 }
549
550 function AsString ($val) {
551     if (func_num_args() > 1) {
552         $str = '';
553         foreach (func_get_args() as $arg)
554             $str .= AsString($arg);
555         return $str;
556     }
557     elseif (is_object($val)) {
558         if (method_exists($val, 'asstring'))
559             return $val->asString();
560         else
561             return sprintf("==Object(%s)==", get_class($val));
562     }
563     elseif (is_array($val)) {
564         // DEPRECATED:
565         // Use XmlContent objects instead of arrays for collections of XmlElements.
566         trigger_error("Passing arrays to AsString() is deprecated", E_USER_NOTICE);
567         $str = '';
568         foreach ($val as $x)
569             $str .= AsString($x);
570         return $str;
571     }
572     
573     return (string) $val;
574 }
575
576
577 function fmt ($fs /* , ... */) {
578     $s = new FormattedText(false);
579
580     $args = func_get_args();
581     $args[0] = _($args[0]);
582     $s->_init($args);
583     return $s;
584 }
585     
586 // (c-file-style: "gnu")
587 // Local Variables:
588 // mode: php
589 // tab-width: 8
590 // c-basic-offset: 4
591 // c-hanging-comment-ender-p: nil
592 // indent-tabs-mode: nil
593 // End:   
594 ?>