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 (/* ... */) {
20 $this->_content = array();
21 $this->_pushContent_array(func_get_args());
24 function pushContent ($arg /*, ...*/) {
25 if (func_num_args() > 1)
26 $this->_pushContent_array(func_get_args());
27 elseif (is_array($arg))
28 $this->_pushContent_array($arg);
30 $this->_pushContent($arg);
33 function _pushContent_array ($array) {
34 foreach ($array as $item) {
36 $this->_pushContent_array($item);
38 $this->_pushContent($item);
42 function _pushContent ($item) {
43 if (is_object($item) && strtolower(get_class($item)) == 'xmlcontent')
44 array_splice($this->_content, count($this->_content), 0,
47 $this->_content[] = $item;
50 function unshiftContent ($arg /*, ...*/) {
51 if (func_num_args() > 1)
52 $this->_unshiftContent_array(func_get_args());
53 elseif (is_array($arg))
54 $this->_unshiftContent_array($arg);
56 $this->_unshiftContent($arg);
59 function _unshiftContent_array ($array) {
60 foreach (array_reverse($array) as $item) {
62 $this->_unshiftContent_array($item);
64 $this->_unshiftContent($item);
68 function _unshiftContent ($item) {
69 if (strtolower(get_class($item)) == 'xmlcontent')
70 array_splice($this->_content, 0, 0, $item->_content);
72 array_unshift($this->_content, $item);
75 function getContent () {
76 return $this->_content;
79 function setContent ($arg /* , ... */) {
80 $this->_content = array();
81 $this->_pushContent_array(func_get_args());
84 function printXML () {
85 foreach ($this->_content as $item) {
86 if (is_object($item)) {
87 if (method_exists($item, 'printXML'))
89 elseif (method_exists($item, 'asXML'))
91 elseif (method_exists($item, 'asString'))
92 echo $this->_quote($item->asString());
94 printf("==Object(%s)==", get_class($item));
96 elseif (is_array($item)) {
98 // Use XmlContent objects instead of arrays for collections of XmlElements.
99 trigger_error("Passing arrays to printXML() is deprecated: (" . AsXML($item, true) . ")",
101 foreach ($item as $x)
104 echo $this->_quote((string) $item);
111 foreach ($this->_content as $item) {
112 if (is_object($item)) {
113 if (method_exists($item, 'asXML'))
114 $xml .= $item->asXML();
115 elseif (method_exists($item, 'asString'))
116 $xml .= $this->_quote($item->asString());
118 $xml .= sprintf("==Object(%s)==", get_class($item));
120 elseif (is_array($item)) {
121 trigger_error("Passing arrays to ->asXML() is deprecated: (" . AsXML($item, true) . ")",
123 foreach ($item as $x)
124 $xml .= $this->asXML($x);
127 $xml .= $this->_quote((string) $item);
134 foreach ($this->_content as $item) {
135 if (is_object($item)) {
136 if (method_exists($item, 'asPDF'))
137 $pdf .= $item->asPDF();
138 elseif (method_exists($item, 'asString'))
139 $pdf .= $this->_quote($item->asString());
141 $pdf .= sprintf("==Object(%s)==", get_class($item));
144 $pdf .= $this->_quote((string) $item);
150 function __toString () {
151 return $this->asString();
154 function asString () {
156 foreach ($this->_content as $item) {
157 if (is_object($item)) {
158 if (method_exists($item, 'asString')) {
159 $string = $item->asString();
160 if (is_object($string)) {
161 ; // ignore error so far: ImageLink labels
163 $val .= $this->_quote($item->asString());
166 $val .= sprintf("==Object(%s)==", get_class($item));
170 $val .= (string) $item;
176 * See if element is empty.
178 * Empty means it has no content.
179 * @return bool True if empty.
181 function isEmpty () {
182 if (empty($this->_content))
184 foreach ($this->_content as $x) {
185 if (is_string($x) ? strlen($x) : !empty($x))
191 function _quote ($string) {
192 if (!$string) return $string;
193 return htmlspecialchars($string, ENT_COMPAT, $GLOBALS['charset']);
200 * @param $tagname string Tag of html element.
202 class XmlElement extends XmlContent
204 function XmlElement ($tagname /* , $attr_or_content , ...*/) {
205 //FIXME: php5 incompatible
207 $this->_init(func_get_args());
210 function _init ($args) {
211 if (!is_array($args))
212 $args = func_get_args();
214 assert(count($args) >= 1);
215 //assert(is_string($args[0]));
216 $this->_tag = array_shift($args);
218 if ($args && is_array($args[0]))
219 $this->_attr = array_shift($args);
221 $this->_attr = array();
222 if ($args && $args[0] === false)
226 $this->setContent($args);
229 /** Methods only needed for XmlParser,
230 * to be fully compatible to perl Html::Element
232 // doesn't yet work with php5 as __destruct()
233 function _destruct () {
234 if ($this->hasChildren()) {
235 foreach ($this->getChildren() as $node) {
241 unset($this->_content);
244 function getChildren () {
245 return $this->_children;
248 function hasChildren () {
249 return !empty($this->_children);
251 /* End XmlParser Methods
258 function setAttr ($attr, $value = false) {
259 if (is_array($attr)) {
260 assert($value === false);
261 foreach ($attr as $a => $v) {
262 $this->_attr[strtolower($a)] = $v;
263 //$this->set($a, $v);
268 assert(is_string($attr));
270 if ($value === false) {
271 unset($this->_attr[$attr]);
276 $this->_attr[$attr] = (string) $value;
279 if ($attr == 'class')
280 unset($this->_classes);
283 function getAttr ($attr) {
284 if ($attr == 'class')
285 $this->_setClasses();
287 if (isset($this->_attr[strtolower($attr)]))
288 return $this->_attr[strtolower($attr)];
293 function _getClasses() {
294 if (!isset($this->_classes)) {
295 $this->_classes = array();
296 if (isset($this->_attr['class'])) {
297 $classes = explode(' ', (string) $this->_attr['class']);
298 foreach ($classes as $class) {
299 $class = trim($class);
301 $this->_classes[$class] = $class;
305 return $this->_classes;
308 function _setClasses() {
309 if (isset($this->_classes)) {
311 $this->_attr['class'] = join(' ', $this->_classes);
313 unset($this->_attr['class']);
318 * Manipulate the elements CSS class membership.
320 * This adds or remove an elements membership
321 * in a give CSS class.
323 * @param $class string
325 * @param $in_class bool
326 * If true (the default) the element is added to class $class.
327 * If false, the element is removed from the class.
329 function setInClass($class, $in_class=true) {
330 $this->_getClasses();
331 $class = trim($class);
333 $this->_classes[$class] = $class;
335 unset($this->_classes[$class]);
339 * Is element in a given (CSS) class?
341 * This checks for the presence of a particular class in the
342 * elements 'class' attribute.
344 * @param $class string The class to check for.
345 * @return bool True if the element is a member of $class.
347 function inClass($class) {
348 $this->_parseClasses();
349 return isset($this->_classes[trim($class)]);
352 function startTag() {
353 $start = "<" . $this->_tag;
354 $this->_setClasses();
355 foreach ($this->_attr as $attr => $val) {
361 $qval = str_replace("\"", '"', $this->_quote((string)$val));
362 $start .= " $attr=\"$qval\"";
368 function emptyTag() {
369 return substr($this->startTag(), 0, -1) . "/>";
374 return "</$this->_tag>";
378 function printXML () {
379 if ($this->isEmpty())
380 echo $this->emptyTag();
382 echo $this->startTag();
383 // FIXME: The next two lines could be removed for efficiency
384 if (!$this->hasInlineContent())
386 XmlContent::printXML();
387 echo "</$this->_tag>";
389 if (!$this->isInlineElement())
394 if ($this->isEmpty()) {
395 $xml = $this->emptyTag();
398 $xml = $this->startTag();
399 // FIXME: The next two lines could be removed for efficiency
400 if (!$this->hasInlineContent())
402 $xml .= XmlContent::asXML();
403 $xml .= "</$this->_tag>";
405 if (!$this->isInlineElement())
411 * Can this element have inline content?
413 * This is a hack, but is probably the best one can do without
414 * knowledge of the DTD...
416 function hasInlineContent () {
418 if (empty($this->_content))
420 if (is_object($this->_content[0]))
426 * Is this element part of inline content?
428 * This is a hack, but is probably the best one can do without
429 * knowledge of the DTD...
431 function isInlineElement () {
438 function RawXml ($xml_text) {
439 $this->_xml = $xml_text;
442 function printXML () {
447 function __toString () {
455 function asString () {
459 function isEmpty () {
460 return empty($this->_xml);
464 class FormattedText {
465 function FormattedText ($fs /* , ... */) {
467 $this->_init(func_get_args());
471 function _init ($args) {
472 $this->_fs = array_shift($args);
474 // PHP's sprintf doesn't support variable width specifiers,
475 // like sprintf("%*s", 10, "x"); --- so we won't either.
477 if (! preg_match_all('/(?<!%)%(\d+)\$/x', $this->_fs, $m)) {
478 $this->_args = $args;
481 // Format string has '%2$s' style argument reordering.
482 // PHP doesn't support this.
483 if (preg_match('/(?<!%)%[- ]?\d*[^- \d$]/x', $this->_fs)) // $fmt
484 // literal variable name substitution only to keep locale
485 // strings uncluttered
486 trigger_error(sprintf(_("Can't mix '%s' with '%s' type format strings"),
487 '%1\$s','%s'), E_USER_WARNING);
489 $this->_fs = preg_replace('/(?<!%)%\d+\$/x', '%', $this->_fs);
491 $this->_args = array();
492 foreach($m[1] as $argnum) {
493 if ($argnum < 1 || $argnum > count($args))
494 trigger_error(sprintf("%s: argument index out of range",
495 $argnum), E_USER_WARNING);
496 $this->_args[] = $args[$argnum - 1];
502 // Not all PHP's have vsprintf, so...
503 $args[] = XmlElement::_quote((string)$this->_fs);
504 foreach ($this->_args as $arg)
505 $args[] = AsXML($arg);
506 return call_user_func_array('sprintf', $args);
509 function printXML () {
510 // Not all PHP's have vsprintf, so...
511 $args[] = XmlElement::_quote((string)$this->_fs);
512 foreach ($this->_args as $arg)
513 $args[] = AsXML($arg);
514 call_user_func_array('printf', $args);
517 function asString() {
518 $args[] = $this->_fs;
519 foreach ($this->_args as $arg)
520 $args[] = AsString($arg);
521 return call_user_func_array('sprintf', $args);
525 function __toString () {
526 return $this->asString();
532 * Error[2048]: Non-static method XmlContent::_quote() should not be called statically
533 * Note: There's lot of room for performance increase if the right charset variant can
534 * be created on load-time.
536 function XmlContent_quote ($string) {
537 if (!$string) return $string;
538 if (isset($GLOBALS['charset'])
539 and (!defined('IGNORE_CHARSET_NOT_SUPPORTED_WARNING') or !IGNORE_CHARSET_NOT_SUPPORTED_WARNING))
541 return htmlspecialchars($string, ENT_COMPAT, $GLOBALS['charset']);
543 return htmlspecialchars($string);
547 function PrintXML ($val /* , ... */ ) {
548 if (func_num_args() > 1) {
549 foreach (func_get_args() as $arg)
552 elseif (is_object($val)) {
553 if (method_exists($val, 'printXML'))
555 elseif (method_exists($val, 'asXML')) {
558 elseif (method_exists($val, 'asString'))
559 echo XmlContent_quote($val->asString());
561 printf("==Object(%s)==", get_class($val));
563 elseif (is_array($val)) {
565 // Use XmlContent objects instead of arrays for collections of XmlElements.
566 trigger_error("Passing arrays to PrintXML() is deprecated: (" . AsXML($val, true) . ")",
572 echo (string)XmlContent_quote((string)$val);
575 function AsXML ($val /* , ... */) {
578 if (func_num_args() > 1) {
580 foreach (func_get_args() as $arg)
584 elseif (is_object($val)) {
585 if (method_exists($val, 'asXML'))
586 return $val->asXML();
587 elseif (method_exists($val, 'asString'))
588 return XmlContent_quote($val->asString());
590 return sprintf("==Object(%s)==", get_class($val));
592 elseif (is_array($val)) {
594 // Use XmlContent objects instead of arrays for collections of XmlElements.
595 if (empty($nowarn)) {
597 trigger_error("Passing arrays to AsXML() is deprecated: (" . AsXML($val) . ")",
607 return XmlContent_quote((string)$val);
610 function AsString ($val) {
611 if (func_num_args() > 1) {
613 foreach (func_get_args() as $arg)
614 $str .= AsString($arg);
617 elseif (is_object($val)) {
618 if (method_exists($val, 'asString'))
619 return $val->asString();
621 return sprintf("==Object(%s)==", get_class($val));
623 elseif (is_array($val)) {
625 // Use XmlContent objects instead of arrays for collections of XmlElements.
626 trigger_error("Passing arrays to AsString() is deprecated", E_USER_NOTICE);
629 $str .= AsString($x);
633 return (string) $val;
636 function fmt ($fs /* , ... */) {
637 $s = new FormattedText(false);
639 $args = func_get_args();
640 $args[0] = _($args[0]);
649 // c-hanging-comment-ender-p: nil
650 // indent-tabs-mode: nil