1 <?php rcs_id('$Id: XmlElement.php,v 1.36 2004-12-06 19:49:56 rurban Exp $');
3 * Code for writing XML.
5 * @author: Jeff Dairiki,
6 * Reini Urban (php5 tricks)
8 * FIXME: This module is very php5 sensitive: It was fixed for 1.3.9,
9 * but is again broken with the 1.3.11
10 * 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));
97 echo $this->_quote((string) $item);
103 foreach ($this->_content as $item) {
104 if (is_object($item)) {
105 if (method_exists($item, 'asxml'))
106 $xml .= $item->asXML();
107 elseif (method_exists($item, 'asstring'))
108 $xml .= $this->_quote($item->asString());
110 $xml .= sprintf("==Object(%s)==", get_class($item));
113 $xml .= $this->_quote((string) $item);
120 foreach ($this->_content as $item) {
121 if (is_object($item)) {
122 if (method_exists($item, 'aspdf'))
123 $pdf .= $item->asPDF();
124 elseif (method_exists($item, 'asstring'))
125 $pdf .= $this->_quote($item->asString());
127 $pdf .= sprintf("==Object(%s)==", get_class($item));
130 $pdf .= $this->_quote((string) $item);
135 function asString () {
137 foreach ($this->_content as $item) {
138 if (is_object($item)) {
139 if (method_exists($item, 'asstring'))
140 $val .= $item->asString();
142 $val .= sprintf("==Object(%s)==", get_class($item));
145 $val .= (string) $item;
152 * See if element is empty.
154 * Empty means it has no content.
155 * @return bool True if empty.
157 function isEmpty () {
158 if (empty($this->_content))
160 foreach ($this->_content as $x) {
161 if (is_string($x) ? strlen($x) : !empty($x))
167 function _quote ($string) {
168 if (!$string) return $string;
169 if (check_php_version(4,1) and isset($GLOBALS['charset']))
170 return htmlspecialchars($string, ENT_COMPAT, $GLOBALS['charset']);
172 return htmlspecialchars($string);
179 * @param $tagname string Tag of html element.
181 class XmlElement extends XmlContent
183 function XmlElement ($tagname /* , $attr_or_content , ...*/) {
184 //FIXME: php5 incompatible
186 $this->_init(func_get_args());
189 function _init ($args) {
190 if (!is_array($args))
191 $args = func_get_args();
193 assert(count($args) >= 1);
194 //assert(is_string($args[0]));
195 $this->_tag = array_shift($args);
197 if ($args && is_array($args[0]))
198 $this->_attr = array_shift($args);
200 $this->_attr = array();
201 if ($args && $args[0] === false)
205 $this->setContent($args);
208 /** Methods only needed for XmlParser,
209 * to be fully compatible to perl Html::Element
211 // doesn't yet work with php5 as __destruct()
212 function _destruct () {
213 if ($this->hasChildren()) {
214 foreach ($this->getChildren() as $node) {
220 unset($this->_content);
223 function getChildren () {
224 return $this->_children;
227 function hasChildren () {
228 return !empty($this->_children);
230 /* End XmlParser Methods
237 function setAttr ($attr, $value = false) {
238 if (is_array($attr)) {
239 assert($value === false);
240 foreach ($attr as $a => $v) {
241 $this->_attr[strtolower($a)] = $v;
242 //$this->set($a, $v);
247 assert(is_string($attr));
249 if ($value === false) {
250 unset($this->_attr[$attr]);
255 $this->_attr[$attr] = (string) $value;
258 if ($attr == 'class')
259 unset($this->_classes);
262 function getAttr ($attr) {
263 if ($attr == 'class')
264 $this->_setClasses();
266 if (isset($this->_attr[strtolower($attr)]))
267 return $this->_attr[strtolower($attr)];
272 function _getClasses() {
273 if (!isset($this->_classes)) {
274 $this->_classes = array();
275 if (isset($this->_attr['class'])) {
276 $classes = explode(' ', (string) $this->_attr['class']);
277 foreach ($classes as $class) {
278 $class = trim($class);
280 $this->_classes[$class] = $class;
284 return $this->_classes;
287 function _setClasses() {
288 if (isset($this->_classes)) {
290 $this->_attr['class'] = join(' ', $this->_classes);
292 unset($this->_attr['class']);
297 * Manipulate the elements CSS class membership.
299 * This adds or remove an elements membership
300 * in a give CSS class.
302 * @param $class string
304 * @param $in_class bool
305 * If true (the default) the element is added to class $class.
306 * If false, the element is removed from the class.
308 function setInClass($class, $in_class=true) {
309 $this->_getClasses();
310 $class = trim($class);
312 $this->_classes[$class] = $class;
314 unset($this->_classes[$class]);
318 * Is element in a given (CSS) class?
320 * This checks for the presence of a particular class in the
321 * elements 'class' attribute.
323 * @param $class string The class to check for.
324 * @return bool True if the element is a member of $class.
326 function inClass($class) {
327 $this->_parseClasses();
328 return isset($this->_classes[trim($class)]);
331 function startTag() {
332 $start = "<" . $this->_tag;
333 $this->_setClasses();
334 foreach ($this->_attr as $attr => $val) {
340 $qval = str_replace("\"", '"', $this->_quote((string)$val));
341 $start .= " $attr=\"$qval\"";
347 function emptyTag() {
348 return substr($this->startTag(), 0, -1) . "/>";
353 return "</$this->_tag>";
357 function printXML () {
358 if ($this->isEmpty())
359 echo $this->emptyTag();
361 echo $this->startTag();
362 // FIXME: The next two lines could be removed for efficiency
363 if (!$this->hasInlineContent())
365 XmlContent::printXML();
366 echo "</$this->_tag>";
368 if (!$this->isInlineElement())
373 if ($this->isEmpty()) {
374 $xml = $this->emptyTag();
377 $xml = $this->startTag();
378 // FIXME: The next two lines could be removed for efficiency
379 if (!$this->hasInlineContent())
381 $xml .= XmlContent::asXML();
382 $xml .= "</$this->_tag>";
384 if (!$this->isInlineElement())
390 * Can this element have inline content?
392 * This is a hack, but is probably the best one can do without
393 * knowledge of the DTD...
395 function hasInlineContent () {
397 if (empty($this->_content))
399 if (is_object($this->_content[0]))
405 * Is this element part of inline content?
407 * This is a hack, but is probably the best one can do without
408 * knowledge of the DTD...
410 function isInlineElement () {
417 function RawXml ($xml_text) {
418 $this->_xml = $xml_text;
421 function printXML () {
429 function isEmpty () {
430 return empty($this->_xml);
434 class FormattedText {
435 function FormattedText ($fs /* , ... */) {
437 $this->_init(func_get_args());
441 function _init ($args) {
442 $this->_fs = array_shift($args);
444 // PHP's sprintf doesn't support variable width specifiers,
445 // like sprintf("%*s", 10, "x"); --- so we won't either.
447 if (! preg_match_all('/(?<!%)%(\d+)\$/x', $this->_fs, $m)) {
448 $this->_args = $args;
451 // Format string has '%2$s' style argument reordering.
452 // PHP doesn't support this.
453 if (preg_match('/(?<!%)%[- ]?\d*[^- \d$]/x', $this->_fs)) // $fmt
454 // literal variable name substitution only to keep locale
455 // strings uncluttered
456 trigger_error(sprintf(_("Can't mix '%s' with '%s' type format strings"),
457 '%1\$s','%s'), E_USER_WARNING);
459 $this->_fs = preg_replace('/(?<!%)%\d+\$/x', '%', $this->_fs);
461 $this->_args = array();
462 foreach($m[1] as $argnum) {
463 if ($argnum < 1 || $argnum > count($args))
464 trigger_error(sprintf("%s: argument index out of range",
465 $argnum), E_USER_WARNING);
466 $this->_args[] = $args[$argnum - 1];
472 // Not all PHP's have vsprintf, so...
473 $args[] = XmlElement::_quote((string)$this->_fs);
474 foreach ($this->_args as $arg)
475 $args[] = AsXML($arg);
476 return call_user_func_array('sprintf', $args);
479 function printXML () {
480 // Not all PHP's have vsprintf, so...
481 $args[] = XmlElement::_quote((string)$this->_fs);
482 foreach ($this->_args as $arg)
483 $args[] = AsXML($arg);
484 call_user_func_array('printf', $args);
487 function asString() {
488 $args[] = $this->_fs;
489 foreach ($this->_args as $arg)
490 $args[] = AsString($arg);
491 return call_user_func_array('sprintf', $args);
497 * Error[2048]: Non-static method XmlContent::_quote() should not be called statically
499 function XmlContent_quote ($string) {
500 if (!$string) return $string;
501 if (check_php_version(4,1) and isset($GLOBALS['charset']))
502 return htmlspecialchars($string, ENT_COMPAT, $GLOBALS['charset']);
504 return htmlspecialchars($string);
507 function PrintXML ($val /* , ... */ ) {
508 if (func_num_args() > 1) {
509 foreach (func_get_args() as $arg)
512 elseif (is_object($val)) {
513 if (method_exists($val, 'printxml'))
515 elseif (method_exists($val, 'asxml')) {
518 elseif (method_exists($val, 'asstring'))
519 echo XmlContent_quote($val->asString());
521 printf("==Object(%s)==", get_class($val));
523 elseif (is_array($val)) {
525 // Use XmlContent objects instead of arrays for collections of XmlElements.
526 trigger_error("Passing arrays to PrintXML() is deprecated: (" . AsXML($val, true) . ")",
532 echo (string)XmlContent_quote((string)$val);
535 function AsXML ($val /* , ... */) {
538 if (func_num_args() > 1) {
540 foreach (func_get_args() as $arg)
544 elseif (is_object($val)) {
545 if (method_exists($val, 'asxml'))
546 return $val->asXML();
547 elseif (method_exists($val, 'asstring'))
548 return XmlContent_quote($val->asString());
550 return sprintf("==Object(%s)==", get_class($val));
552 elseif (is_array($val)) {
554 // Use XmlContent objects instead of arrays for collections of XmlElements.
555 if (empty($nowarn)) {
557 trigger_error("Passing arrays to AsXML() is deprecated: (" . AsXML($val) . ")",
567 return XmlContent_quote((string)$val);
570 function AsString ($val) {
571 if (func_num_args() > 1) {
573 foreach (func_get_args() as $arg)
574 $str .= AsString($arg);
577 elseif (is_object($val)) {
578 if (method_exists($val, 'asstring'))
579 return $val->asString();
581 return sprintf("==Object(%s)==", get_class($val));
583 elseif (is_array($val)) {
585 // Use XmlContent objects instead of arrays for collections of XmlElements.
586 trigger_error("Passing arrays to AsString() is deprecated", E_USER_NOTICE);
589 $str .= AsString($x);
593 return (string) $val;
597 function fmt ($fs /* , ... */) {
598 $s = new FormattedText(false);
600 $args = func_get_args();
601 $args[0] = _($args[0]);
606 // $Log: not supported by cvs2svn $
607 // Revision 1.35 2004/11/21 11:59:18 rurban
608 // remove final \n to be ob_cache independent
610 // Revision 1.34 2004/10/12 13:13:19 rurban
611 // php5 compatibility (5.0.1 ok)
613 // Revision 1.33 2004/07/02 09:55:58 rurban
614 // more stability fixes: new DISABLE_GETIMAGESIZE if your php crashes when loading LinkIcons: failing getimagesize in old phps; blockparser stabilized
616 // Revision 1.32 2004/06/20 15:30:05 rurban
617 // get_class case-sensitivity issues
619 // Revision 1.31 2004/06/20 14:42:54 rurban
620 // various php5 fixes (still broken at blockparser)
623 // (c-file-style: "gnu")
628 // c-hanging-comment-ender-p: nil
629 // indent-tabs-mode: nil