]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/XmlElement.php
Reformat code
[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
204 /**
205  * An XML element.
206  *
207  * @param $tagname string Tag of html element.
208  */
209 class XmlElement extends XmlContent
210 {
211     function XmlElement($tagname /* , $attr_or_content , ...*/)
212     {
213         //FIXME: php5 incompatible
214         $this->XmlContent();
215         $this->_init(func_get_args());
216     }
217
218     function _init($args)
219     {
220         if (!is_array($args))
221             $args = func_get_args();
222
223         assert(count($args) >= 1);
224         //assert(is_string($args[0]));
225         $this->_tag = array_shift($args);
226
227         if ($args && is_array($args[0]))
228             $this->_attr = array_shift($args);
229         else {
230             $this->_attr = array();
231             if ($args && $args[0] === false)
232                 array_shift($args);
233         }
234
235         $this->setContent($args);
236     }
237
238     /** Methods only needed for XmlParser,
239      *  to be fully compatible to perl Html::Element
240      */
241     // doesn't yet work with php5 as __destruct()
242     function _destruct()
243     {
244         if ($this->hasChildren()) {
245             foreach ($this->getChildren() as $node) {
246                 $node->_destruct();
247             }
248         }
249         unset($this->_tag);
250         unset($this->_attr);
251         unset($this->_content);
252     }
253
254     function getChildren()
255     {
256         return $this->_children;
257     }
258
259     function hasChildren()
260     {
261         return !empty($this->_children);
262     }
263
264     /* End XmlParser Methods
265      */
266
267     function getTag()
268     {
269         return $this->_tag;
270     }
271
272     function setAttr($attr, $value = false)
273     {
274         if (is_array($attr)) {
275             assert($value === false);
276             foreach ($attr as $a => $v) {
277                 $this->_attr[strtolower($a)] = $v;
278                 //$this->set($a, $v);
279             }
280             return;
281         }
282
283         assert(is_string($attr));
284
285         if ($value === false) {
286             unset($this->_attr[$attr]);
287         } else {
288             if (is_bool($value))
289                 $value = $attr;
290             $this->_attr[$attr] = (string)$value;
291         }
292
293         if ($attr == 'class')
294             unset($this->_classes);
295     }
296
297     function getAttr($attr)
298     {
299         if ($attr == 'class')
300             $this->_setClasses();
301
302         if (isset($this->_attr[strtolower($attr)]))
303             return $this->_attr[strtolower($attr)];
304         else
305             return false;
306     }
307
308     function _getClasses()
309     {
310         if (!isset($this->_classes)) {
311             $this->_classes = array();
312             if (isset($this->_attr['class'])) {
313                 $classes = explode(' ', (string)$this->_attr['class']);
314                 foreach ($classes as $class) {
315                     $class = trim($class);
316                     if ($class)
317                         $this->_classes[$class] = $class;
318                 }
319             }
320         }
321         return $this->_classes;
322     }
323
324     function _setClasses()
325     {
326         if (isset($this->_classes)) {
327             if ($this->_classes)
328                 $this->_attr['class'] = join(' ', $this->_classes);
329             else
330                 unset($this->_attr['class']);
331         }
332     }
333
334     /**
335      * Manipulate the elements CSS class membership.
336      *
337      * This adds or remove an elements membership
338      * in a give CSS class.
339      *
340      * @param $class string
341      *
342      * @param $in_class bool
343      *   If true (the default) the element is added to class $class.
344      *   If false, the element is removed from the class.
345      */
346     function setInClass($class, $in_class = true)
347     {
348         $this->_getClasses();
349         $class = trim($class);
350         if ($in_class)
351             $this->_classes[$class] = $class;
352         else
353             unset($this->_classes[$class]);
354     }
355
356     /**
357      * Is element in a given (CSS) class?
358      *
359      * This checks for the presence of a particular class in the
360      * elements 'class' attribute.
361      *
362      * @param $class string  The class to check for.
363      * @return bool True if the element is a member of $class.
364      */
365     function inClass($class)
366     {
367         $this->_parseClasses();
368         return isset($this->_classes[trim($class)]);
369     }
370
371     function startTag()
372     {
373         $start = "<" . $this->_tag;
374         $this->_setClasses();
375         foreach ($this->_attr as $attr => $val) {
376             if (is_bool($val)) {
377                 if (!$val)
378                     continue;
379                 $val = $attr;
380             }
381             $qval = str_replace("\"", '&quot;', $this->_quote((string)$val));
382             $start .= " $attr=\"$qval\"";
383         }
384         $start .= ">";
385         return $start;
386     }
387
388     function emptyTag()
389     {
390         return substr($this->startTag(), 0, -1) . "/>";
391     }
392
393
394     function endTag()
395     {
396         return "</$this->_tag>";
397     }
398
399
400     function printXML()
401     {
402         if ($this->isEmpty())
403             echo $this->emptyTag();
404         else {
405             echo $this->startTag();
406             // FIXME: The next two lines could be removed for efficiency
407             if (!$this->hasInlineContent())
408                 echo "\n";
409             XmlContent::printXML();
410             echo "</$this->_tag>";
411         }
412         if (!$this->isInlineElement())
413             echo "\n";
414     }
415
416     function asXML()
417     {
418         if ($this->isEmpty()) {
419             $xml = $this->emptyTag();
420         } else {
421             $xml = $this->startTag();
422             // FIXME: The next two lines could be removed for efficiency
423             if (!$this->hasInlineContent())
424                 $xml .= "\n";
425             $xml .= XmlContent::asXML();
426             $xml .= "</$this->_tag>";
427         }
428         if (!$this->isInlineElement())
429             $xml .= "\n";
430         return $xml;
431     }
432
433     /**
434      * Can this element have inline content?
435      *
436      * This is a hack, but is probably the best one can do without
437      * knowledge of the DTD...
438      */
439     function hasInlineContent()
440     {
441         // This is a hack.
442         if (empty($this->_content))
443             return true;
444         if (is_object($this->_content[0]))
445             return false;
446         return true;
447     }
448
449     /**
450      * Is this element part of inline content?
451      *
452      * This is a hack, but is probably the best one can do without
453      * knowledge of the DTD...
454      */
455     function isInlineElement()
456     {
457         return false;
458     }
459
460 }
461
462 ;
463
464 class RawXml
465 {
466     function RawXml($xml_text)
467     {
468         $this->_xml = $xml_text;
469     }
470
471     function printXML()
472     {
473         echo $this->_xml;
474     }
475
476     /* php-5.2 magic */
477     function __toString()
478     {
479         return $this->_xml;
480     }
481
482     function asXML()
483     {
484         return $this->_xml;
485     }
486
487     function asString()
488     {
489         return $this->_xml;
490     }
491
492     function isEmpty()
493     {
494         return empty($this->_xml);
495     }
496 }
497
498 class FormattedText
499 {
500     function FormattedText($fs /* , ... */)
501     {
502         if ($fs !== false) {
503             $this->_init(func_get_args());
504         }
505     }
506
507     function _init($args)
508     {
509         $this->_fs = array_shift($args);
510
511         // PHP's sprintf doesn't support variable width specifiers,
512         // like sprintf("%*s", 10, "x"); --- so we won't either.
513         $m = array();
514         if (!preg_match_all('/(?<!%)%(\d+)\$/x', $this->_fs, $m)) {
515             $this->_args = $args;
516         } else {
517             // Format string has '%2$s' style argument reordering.
518             // PHP doesn't support this.
519             if (preg_match('/(?<!%)%[- ]?\d*[^- \d$]/x', $this->_fs)) // $fmt
520                 // literal variable name substitution only to keep locale
521                 // strings uncluttered
522                 trigger_error(sprintf(_("Can't mix '%s' with '%s' type format strings"),
523                     '%1\$s', '%s'), E_USER_WARNING);
524
525             $this->_fs = preg_replace('/(?<!%)%\d+\$/x', '%', $this->_fs);
526
527             $this->_args = array();
528             foreach ($m[1] as $argnum) {
529                 if ($argnum < 1 || $argnum > count($args))
530                     trigger_error(sprintf("%s: argument index out of range",
531                         $argnum), E_USER_WARNING);
532                 $this->_args[] = $args[$argnum - 1];
533             }
534         }
535     }
536
537     function asXML()
538     {
539         // Not all PHP's have vsprintf, so...
540         $args[] = XmlElement::_quote((string)$this->_fs);
541         foreach ($this->_args as $arg)
542             $args[] = AsXML($arg);
543         return call_user_func_array('sprintf', $args);
544     }
545
546     function printXML()
547     {
548         // Not all PHP's have vsprintf, so...
549         $args[] = XmlElement::_quote((string)$this->_fs);
550         foreach ($this->_args as $arg)
551             $args[] = AsXML($arg);
552         call_user_func_array('printf', $args);
553     }
554
555     function asString()
556     {
557         $args[] = $this->_fs;
558         foreach ($this->_args as $arg)
559             $args[] = AsString($arg);
560         return call_user_func_array('sprintf', $args);
561     }
562
563     /* php-5.2 magic */
564     function __toString()
565     {
566         return $this->asString();
567     }
568 }
569
570 /**
571  * PHP5 compatibility
572  * Error[2048]: Non-static method XmlContent::_quote() should not be called statically
573  * Note: There's lot of room for performance increase if the right charset variant can
574  * be created on load-time.
575  */
576 function XmlContent_quote($string)
577 {
578     if (!$string) return $string;
579     if (isset($GLOBALS['charset'])
580         and (!defined('IGNORE_CHARSET_NOT_SUPPORTED_WARNING') or !IGNORE_CHARSET_NOT_SUPPORTED_WARNING)
581     ) {
582         return htmlspecialchars($string, ENT_COMPAT, $GLOBALS['charset']);
583     } else {
584         return htmlspecialchars($string);
585     }
586 }
587
588 function PrintXML($val /* , ... */)
589 {
590     if (func_num_args() > 1) {
591         foreach (func_get_args() as $arg)
592             PrintXML($arg);
593     } elseif (is_object($val)) {
594         if (method_exists($val, 'printXML'))
595             $val->printXML();
596         elseif (method_exists($val, 'asXML')) {
597             echo $val->asXML();
598         } elseif (method_exists($val, 'asString'))
599             echo XmlContent_quote($val->asString()); else
600             printf("==Object(%s)==", get_class($val));
601     } elseif (is_array($val)) {
602         // DEPRECATED:
603         // Use XmlContent objects instead of arrays for collections of XmlElements.
604         trigger_error("Passing arrays to PrintXML() is deprecated: (" . AsXML($val, true) . ")",
605             E_USER_NOTICE);
606         foreach ($val as $x)
607             PrintXML($x);
608     } else
609         echo (string)XmlContent_quote((string)$val);
610 }
611
612 function AsXML($val /* , ... */)
613 {
614     static $nowarn;
615
616     if (func_num_args() > 1) {
617         $xml = '';
618         foreach (func_get_args() as $arg)
619             $xml .= AsXML($arg);
620         return $xml;
621     } elseif (is_object($val)) {
622         if (method_exists($val, 'asXML'))
623             return $val->asXML();
624         elseif (method_exists($val, 'asString'))
625             return XmlContent_quote($val->asString()); else
626             return sprintf("==Object(%s)==", get_class($val));
627     } elseif (is_array($val)) {
628         // DEPRECATED:
629         // Use XmlContent objects instead of arrays for collections of XmlElements.
630         if (empty($nowarn)) {
631             $nowarn = true;
632             trigger_error("Passing arrays to AsXML() is deprecated: (" . AsXML($val) . ")",
633                 E_USER_NOTICE);
634             unset($nowarn);
635         }
636         $xml = '';
637         foreach ($val as $x)
638             $xml .= AsXML($x);
639         return $xml;
640     } else
641         return XmlContent_quote((string)$val);
642 }
643
644 function AsString($val)
645 {
646     if (func_num_args() > 1) {
647         $str = '';
648         foreach (func_get_args() as $arg)
649             $str .= AsString($arg);
650         return $str;
651     } elseif (is_object($val)) {
652         if (method_exists($val, 'asString'))
653             return $val->asString();
654         else
655             return sprintf("==Object(%s)==", get_class($val));
656     } elseif (is_array($val)) {
657         // DEPRECATED:
658         // Use XmlContent objects instead of arrays for collections of XmlElements.
659         trigger_error("Passing arrays to AsString() is deprecated", E_USER_NOTICE);
660         $str = '';
661         foreach ($val as $x)
662             $str .= AsString($x);
663         return $str;
664     }
665
666     return (string)$val;
667 }
668
669 function fmt($fs /* , ... */)
670 {
671     $s = new FormattedText(false);
672
673     $args = func_get_args();
674     $args[0] = _($args[0]);
675     $s->_init($args);
676     return $s;
677 }
678
679 // Local Variables:
680 // mode: php
681 // tab-width: 8
682 // c-basic-offset: 4
683 // c-hanging-comment-ender-p: nil
684 // indent-tabs-mode: nil
685 // End: