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