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