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