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