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