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