1 <?php rcs_id('$Id: XmlElement.php,v 1.23 2003-04-01 17:20:16 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) {
138 if (is_string($x) ? strlen($x) : !empty($x))
144 function _quote ($string) {
145 return htmlspecialchars($string);
152 * @param $tagname string Tag of html element.
154 class XmlElement extends XmlContent
156 function XmlElement ($tagname /* , $attr_or_content , ...*/) {
158 $this->_init(func_get_args());
161 function _init ($args) {
162 if (!is_array($args))
163 $args = func_get_args();
165 assert(count($args) >= 1);
166 //assert(is_string($args[0]));
167 $this->_tag = array_shift($args);
169 if ($args && is_array($args[0]))
170 $this->_attr = array_shift($args);
172 $this->_attr = array();
173 if ($args && $args[0] === false)
177 $this->setContent($args);
184 function setAttr ($attr, $value = false) {
185 if (is_array($attr)) {
186 assert($value === false);
187 foreach ($attr as $a => $v)
192 assert(is_string($attr));
194 if ($value === false) {
195 unset($this->_attr[$attr]);
200 $this->_attr[$attr] = (string) $value;
203 if ($attr == 'class')
204 unset($this->_classes);
207 function getAttr ($attr) {
208 if ($attr == 'class')
209 $this->_setClasses();
211 if (isset($this->_attr[$attr]))
212 return $this->_attr[$attr];
217 function _getClasses() {
218 if (!isset($this->_classes)) {
219 $this->_classes = array();
220 if (isset($this->_attr['class'])) {
221 $classes = explode(' ', (string) $this->_attr['class']);
222 foreach ($classes as $class) {
223 $class = trim($class);
225 $this->_classes[$class] = $class;
229 return $this->_classes;
232 function _setClasses() {
233 if (isset($this->_classes)) {
235 $this->_attr['class'] = join(' ', $this->_classes);
237 unset($this->_attr['class']);
242 * Manipulate the elements CSS class membership.
244 * This adds or remove an elements membership
245 * in a give CSS class.
247 * @param $class string
249 * @param $in_class bool
250 * If true (the default) the element is added to class $class.
251 * If false, the element is removed from the class.
253 function setInClass($class, $in_class=true) {
254 $this->_getClasses();
255 $class = trim($class);
257 $this->_classes[$class] = $class;
259 unset($this->_classes[$class]);
263 * Is element in a given (CSS) class?
265 * This checks for the presence of a particular class in the
266 * elements 'class' attribute.
268 * @param $class string The class to check for.
269 * @return bool True if the element is a member of $class.
271 function inClass($class) {
272 $this->_parseClasses();
273 return isset($this->_classes[trim($class)]);
276 function startTag() {
277 $start = "<" . $this->_tag;
278 $this->_setClasses();
279 foreach ($this->_attr as $attr => $val) {
285 $qval = str_replace("\"", '"', $this->_quote((string)$val));
286 $start .= " $attr=\"$qval\"";
292 function emptyTag() {
293 return substr($this->startTag(), 0, -1) . "/>";
298 return "</$this->_tag>";
302 function printXML () {
303 if ($this->isEmpty())
304 echo $this->emptyTag();
306 echo $this->startTag();
307 // FIXME: The next two lines could be removed for efficiency
308 if (!$this->hasInlineContent())
310 XmlContent::printXML();
311 echo "</$this->_tag>";
313 if (!$this->isInlineElement())
318 if ($this->isEmpty()) {
319 $xml = $this->emptyTag();
322 $xml = $this->startTag();
323 // FIXME: The next two lines could be removed for efficiency
324 if (!$this->hasInlineContent())
326 $xml .= XmlContent::asXML();
327 $xml .= "</$this->_tag>";
329 if (!$this->isInlineElement())
335 * Can this element have inline content?
337 * This is a hack, but is probably the best one can do without
338 * knowledge of the DTD...
340 function hasInlineContent () {
342 if (empty($this->_content))
344 if (is_object($this->_content[0]))
350 * Is this element part of inline content?
352 * This is a hack, but is probably the best one can do without
353 * knowledge of the DTD...
355 function isInlineElement () {
362 function RawXml ($xml_text) {
363 $this->_xml = $xml_text;
366 function printXML () {
374 function isEmpty () {
375 return empty($this->_xml);
379 class FormattedText {
380 function FormattedText ($fs /* , ... */) {
382 $this->_init(func_get_args());
386 function _init ($args) {
387 $this->_fs = array_shift($args);
389 // PHP's sprintf doesn't support variable width specifiers,
390 // like sprintf("%*s", 10, "x"); --- so we won't either.
392 if (! preg_match_all('/(?<!%)%(\d+)\$/x', $this->_fs, $m)) {
393 $this->_args = $args;
396 // Format string has '%2$s' style argument reordering.
397 // PHP doesn't support this.
398 if (preg_match('/(?<!%)%[- ]?\d*[^- \d$]/x', $this->_fs)) // $fmt
399 // literal variable name substitution only to keep locale
400 // strings uncluttered
401 trigger_error(sprintf(_("Can't mix '%s' with '%s' type format strings"),
402 '%1\$s','%s'), E_USER_WARNING);
404 $this->_fs = preg_replace('/(?<!%)%\d+\$/x', '%', $this->_fs);
406 $this->_args = array();
407 foreach($m[1] as $argnum) {
408 if ($argnum < 1 || $argnum > count($args))
409 trigger_error(sprintf("%s: argument index out of range",
410 $argnum), E_USER_WARNING);
411 $this->_args[] = $args[$argnum - 1];
417 // Not all PHP's have vsprintf, so...
418 $args[] = XmlElement::_quote((string)$this->_fs);
419 foreach ($this->_args as $arg)
420 $args[] = AsXML($arg);
421 return call_user_func_array('sprintf', $args);
424 function printXML () {
425 // Not all PHP's have vsprintf, so...
426 $args[] = XmlElement::_quote((string)$this->_fs);
427 foreach ($this->_args as $arg)
428 $args[] = AsXML($arg);
429 call_user_func_array('printf', $args);
432 function asString() {
433 $args[] = $this->_fs;
434 foreach ($this->_args as $arg)
435 $args[] = AsString($arg);
436 return call_user_func_array('sprintf', $args);
440 function PrintXML ($val /* , ... */ ) {
441 if (func_num_args() > 1) {
442 foreach (func_get_args() as $arg)
445 elseif (is_object($val)) {
446 if (method_exists($val, 'printxml'))
448 elseif (method_exists($val, 'asxml')) {
451 elseif (method_exists($val, 'asstring'))
452 echo XmlContent::_quote($val->asString());
454 printf("==Object(%s)==", get_class($val));
456 elseif (is_array($val)) {
458 // Use XmlContent objects instead of arrays for collections of XmlElements.
459 trigger_error("Passing arrays to PrintXML() is deprecated: (" . AsXML($val, true) . ")",
465 echo (string)XmlContent::_quote((string)$val);
468 function AsXML ($val /* , ... */) {
471 if (func_num_args() > 1) {
473 foreach (func_get_args() as $arg)
477 elseif (is_object($val)) {
478 if (method_exists($val, 'asxml'))
479 return $val->asXML();
480 elseif (method_exists($val, 'asstring'))
481 return XmlContent::_quote($val->asString());
483 return sprintf("==Object(%s)==", get_class($val));
485 elseif (is_array($val)) {
487 // Use XmlContent objects instead of arrays for collections of XmlElements.
488 if (empty($nowarn)) {
490 trigger_error("Passing arrays to AsXML() is deprecated: (" . AsXML($val) . ")",
500 return XmlContent::_quote((string)$val);
503 function AsString ($val) {
504 if (func_num_args() > 1) {
506 foreach (func_get_args() as $arg)
507 $str .= AsString($arg);
510 elseif (is_object($val)) {
511 if (method_exists($val, 'asstring'))
512 return $val->asString();
514 return sprintf("==Object(%s)==", get_class($val));
516 elseif (is_array($val)) {
518 // Use XmlContent objects instead of arrays for collections of XmlElements.
519 trigger_error("Passing arrays to AsString() is deprecated", E_USER_NOTICE);
522 $str .= AsString($x);
526 return (string) $val;
530 function fmt ($fs /* , ... */) {
531 $s = new FormattedText(false);
533 $args = func_get_args();
534 $args[0] = _($args[0]);
539 // (c-file-style: "gnu")
544 // c-hanging-comment-ender-p: nil
545 // indent-tabs-mode: nil