3 * Code for writing XML.
5 * @author: Jeff Dairiki,
6 * Reini Urban (php5 tricks)
8 * WARNING: This module is very php5 sensitive.
9 * Fixed for 1.3.9, 1.3.11 and 1.3.13 (php-5.2).
10 * With allow_call_time_pass_reference clean fixes.
14 * A sequence of (zero or more) XmlElements (possibly interspersed with
15 * plain strings (CDATA).
19 function XmlContent( /* ... */)
21 $this->_content = array();
22 $this->_pushContent_array(func_get_args());
25 function pushContent($arg /*, ...*/)
27 if (func_num_args() > 1)
28 $this->_pushContent_array(func_get_args());
29 elseif (is_array($arg))
30 $this->_pushContent_array($arg); else
31 $this->_pushContent($arg);
34 function _pushContent_array($array)
36 foreach ($array as $item) {
38 $this->_pushContent_array($item);
40 $this->_pushContent($item);
44 function _pushContent($item)
46 if (is_object($item) && strtolower(get_class($item)) == 'xmlcontent')
47 array_splice($this->_content, count($this->_content), 0,
50 $this->_content[] = $item;
53 function unshiftContent($arg /*, ...*/)
55 if (func_num_args() > 1)
56 $this->_unshiftContent_array(func_get_args());
57 elseif (is_array($arg))
58 $this->_unshiftContent_array($arg); else
59 $this->_unshiftContent($arg);
62 function _unshiftContent_array($array)
64 foreach (array_reverse($array) as $item) {
66 $this->_unshiftContent_array($item);
68 $this->_unshiftContent($item);
72 function _unshiftContent($item)
74 if (strtolower(get_class($item)) == 'xmlcontent')
75 array_splice($this->_content, 0, 0, $item->_content);
77 array_unshift($this->_content, $item);
82 return $this->_content;
85 function setContent($arg /* , ... */)
87 $this->_content = array();
88 $this->_pushContent_array(func_get_args());
93 foreach ($this->_content as $item) {
94 if (is_object($item)) {
95 if (method_exists($item, 'printXML'))
97 elseif (method_exists($item, 'asXML'))
98 echo $item->asXML(); elseif (method_exists($item, 'asString'))
99 echo $this->_quote($item->asString()); else
100 printf("==Object(%s)==", get_class($item));
101 } elseif (is_array($item)) {
103 // Use XmlContent objects instead of arrays for collections of XmlElements.
104 trigger_error("Passing arrays to printXML() is deprecated: (" . AsXML($item, true) . ")",
106 foreach ($item as $x)
109 echo $this->_quote((string)$item);
117 foreach ($this->_content as $item) {
118 if (is_object($item)) {
119 if (method_exists($item, 'asXML'))
120 $xml .= $item->asXML();
121 elseif (method_exists($item, 'asString'))
122 $xml .= $this->_quote($item->asString()); else
123 $xml .= sprintf("==Object(%s)==", get_class($item));
124 } elseif (is_array($item)) {
125 trigger_error("Passing arrays to ->asXML() is deprecated: (" . AsXML($item, true) . ")",
127 foreach ($item as $x)
128 $xml .= $this->asXML($x);
130 $xml .= $this->_quote((string)$item);
138 foreach ($this->_content as $item) {
139 if (is_object($item)) {
140 if (method_exists($item, 'asPDF'))
141 $pdf .= $item->asPDF();
142 elseif (method_exists($item, 'asString'))
143 $pdf .= $this->_quote($item->asString()); else
144 $pdf .= sprintf("==Object(%s)==", get_class($item));
146 $pdf .= $this->_quote((string)$item);
152 function __toString()
154 return $this->asString();
160 foreach ($this->_content as $item) {
161 if (is_object($item)) {
162 if (method_exists($item, 'asString')) {
163 $string = $item->asString();
164 if (is_object($string)) {
165 ; // ignore error so far: ImageLink labels
167 $val .= $this->_quote($item->asString());
170 $val .= sprintf("==Object(%s)==", get_class($item));
173 $val .= (string)$item;
179 * See if element is empty.
181 * Empty means it has no content.
182 * @return bool True if empty.
186 if (empty($this->_content))
188 foreach ($this->_content as $x) {
189 if (is_string($x) ? strlen($x) : !empty($x))
195 function _quote($string)
197 if (!$string) return $string;
198 return htmlspecialchars($string, ENT_COMPAT, $GLOBALS['charset']);
207 * @param $tagname string Tag of html element.
209 class XmlElement extends XmlContent
211 function XmlElement($tagname /* , $attr_or_content , ...*/)
213 //FIXME: php5 incompatible
215 $this->_init(func_get_args());
218 function _init($args)
220 if (!is_array($args))
221 $args = func_get_args();
223 assert(count($args) >= 1);
224 //assert(is_string($args[0]));
225 $this->_tag = array_shift($args);
227 if ($args && is_array($args[0]))
228 $this->_attr = array_shift($args);
230 $this->_attr = array();
231 if ($args && $args[0] === false)
235 $this->setContent($args);
238 /** Methods only needed for XmlParser,
239 * to be fully compatible to perl Html::Element
241 // doesn't yet work with php5 as __destruct()
244 if ($this->hasChildren()) {
245 foreach ($this->getChildren() as $node) {
251 unset($this->_content);
254 function getChildren()
256 return $this->_children;
259 function hasChildren()
261 return !empty($this->_children);
264 /* End XmlParser Methods
272 function setAttr($attr, $value = false)
274 if (is_array($attr)) {
275 assert($value === false);
276 foreach ($attr as $a => $v) {
277 $this->_attr[strtolower($a)] = $v;
278 //$this->set($a, $v);
283 assert(is_string($attr));
285 if ($value === false) {
286 unset($this->_attr[$attr]);
290 $this->_attr[$attr] = (string)$value;
293 if ($attr == 'class')
294 unset($this->_classes);
297 function getAttr($attr)
299 if ($attr == 'class')
300 $this->_setClasses();
302 if (isset($this->_attr[strtolower($attr)]))
303 return $this->_attr[strtolower($attr)];
308 function _getClasses()
310 if (!isset($this->_classes)) {
311 $this->_classes = array();
312 if (isset($this->_attr['class'])) {
313 $classes = explode(' ', (string)$this->_attr['class']);
314 foreach ($classes as $class) {
315 $class = trim($class);
317 $this->_classes[$class] = $class;
321 return $this->_classes;
324 function _setClasses()
326 if (isset($this->_classes)) {
328 $this->_attr['class'] = join(' ', $this->_classes);
330 unset($this->_attr['class']);
335 * Manipulate the elements CSS class membership.
337 * This adds or remove an elements membership
338 * in a give CSS class.
340 * @param $class string
342 * @param $in_class bool
343 * If true (the default) the element is added to class $class.
344 * If false, the element is removed from the class.
346 function setInClass($class, $in_class = true)
348 $this->_getClasses();
349 $class = trim($class);
351 $this->_classes[$class] = $class;
353 unset($this->_classes[$class]);
357 * Is element in a given (CSS) class?
359 * This checks for the presence of a particular class in the
360 * elements 'class' attribute.
362 * @param $class string The class to check for.
363 * @return bool True if the element is a member of $class.
365 function inClass($class)
367 $this->_parseClasses();
368 return isset($this->_classes[trim($class)]);
373 $start = "<" . $this->_tag;
374 $this->_setClasses();
375 foreach ($this->_attr as $attr => $val) {
381 $qval = str_replace("\"", '"', $this->_quote((string)$val));
382 $start .= " $attr=\"$qval\"";
390 return substr($this->startTag(), 0, -1) . "/>";
396 return "</$this->_tag>";
402 if ($this->isEmpty())
403 echo $this->emptyTag();
405 echo $this->startTag();
406 // FIXME: The next two lines could be removed for efficiency
407 if (!$this->hasInlineContent())
409 XmlContent::printXML();
410 echo "</$this->_tag>";
412 if (!$this->isInlineElement())
418 if ($this->isEmpty()) {
419 $xml = $this->emptyTag();
421 $xml = $this->startTag();
422 // FIXME: The next two lines could be removed for efficiency
423 if (!$this->hasInlineContent())
425 $xml .= XmlContent::asXML();
426 $xml .= "</$this->_tag>";
428 if (!$this->isInlineElement())
434 * Can this element have inline content?
436 * This is a hack, but is probably the best one can do without
437 * knowledge of the DTD...
439 function hasInlineContent()
442 if (empty($this->_content))
444 if (is_object($this->_content[0]))
450 * Is this element part of inline content?
452 * This is a hack, but is probably the best one can do without
453 * knowledge of the DTD...
455 function isInlineElement()
466 function RawXml($xml_text)
468 $this->_xml = $xml_text;
477 function __toString()
494 return empty($this->_xml);
500 function FormattedText($fs /* , ... */)
503 $this->_init(func_get_args());
507 function _init($args)
509 $this->_fs = array_shift($args);
511 // PHP's sprintf doesn't support variable width specifiers,
512 // like sprintf("%*s", 10, "x"); --- so we won't either.
514 if (!preg_match_all('/(?<!%)%(\d+)\$/x', $this->_fs, $m)) {
515 $this->_args = $args;
517 // Format string has '%2$s' style argument reordering.
518 // PHP doesn't support this.
519 if (preg_match('/(?<!%)%[- ]?\d*[^- \d$]/x', $this->_fs)) // $fmt
520 // literal variable name substitution only to keep locale
521 // strings uncluttered
522 trigger_error(sprintf(_("Can't mix '%s' with '%s' type format strings"),
523 '%1\$s', '%s'), E_USER_WARNING);
525 $this->_fs = preg_replace('/(?<!%)%\d+\$/x', '%', $this->_fs);
527 $this->_args = array();
528 foreach ($m[1] as $argnum) {
529 if ($argnum < 1 || $argnum > count($args))
530 trigger_error(sprintf("%s: argument index out of range",
531 $argnum), E_USER_WARNING);
532 $this->_args[] = $args[$argnum - 1];
539 // Not all PHP's have vsprintf, so...
540 $args[] = XmlElement::_quote((string)$this->_fs);
541 foreach ($this->_args as $arg)
542 $args[] = AsXML($arg);
543 return call_user_func_array('sprintf', $args);
548 // Not all PHP's have vsprintf, so...
549 $args[] = XmlElement::_quote((string)$this->_fs);
550 foreach ($this->_args as $arg)
551 $args[] = AsXML($arg);
552 call_user_func_array('printf', $args);
557 $args[] = $this->_fs;
558 foreach ($this->_args as $arg)
559 $args[] = AsString($arg);
560 return call_user_func_array('sprintf', $args);
564 function __toString()
566 return $this->asString();
572 * Error[2048]: Non-static method XmlContent::_quote() should not be called statically
573 * Note: There's lot of room for performance increase if the right charset variant can
574 * be created on load-time.
576 function XmlContent_quote($string)
578 if (!$string) return $string;
579 if (isset($GLOBALS['charset'])
580 and (!defined('IGNORE_CHARSET_NOT_SUPPORTED_WARNING') or !IGNORE_CHARSET_NOT_SUPPORTED_WARNING)
582 return htmlspecialchars($string, ENT_COMPAT, $GLOBALS['charset']);
584 return htmlspecialchars($string);
588 function PrintXML($val /* , ... */)
590 if (func_num_args() > 1) {
591 foreach (func_get_args() as $arg)
593 } elseif (is_object($val)) {
594 if (method_exists($val, 'printXML'))
596 elseif (method_exists($val, 'asXML')) {
598 } elseif (method_exists($val, 'asString'))
599 echo XmlContent_quote($val->asString()); else
600 printf("==Object(%s)==", get_class($val));
601 } elseif (is_array($val)) {
603 // Use XmlContent objects instead of arrays for collections of XmlElements.
604 trigger_error("Passing arrays to PrintXML() is deprecated: (" . AsXML($val, true) . ")",
609 echo (string)XmlContent_quote((string)$val);
612 function AsXML($val /* , ... */)
616 if (func_num_args() > 1) {
618 foreach (func_get_args() as $arg)
621 } elseif (is_object($val)) {
622 if (method_exists($val, 'asXML'))
623 return $val->asXML();
624 elseif (method_exists($val, 'asString'))
625 return XmlContent_quote($val->asString()); else
626 return sprintf("==Object(%s)==", get_class($val));
627 } elseif (is_array($val)) {
629 // Use XmlContent objects instead of arrays for collections of XmlElements.
630 if (empty($nowarn)) {
632 trigger_error("Passing arrays to AsXML() is deprecated: (" . AsXML($val) . ")",
641 return XmlContent_quote((string)$val);
644 function AsString($val)
646 if (func_num_args() > 1) {
648 foreach (func_get_args() as $arg)
649 $str .= AsString($arg);
651 } elseif (is_object($val)) {
652 if (method_exists($val, 'asString'))
653 return $val->asString();
655 return sprintf("==Object(%s)==", get_class($val));
656 } elseif (is_array($val)) {
658 // Use XmlContent objects instead of arrays for collections of XmlElements.
659 trigger_error("Passing arrays to AsString() is deprecated", E_USER_NOTICE);
662 $str .= AsString($x);
669 function fmt($fs /* , ... */)
671 $s = new FormattedText(false);
673 $args = func_get_args();
674 $args[0] = _($args[0]);
683 // c-hanging-comment-ender-p: nil
684 // indent-tabs-mode: nil