1 <?php rcs_id('$Id: XmlElement.php,v 1.44 2008-03-17 19:40:18 rurban Exp $');
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 (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);
126 elseif (is_array($item)) {
127 trigger_error("Passing arrays to ->asXML() is deprecated: (" . AsXML($item, true) . ")",
129 foreach ($item as $x)
130 $xml .= $this->asXML($x);
133 $xml .= $this->_quote((string) $item);
140 foreach ($this->_content as $item) {
141 if (is_object($item)) {
142 if (method_exists($item, 'asPDF'))
143 $pdf .= $item->asPDF();
144 elseif (method_exists($item, 'asString'))
145 $pdf .= $this->_quote($item->asString());
147 $pdf .= sprintf("==Object(%s)==", get_class($item));
150 $pdf .= $this->_quote((string) $item);
156 function __toString () {
157 return $this->asString();
160 function asString () {
162 foreach ($this->_content as $item) {
163 if (is_object($item)) {
164 if (method_exists($item, 'asString')) {
165 $val .= (string) $item->asString();
167 $val .= sprintf("==Object(%s)==", get_class($item));
171 $val .= (string) $item;
178 * See if element is empty.
180 * Empty means it has no content.
181 * @return bool True if empty.
183 function isEmpty () {
184 if (empty($this->_content))
186 foreach ($this->_content as $x) {
187 if (is_string($x) ? strlen($x) : !empty($x))
193 function _quote ($string) {
194 if (!$string) return $string;
195 if (check_php_version(4,1) and isset($GLOBALS['charset']))
196 return htmlspecialchars($string, ENT_COMPAT, $GLOBALS['charset']);
198 return htmlspecialchars($string);
205 * @param $tagname string Tag of html element.
207 class XmlElement extends XmlContent
209 function XmlElement ($tagname /* , $attr_or_content , ...*/) {
210 //FIXME: php5 incompatible
212 $this->_init(func_get_args());
215 function _init ($args) {
216 if (!is_array($args))
217 $args = func_get_args();
219 assert(count($args) >= 1);
220 //assert(is_string($args[0]));
221 $this->_tag = array_shift($args);
223 if ($args && is_array($args[0]))
224 $this->_attr = array_shift($args);
226 $this->_attr = array();
227 if ($args && $args[0] === false)
231 $this->setContent($args);
234 /** Methods only needed for XmlParser,
235 * to be fully compatible to perl Html::Element
237 // doesn't yet work with php5 as __destruct()
238 function _destruct () {
239 if ($this->hasChildren()) {
240 foreach ($this->getChildren() as $node) {
246 unset($this->_content);
249 function getChildren () {
250 return $this->_children;
253 function hasChildren () {
254 return !empty($this->_children);
256 /* End XmlParser Methods
263 function setAttr ($attr, $value = false) {
264 if (is_array($attr)) {
265 assert($value === false);
266 foreach ($attr as $a => $v) {
267 $this->_attr[strtolower($a)] = $v;
268 //$this->set($a, $v);
273 assert(is_string($attr));
275 if ($value === false) {
276 unset($this->_attr[$attr]);
281 $this->_attr[$attr] = (string) $value;
284 if ($attr == 'class')
285 unset($this->_classes);
288 function getAttr ($attr) {
289 if ($attr == 'class')
290 $this->_setClasses();
292 if (isset($this->_attr[strtolower($attr)]))
293 return $this->_attr[strtolower($attr)];
298 function _getClasses() {
299 if (!isset($this->_classes)) {
300 $this->_classes = array();
301 if (isset($this->_attr['class'])) {
302 $classes = explode(' ', (string) $this->_attr['class']);
303 foreach ($classes as $class) {
304 $class = trim($class);
306 $this->_classes[$class] = $class;
310 return $this->_classes;
313 function _setClasses() {
314 if (isset($this->_classes)) {
316 $this->_attr['class'] = join(' ', $this->_classes);
318 unset($this->_attr['class']);
323 * Manipulate the elements CSS class membership.
325 * This adds or remove an elements membership
326 * in a give CSS class.
328 * @param $class string
330 * @param $in_class bool
331 * If true (the default) the element is added to class $class.
332 * If false, the element is removed from the class.
334 function setInClass($class, $in_class=true) {
335 $this->_getClasses();
336 $class = trim($class);
338 $this->_classes[$class] = $class;
340 unset($this->_classes[$class]);
344 * Is element in a given (CSS) class?
346 * This checks for the presence of a particular class in the
347 * elements 'class' attribute.
349 * @param $class string The class to check for.
350 * @return bool True if the element is a member of $class.
352 function inClass($class) {
353 $this->_parseClasses();
354 return isset($this->_classes[trim($class)]);
357 function startTag() {
358 $start = "<" . $this->_tag;
359 $this->_setClasses();
360 foreach ($this->_attr as $attr => $val) {
366 $qval = str_replace("\"", '"', $this->_quote((string)$val));
367 $start .= " $attr=\"$qval\"";
373 function emptyTag() {
374 return substr($this->startTag(), 0, -1) . "/>";
379 return "</$this->_tag>";
383 function printXML () {
384 if ($this->isEmpty())
385 echo $this->emptyTag();
387 echo $this->startTag();
388 // FIXME: The next two lines could be removed for efficiency
389 if (!$this->hasInlineContent())
391 XmlContent::printXML();
392 echo "</$this->_tag>";
394 if (!$this->isInlineElement())
399 if ($this->isEmpty()) {
400 $xml = $this->emptyTag();
403 $xml = $this->startTag();
404 // FIXME: The next two lines could be removed for efficiency
405 if (!$this->hasInlineContent())
407 $xml .= XmlContent::asXML();
408 $xml .= "</$this->_tag>";
410 if (!$this->isInlineElement())
416 * Can this element have inline content?
418 * This is a hack, but is probably the best one can do without
419 * knowledge of the DTD...
421 function hasInlineContent () {
423 if (empty($this->_content))
425 if (is_object($this->_content[0]))
431 * Is this element part of inline content?
433 * This is a hack, but is probably the best one can do without
434 * knowledge of the DTD...
436 function isInlineElement () {
443 function RawXml ($xml_text) {
444 $this->_xml = $xml_text;
447 function printXML () {
452 function __toString () {
460 function isEmpty () {
461 return empty($this->_xml);
465 class FormattedText {
466 function FormattedText ($fs /* , ... */) {
468 $this->_init(func_get_args());
472 function _init ($args) {
473 $this->_fs = array_shift($args);
475 // PHP's sprintf doesn't support variable width specifiers,
476 // like sprintf("%*s", 10, "x"); --- so we won't either.
478 if (! preg_match_all('/(?<!%)%(\d+)\$/x', $this->_fs, $m)) {
479 $this->_args = $args;
482 // Format string has '%2$s' style argument reordering.
483 // PHP doesn't support this.
484 if (preg_match('/(?<!%)%[- ]?\d*[^- \d$]/x', $this->_fs)) // $fmt
485 // literal variable name substitution only to keep locale
486 // strings uncluttered
487 trigger_error(sprintf(_("Can't mix '%s' with '%s' type format strings"),
488 '%1\$s','%s'), E_USER_WARNING);
490 $this->_fs = preg_replace('/(?<!%)%\d+\$/x', '%', $this->_fs);
492 $this->_args = array();
493 foreach($m[1] as $argnum) {
494 if ($argnum < 1 || $argnum > count($args))
495 trigger_error(sprintf("%s: argument index out of range",
496 $argnum), E_USER_WARNING);
497 $this->_args[] = $args[$argnum - 1];
503 // Not all PHP's have vsprintf, so...
504 $args[] = XmlElement::_quote((string)$this->_fs);
505 foreach ($this->_args as $arg)
506 $args[] = AsXML($arg);
507 return call_user_func_array('sprintf', $args);
510 function printXML () {
511 // Not all PHP's have vsprintf, so...
512 $args[] = XmlElement::_quote((string)$this->_fs);
513 foreach ($this->_args as $arg)
514 $args[] = AsXML($arg);
515 call_user_func_array('printf', $args);
518 function asString() {
519 $args[] = $this->_fs;
520 foreach ($this->_args as $arg)
521 $args[] = AsString($arg);
522 return call_user_func_array('sprintf', $args);
526 function __toString () {
527 return $this->asString();
533 * Error[2048]: Non-static method XmlContent::_quote() should not be called statically
534 * Note: There's lot of room for performance increase if the right charset variant can
535 * be created on load-time.
537 function XmlContent_quote ($string) {
538 if (!$string) return $string;
539 if (check_php_version(4,1) and isset($GLOBALS['charset'])
540 and (!defined('IGNORE_CHARSET_NOT_SUPPORTED_WARNING') or !IGNORE_CHARSET_NOT_SUPPORTED_WARNING))
542 return htmlspecialchars($string, ENT_COMPAT, $GLOBALS['charset']);
544 return htmlspecialchars($string);
548 function PrintXML ($val /* , ... */ ) {
549 if (func_num_args() > 1) {
550 foreach (func_get_args() as $arg)
553 elseif (is_object($val)) {
554 if (method_exists($val, 'printXML'))
556 elseif (method_exists($val, 'asXML')) {
559 elseif (method_exists($val, 'asString'))
560 echo XmlContent_quote($val->asString());
562 printf("==Object(%s)==", get_class($val));
564 elseif (is_array($val)) {
566 // Use XmlContent objects instead of arrays for collections of XmlElements.
567 trigger_error("Passing arrays to PrintXML() is deprecated: (" . AsXML($val, true) . ")",
573 echo (string)XmlContent_quote((string)$val);
576 function AsXML ($val /* , ... */) {
579 if (func_num_args() > 1) {
581 foreach (func_get_args() as $arg)
585 elseif (is_object($val)) {
586 if (method_exists($val, 'asXML'))
587 return $val->asXML();
588 elseif (method_exists($val, 'asString'))
589 return XmlContent_quote($val->asString());
591 return sprintf("==Object(%s)==", get_class($val));
593 elseif (is_array($val)) {
595 // Use XmlContent objects instead of arrays for collections of XmlElements.
596 if (empty($nowarn)) {
598 trigger_error("Passing arrays to AsXML() is deprecated: (" . AsXML($val) . ")",
608 return XmlContent_quote((string)$val);
611 function AsString ($val) {
612 if (func_num_args() > 1) {
614 foreach (func_get_args() as $arg)
615 $str .= AsString($arg);
618 elseif (is_object($val)) {
619 if (method_exists($val, 'asString'))
620 return $val->asString();
622 return sprintf("==Object(%s)==", get_class($val));
624 elseif (is_array($val)) {
626 // Use XmlContent objects instead of arrays for collections of XmlElements.
627 trigger_error("Passing arrays to AsString() is deprecated", E_USER_NOTICE);
630 $str .= AsString($x);
634 return (string) $val;
638 function fmt ($fs /* , ... */) {
639 $s = new FormattedText(false);
641 $args = func_get_args();
642 $args[0] = _($args[0]);
647 // $Log: not supported by cvs2svn $
648 // Revision 1.43 2007/07/14 17:55:30 rurban
651 // Revision 1.42 2007/01/20 11:41:30 rurban
652 // Add php-5.2.0 note
654 // Revision 1.41 2007/01/13 23:34:49 rurban
655 // More php-5.2.0 fixes __toString
657 // Revision 1.40 2007/01/13 23:28:56 rurban
658 // php-5.2.0 fix for string fallback: Provide the magic method __toString
660 // Revision 1.39 2006/08/15 13:36:23 rurban
661 // support iso-8859-2
663 // Revision 1.38 2005/10/10 19:36:09 rurban
666 // Revision 1.37 2005/01/25 07:04:27 rurban
667 // case-sensitive for php5
669 // Revision 1.36 2004/12/06 19:49:56 rurban
670 // enable action=remove which is undoable and seeable in RecentChanges: ADODB ony for now.
671 // renamed delete_page to purge_page.
672 // enable action=edit&version=-1 to force creation of a new version.
673 // added BABYCART_PATH config
674 // fixed magiqc in adodb.inc.php
675 // and some more docs
677 // Revision 1.35 2004/11/21 11:59:18 rurban
678 // remove final \n to be ob_cache independent
680 // Revision 1.34 2004/10/12 13:13:19 rurban
681 // php5 compatibility (5.0.1 ok)
683 // Revision 1.33 2004/07/02 09:55:58 rurban
684 // more stability fixes: new DISABLE_GETIMAGESIZE if your php crashes when loading LinkIcons: failing getimagesize in old phps; blockparser stabilized
686 // Revision 1.32 2004/06/20 15:30:05 rurban
687 // get_class case-sensitivity issues
689 // Revision 1.31 2004/06/20 14:42:54 rurban
690 // various php5 fixes (still broken at blockparser)
693 // (c-file-style: "gnu")
698 // c-hanging-comment-ender-p: nil
699 // indent-tabs-mode: nil