]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/XmlElement.php
Output newlines between content-less non-inline elements.
[SourceForge/phpwiki.git] / lib / XmlElement.php
1 <?php rcs_id('$Id: XmlElement.php,v 1.18 2003-02-15 02:14:52 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 (!empty($x))
139                 return false;
140         }
141         return true;
142     }
143     
144     function _quote ($string) {
145         return str_replace('<', '&lt;',
146                            str_replace('>', '&gt;',
147                                        str_replace('&', '&amp;', $string)));
148     }
149 };
150
151 /**
152  * An XML element.
153  *
154  * @param $tagname string Tag of html element.
155  */
156 class XmlElement extends XmlContent
157 {
158     function XmlElement ($tagname /* , $attr_or_content , ...*/) {
159         $this->XmlContent();
160         $this->_init(func_get_args());
161     }
162
163     function _init ($args) {
164         if (!is_array($args))
165             $args = func_get_args();
166
167         assert(count($args) >= 1);
168         //assert(is_string($args[0]));
169         $this->_tag = array_shift($args);
170         
171         if ($args && is_array($args[0]))
172             $this->_attr = array_shift($args);
173         else {
174             $this->_attr = array();
175             if ($args && $args[0] === false)
176                 array_shift($args);
177         }
178
179         $this->setContent($args);
180     }
181
182     function getTag () {
183         return $this->_tag;
184     }
185     
186     function setAttr ($attr, $value = false) {
187         if (is_array($attr)) {
188             assert($value === false);
189             foreach ($attr as $a => $v)
190                 $this->set($a, $v);
191             return;
192         }
193
194         assert(is_string($attr));
195         if ($value === false) {
196             unset($this->_attr[$attr]);
197         }
198         if (is_bool($value))
199             $value = $attr;
200         $this->_attr[$attr] = (string) $value;
201     }
202
203     function getAttr ($attr) {
204         if (isset($this->_attr[$attr]))
205             return $this->_attr[$attr];
206         else
207             return false;
208     }
209     
210     function startTag() {
211         $start = "<" . $this->_tag;
212         foreach ($this->_attr as $attr => $val) {
213             if (is_bool($val)) {
214                 if (!$val)
215                     continue;
216                 $val = $attr;
217             }
218             $qval = str_replace("\"", '&quot;', $this->_quote($val));
219             $start .= " $attr=\"$qval\"";
220         }
221         $start .= ">";
222         return $start;
223     }
224
225     function emptyTag() {
226         return substr($this->startTag(), 0, -1) . "/>";
227     }
228
229     
230     function endTag() {
231         return "</$this->_tag>";
232     }
233     
234         
235     function printXML () {
236         if ($this->isEmpty())
237             echo $this->emptyTag();
238         else {
239             echo $this->startTag();
240             // FIXME: The next two lines could be removed for efficiency
241             if (!$this->hasInlineContent())
242                 echo "\n";
243             XmlContent::printXML();
244             echo "</$this->_tag>";
245         }
246         if (!$this->isInlineElement())
247             echo "\n";
248     }
249
250     function asXML () {
251         if ($this->isEmpty()) {
252             $xml = $this->emptyTag();
253         }
254         else {
255             $xml = $this->startTag();
256             // FIXME: The next two lines could be removed for efficiency
257             if (!$this->hasInlineContent())
258                 $xml .= "\n";
259             $xml .= XmlContent::asXML();
260             $xml .= "</$this->_tag>";
261         }
262         if (!$this->isInlineElement())
263             $xml .= "\n";
264         return $xml;
265     }
266
267     /**
268      * Can this element have inline content?
269      *
270      * This is a hack, but is probably the best one can do without
271      * knowledge of the DTD...
272      */
273     function hasInlineContent () {
274         // This is a hack.
275         if (empty($this->_content))
276             return true;
277         if (is_object($this->_content[0]))
278             return false;
279         return true;
280     }
281     
282     /**
283      * Is this element part of inline content?
284      *
285      * This is a hack, but is probably the best one can do without
286      * knowledge of the DTD...
287      */
288     function isInlineElement () {
289         return false;
290     }
291     
292 };
293
294 class RawXml {
295     function RawXml ($xml_text) {
296         $this->_xml = $xml_text;
297     }
298
299     function printXML () {
300         echo $this->_xml;
301     }
302
303     function asXML () {
304         return $this->_xml;
305     }
306
307     function isEmpty () {
308         return empty($this->_xml);
309     }
310 }
311
312 class FormattedText {
313     function FormattedText ($fs /* , ... */) {
314         if ($fs !== false) {
315             $this->_init(func_get_args());
316         }
317     }
318
319     function _init ($args) {
320         $this->_fs = array_shift($args);
321
322         // PHP's sprintf doesn't support variable width specifiers,
323         // like sprintf("%*s", 10, "x"); --- so we won't either.
324
325         if (! preg_match_all('/(?<!%)%(\d+)\$/x', $this->_fs, $m)) {
326             $this->_args  = $args;
327         }
328         else {
329             // Format string has '%2$s' style argument reordering.
330             // PHP doesn't support this.
331             if (preg_match('/(?<!%)%[- ]?\d*[^- \d$]/x', $this->_fs)) // $fmt
332                 // literal variable name substitution only to keep locale
333                 // strings uncluttered
334                 trigger_error(sprintf(_("Can't mix '%s' with '%s' type format strings"),
335                                       '%1\$s','%s'), E_USER_WARNING);
336         
337             $this->_fs = preg_replace('/(?<!%)%\d+\$/x', '%', $this->_fs);
338
339             $this->_args = array();
340             foreach($m[1] as $argnum) {
341                 if ($argnum < 1 || $argnum > count($args))
342                     trigger_error(sprintf("%s: argument index out of range", 
343                                           $argnum), E_USER_WARNING);
344                 $this->_args[] = $args[$argnum - 1];
345             }
346         }
347     }
348
349     function asXML () {
350         // Not all PHP's have vsprintf, so...
351         $args[] = XmlElement::_quote($this->_fs);
352         foreach ($this->_args as $arg)
353             $args[] = AsXML($arg);
354         return call_user_func_array('sprintf', $args);
355     }
356
357     function printXML () {
358         // Not all PHP's have vsprintf, so...
359         $args[] = XmlElement::_quote($this->_fs);
360         foreach ($this->_args as $arg)
361             $args[] = AsXML($arg);
362         call_user_func_array('printf', $args);
363     }
364
365     function asString() {
366         $args[] = $this->_fs;
367         foreach ($this->_args as $arg)
368             $args[] = AsString($arg);
369         return call_user_func_array('sprintf', $args);
370     }
371 }
372
373 function PrintXML ($val /* , ... */ ) {
374     if (func_num_args() > 1) {
375         foreach (func_get_args() as $arg)
376             PrintXML($arg);
377     }
378     elseif (is_object($val)) {
379         if (method_exists($val, 'printxml'))
380             $val->printXML();
381         elseif (method_exists($val, 'asxml')) {
382             echo $val->asXML();
383         }
384         elseif (method_exists($val, 'asstring'))
385             echo XmlContent::_quote($val->asString());
386         else
387             printf("==Object(%s)==", get_class($val));
388     }
389     elseif (is_array($val)) {
390         // DEPRECATED:
391         // Use XmlContent objects instead of arrays for collections of XmlElements.
392         trigger_error("Passing arrays to PrintXML() is deprecated: (" . AsXML($val, true) . ")",
393                       E_USER_NOTICE);
394         foreach ($val as $x)
395             PrintXML($x);
396     }
397     else
398         echo (string)XmlContent::_quote($val);
399 }
400
401 function AsXML ($val /* , ... */) {
402     static $nowarn;
403
404     if (func_num_args() > 1) {
405         $xml = '';
406         foreach (func_get_args() as $arg)
407             $xml .= AsXML($arg);
408         return $xml;
409     }
410     elseif (is_object($val)) {
411         if (method_exists($val, 'asxml'))
412             return $val->asXML();
413         elseif (method_exists($val, 'asstring'))
414             return XmlContent::_quote($val->asString());
415         else
416             return sprintf("==Object(%s)==", get_class($val));
417     }
418     elseif (is_array($val)) {
419         // DEPRECATED:
420         // Use XmlContent objects instead of arrays for collections of XmlElements.
421         if (empty($nowarn)) {
422             $nowarn = true;
423             trigger_error("Passing arrays to AsXML() is deprecated: (" . AsXML($val) . ")",
424                           E_USER_NOTICE);
425             unset($nowarn);
426         }
427         $xml = '';
428         foreach ($val as $x)
429             $xml .= AsXML($x);
430         return $xml;
431     }
432     else
433         return XmlContent::_quote((string)$val);
434 }
435
436 function AsString ($val) {
437     if (func_num_args() > 1) {
438         $str = '';
439         foreach (func_get_args() as $arg)
440             $str .= AsString($arg);
441         return $str;
442     }
443     elseif (is_object($val)) {
444         if (method_exists($val, 'asstring'))
445             return $val->asString();
446         else
447             return sprintf("==Object(%s)==", get_class($val));
448     }
449     elseif (is_array($val)) {
450         // DEPRECATED:
451         // Use XmlContent objects instead of arrays for collections of XmlElements.
452         trigger_error("Passing arrays to AsString() is deprecated", E_USER_NOTICE);
453         $str = '';
454         foreach ($val as $x)
455             $str .= AsString($x);
456         return $str;
457     }
458     
459     return (string) $val;
460 }
461
462
463 function fmt ($fs /* , ... */) {
464     $s = new FormattedText(false);
465
466     $args = func_get_args();
467     $args[0] = _($args[0]);
468     $s->_init($args);
469     return $s;
470 }
471     
472 // (c-file-style: "gnu")
473 // Local Variables:
474 // mode: php
475 // tab-width: 8
476 // c-basic-offset: 4
477 // c-hanging-comment-ender-p: nil
478 // indent-tabs-mode: nil
479 // End:   
480 ?>