1 <?php rcs_id('$Id: XmlElement.php,v 1.20 2003-02-18 19:00:47 dairiki Exp $');
3 * Code for writing XML.
7 * A sequence of (zero or more) XmlElements (possibly interspersed with
8 * plain strings (CDATA).
12 function XmlContent (/* ... */) {
13 $this->_content = array();
14 $this->_pushContent_array(func_get_args());
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);
23 $this->_pushContent($arg);
26 function _pushContent_array ($array) {
27 foreach ($array as $item) {
29 $this->_pushContent_array($item);
31 $this->_pushContent($item);
35 function _pushContent ($item) {
36 if (get_class($item) == 'xmlcontent')
37 array_splice($this->_content, count($this->_content), 0,
40 $this->_content[] = $item;
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);
49 $this->_unshiftContent($arg);
52 function _unshiftContent_array ($array) {
53 foreach (array_reverse($array) as $item) {
55 $this->_unshiftContent_array($item);
57 $this->_unshiftContent($item);
61 function _unshiftContent ($item) {
62 if (get_class($item) == 'xmlcontent')
63 array_splice($this->_content, 0, 0, $item->_content);
65 array_unshift($this->_content, $item);
68 function getContent () {
69 return $this->_content;
72 function setContent ($arg /* , ... */) {
73 $this->_content = array();
74 $this->_pushContent_array(func_get_args());
77 function printXML () {
78 foreach ($this->_content as $item) {
79 if (is_object($item)) {
80 if (method_exists($item, 'printxml'))
82 elseif (method_exists($item, 'asxml'))
84 elseif (method_exists($item, 'asstring'))
85 echo $this->_quote($item->asString());
87 printf("==Object(%s)==", get_class($item));
90 echo $this->_quote((string) $item);
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());
104 $xml .= sprintf("==Object(%s)==", get_class($item));
107 $xml .= $this->_quote((string) $item);
112 function asString () {
114 foreach ($this->_content as $item) {
115 if (is_object($item)) {
116 if (method_exists($item, 'asstring'))
117 $val .= $item->asString();
119 $val .= sprintf("==Object(%s)==", get_class($item));
122 $val .= (string) $item;
129 * See if element is empty.
131 * Empty means it has no content.
132 * @return bool True if empty.
134 function isEmpty () {
135 if (empty($this->_content))
137 foreach ($this->_content as $x) {
144 function _quote ($string) {
145 return str_replace('<', '<',
146 str_replace('>', '>',
147 str_replace('&', '&', $string)));
154 * @param $tagname string Tag of html element.
156 class XmlElement extends XmlContent
158 function XmlElement ($tagname /* , $attr_or_content , ...*/) {
160 $this->_init(func_get_args());
163 function _init ($args) {
164 if (!is_array($args))
165 $args = func_get_args();
167 assert(count($args) >= 1);
168 //assert(is_string($args[0]));
169 $this->_tag = array_shift($args);
171 if ($args && is_array($args[0]))
172 $this->_attr = array_shift($args);
174 $this->_attr = array();
175 if ($args && $args[0] === false)
179 $this->setContent($args);
186 function setAttr ($attr, $value = false) {
187 if (is_array($attr)) {
188 assert($value === false);
189 foreach ($attr as $a => $v)
194 assert(is_string($attr));
196 if ($value === false) {
197 unset($this->_attr[$attr]);
202 $this->_attr[$attr] = (string) $value;
205 if ($attr == 'class')
206 unset($this->_classes);
209 function getAttr ($attr) {
210 if ($attr == 'class')
211 $this->_setClasses();
213 if (isset($this->_attr[$attr]))
214 return $this->_attr[$attr];
219 function _getClasses() {
220 if (!isset($this->_classes)) {
221 $this->_classes = array();
222 if (isset($this->_attr['class'])) {
223 $classes = explode(' ', (string) $this->_attr['class']);
224 foreach ($classes as $class) {
225 $class = trim($class);
227 $this->_classes[$class] = $class;
231 return $this->_classes;
234 function _setClasses() {
235 if (isset($this->_classes)) {
237 $this->_attr['class'] = join(' ', $this->_classes);
239 unset($this->_attr['class']);
244 * Manipulate the elements CSS class membership.
246 * This adds or remove an elements membership
247 * in a give CSS class.
249 * @param $class string
251 * @param $in_class bool
252 * If true (the default) the element is added to class $class.
253 * If false, the element is removed from the class.
255 function setInClass($class, $in_class=true) {
256 $this->_getClasses();
257 $class = trim($class);
259 $this->_classes[$class] = $class;
261 unset($this->_classes[$class]);
265 * Is element in a given (CSS) class?
267 * This checks for the presence of a particular class in the
268 * elements 'class' attribute.
270 * @param $class string The class to check for.
271 * @return bool True if the element is a member of $class.
273 function inClass($class) {
274 $this->_parseClasses();
275 return isset($this->_classes[trim($class)]);
278 function startTag() {
279 $start = "<" . $this->_tag;
280 $this->_setClasses();
281 foreach ($this->_attr as $attr => $val) {
287 $qval = str_replace("\"", '"', $this->_quote($val));
288 $start .= " $attr=\"$qval\"";
294 function emptyTag() {
295 return substr($this->startTag(), 0, -1) . "/>";
300 return "</$this->_tag>";
304 function printXML () {
305 if ($this->isEmpty())
306 echo $this->emptyTag();
308 echo $this->startTag();
309 // FIXME: The next two lines could be removed for efficiency
310 if (!$this->hasInlineContent())
312 XmlContent::printXML();
313 echo "</$this->_tag>";
315 if (!$this->isInlineElement())
320 if ($this->isEmpty()) {
321 $xml = $this->emptyTag();
324 $xml = $this->startTag();
325 // FIXME: The next two lines could be removed for efficiency
326 if (!$this->hasInlineContent())
328 $xml .= XmlContent::asXML();
329 $xml .= "</$this->_tag>";
331 if (!$this->isInlineElement())
337 * Can this element have inline content?
339 * This is a hack, but is probably the best one can do without
340 * knowledge of the DTD...
342 function hasInlineContent () {
344 if (empty($this->_content))
346 if (is_object($this->_content[0]))
352 * Is this element part of inline content?
354 * This is a hack, but is probably the best one can do without
355 * knowledge of the DTD...
357 function isInlineElement () {
364 function RawXml ($xml_text) {
365 $this->_xml = $xml_text;
368 function printXML () {
376 function isEmpty () {
377 return empty($this->_xml);
381 class FormattedText {
382 function FormattedText ($fs /* , ... */) {
384 $this->_init(func_get_args());
388 function _init ($args) {
389 $this->_fs = array_shift($args);
391 // PHP's sprintf doesn't support variable width specifiers,
392 // like sprintf("%*s", 10, "x"); --- so we won't either.
394 if (! preg_match_all('/(?<!%)%(\d+)\$/x', $this->_fs, $m)) {
395 $this->_args = $args;
398 // Format string has '%2$s' style argument reordering.
399 // PHP doesn't support this.
400 if (preg_match('/(?<!%)%[- ]?\d*[^- \d$]/x', $this->_fs)) // $fmt
401 // literal variable name substitution only to keep locale
402 // strings uncluttered
403 trigger_error(sprintf(_("Can't mix '%s' with '%s' type format strings"),
404 '%1\$s','%s'), E_USER_WARNING);
406 $this->_fs = preg_replace('/(?<!%)%\d+\$/x', '%', $this->_fs);
408 $this->_args = array();
409 foreach($m[1] as $argnum) {
410 if ($argnum < 1 || $argnum > count($args))
411 trigger_error(sprintf("%s: argument index out of range",
412 $argnum), E_USER_WARNING);
413 $this->_args[] = $args[$argnum - 1];
419 // Not all PHP's have vsprintf, so...
420 $args[] = XmlElement::_quote($this->_fs);
421 foreach ($this->_args as $arg)
422 $args[] = AsXML($arg);
423 return call_user_func_array('sprintf', $args);
426 function printXML () {
427 // Not all PHP's have vsprintf, so...
428 $args[] = XmlElement::_quote($this->_fs);
429 foreach ($this->_args as $arg)
430 $args[] = AsXML($arg);
431 call_user_func_array('printf', $args);
434 function asString() {
435 $args[] = $this->_fs;
436 foreach ($this->_args as $arg)
437 $args[] = AsString($arg);
438 return call_user_func_array('sprintf', $args);
442 function PrintXML ($val /* , ... */ ) {
443 if (func_num_args() > 1) {
444 foreach (func_get_args() as $arg)
447 elseif (is_object($val)) {
448 if (method_exists($val, 'printxml'))
450 elseif (method_exists($val, 'asxml')) {
453 elseif (method_exists($val, 'asstring'))
454 echo XmlContent::_quote($val->asString());
456 printf("==Object(%s)==", get_class($val));
458 elseif (is_array($val)) {
460 // Use XmlContent objects instead of arrays for collections of XmlElements.
461 trigger_error("Passing arrays to PrintXML() is deprecated: (" . AsXML($val, true) . ")",
467 echo (string)XmlContent::_quote($val);
470 function AsXML ($val /* , ... */) {
473 if (func_num_args() > 1) {
475 foreach (func_get_args() as $arg)
479 elseif (is_object($val)) {
480 if (method_exists($val, 'asxml'))
481 return $val->asXML();
482 elseif (method_exists($val, 'asstring'))
483 return XmlContent::_quote($val->asString());
485 return sprintf("==Object(%s)==", get_class($val));
487 elseif (is_array($val)) {
489 // Use XmlContent objects instead of arrays for collections of XmlElements.
490 if (empty($nowarn)) {
492 trigger_error("Passing arrays to AsXML() is deprecated: (" . AsXML($val) . ")",
502 return XmlContent::_quote((string)$val);
505 function AsString ($val) {
506 if (func_num_args() > 1) {
508 foreach (func_get_args() as $arg)
509 $str .= AsString($arg);
512 elseif (is_object($val)) {
513 if (method_exists($val, 'asstring'))
514 return $val->asString();
516 return sprintf("==Object(%s)==", get_class($val));
518 elseif (is_array($val)) {
520 // Use XmlContent objects instead of arrays for collections of XmlElements.
521 trigger_error("Passing arrays to AsString() is deprecated", E_USER_NOTICE);
524 $str .= AsString($x);
528 return (string) $val;
532 function fmt ($fs /* , ... */) {
533 $s = new FormattedText(false);
535 $args = func_get_args();
536 $args[0] = _($args[0]);
541 // (c-file-style: "gnu")
546 // c-hanging-comment-ender-p: nil
547 // indent-tabs-mode: nil