1 <?php rcs_id('$Id: XmlElement.php,v 1.14 2002-01-27 22:06:38 dairiki Exp $');
3 * Code for writing XML.
9 * @param $tagname string Tag of html element.
11 * If $tagname is set to <code>false</code> you get a fake element with no start
12 * or end tag, (just content, if any). This is useful for creating a single
13 * quasi-element object, which contains a number of other elements.
17 function XmlElement ($tagname /* , $attr_or_content , ...*/) {
18 $this->_init(func_get_args());
21 function _init ($args) {
23 $args = func_get_args();
25 assert(count($args) >= 1);
26 //assert(is_string($args[0]));
27 $this->_tag = array_shift($args);
29 if ($args && is_array($args[0]))
30 $this->_attr = array_shift($args);
32 $this->_attr = array();
33 if (count($args) > 1 && ! $args[0])
37 if (count($args) == 1 && is_array($args[0]))
39 $this->_content = $args;
46 function setAttr ($attr, $value = false) {
47 if (is_array($attr)) {
48 assert($value === false);
49 foreach ($attr as $a => $v)
54 assert(is_string($attr));
55 if ($value === false) {
56 unset($this->_attr[$attr]);
60 $this->_attr[$attr] = (string) $value;
63 function getAttr ($attr) {
64 if (isset($this->_attr[$attr]))
65 return $this->_attr[$attr];
70 function pushContent ($args /*, ...*/) {
71 $c = &$this->_content;
72 if (func_num_args() != 1 || ! is_array($args))
73 $args = func_get_args();
74 array_splice($c, count($c), 0, $args);
77 function unshiftContent ($args /*, ...*/) {
78 $c = &$this->_content;
79 if (func_num_args() != 1 || ! is_array($args))
80 $args = func_get_args();
81 array_splice($c, 0, 0, $args);
84 function getContent () {
85 return $this->_content;
91 function _startTag() {
92 $start = "<" . $this->_tag;
93 foreach ($this->_attr as $attr => $val) {
99 $start .= " $attr=\"" . $this->_quoteAttr($val) . "\"";
108 function _emptyTag() {
109 return substr($this->_startTag(), 0, -1) . "/>";
112 function startTag() {
113 return $this->_tag ? $this->_startTag() : '';
117 return $this->_tag ? "</$this->_tag>" : '';
121 function printXML () {
123 if (! $this->_content) {
125 echo $this->_emptyTag();
128 $sep = $this->hasInlineContent() ? "" : "\n";
130 echo $this->_startTag() . $sep;
131 foreach ($this->_content as $c) {
136 echo "</$this->_tag>";
141 if (! $this->_content)
142 return $this->_tag ? $this->_emptyTag() : '';
144 $sep = $this->hasInlineContent() ? "" : "\n";
147 $xml .= $this->_startTag() . $sep;
148 foreach ($this->_content as $c)
149 $xml .= AsXML($c) . $sep;
151 $xml .= "</$this->_tag>";
155 function asString () {
157 foreach ($this->_content as $c)
158 $str .= AsString($c);
163 * See if element is empty.
165 * Empty means it has no content.
166 * @return bool True if empty.
168 * Bugs: This fails if any of the content is itself an empty array.
169 * We might want to fix things so that content gets flattend by
170 * {push,unshift}Content, rather than by {print,as}XML.
172 function isEmpty () {
173 if (empty($this->_content))
175 foreach ($this->_content as $x) {
182 function hasInlineContent () {
183 // FIXME: This is a hack.
184 if (empty($this->_content))
186 if (is_object($this->_content[0]))
191 function _quote ($string) {
192 return str_replace('<', '<',
193 str_replace('>', '>',
194 str_replace('&', '&', $string)));
197 function _quoteAttr ($value) {
198 return str_replace('"', '"', XmlElement::_quote($value));
199 //"<--kludge for brain-dead syntax coloring
204 function RawXml ($xml_text) {
205 $this->_xml = $xml_text;
208 function printXML () {
216 function isEmpty () {
217 return empty($this->_xml);
221 class FormattedText {
222 function FormattedText ($fs /* , ... */) {
224 $this->_init(func_get_args());
228 function _init ($args) {
229 $this->_fs = array_shift($args);
231 // PHP's sprintf doesn't support variable width specifiers,
232 // like sprintf("%*s", 10, "x"); --- so we won't either.
234 if (! preg_match_all('/(?<!%)%(\d+)\$/x', $this->_fs, $m)) {
235 $this->_args = $args;
238 // Format string has '%2$s' style argument reordering.
239 // PHP doesn't support this.
240 if (preg_match('/(?<!%)%[- ]?\d*[^- \d$]/x', $fmt))
241 // literal variable name substitution only to keep locale
242 // strings uncluttered
243 trigger_error(sprintf(_("Can't mix '%s' with '%s' type format strings"),
244 '%1\$s','%s'), E_USER_WARNING);
246 $this->_fs = preg_replace('/(?<!%)%\d+\$/x', '%', $this->_fs);
248 $this->_args = array();
249 foreach($m[1] as $argnum) {
250 if ($argnum < 1 || $argnum > count($args))
251 trigger_error(sprintf("%s: argument index out of range",
252 $argnum), E_USER_WARNING);
253 $this->_args[] = $args[$argnum - 1];
259 // Not all PHP's have vsprintf, so...
260 $args[] = XmlElement::_quote($this->_fs);
261 foreach ($this->_args as $arg)
262 $args[] = AsXML($arg);
263 return call_user_func_array('sprintf', $args);
266 function printXML () {
267 // Not all PHP's have vsprintf, so...
268 $args[] = XmlElement::_quote($this->_fs);
269 foreach ($this->_args as $arg)
270 $args[] = AsXML($arg);
271 call_user_func_array('printf', $args);
274 function asString() {
275 $args[] = $this->_fs;
276 foreach ($this->_args as $arg)
277 $args[] = AsString($arg);
278 return call_user_func_array('sprintf', $args);
282 function PrintXML ($val) {
283 if (is_object($val)) {
284 if (method_exists($val, 'printxml'))
285 return $val->printXML();
286 elseif (method_exists($val, 'asxml')) {
290 elseif (method_exists($val, 'asstring'))
291 $val = $val->asString();
293 elseif (is_array($val)) {
299 echo (string)XmlElement::_quote($val);
302 function AsXML ($val) {
303 if (is_object($val)) {
304 if (method_exists($val, 'asxml'))
305 return $val->asXML();
306 elseif (method_exists($val, 'asstring'))
307 $val = $val->asString();
309 elseif (is_array($val)) {
316 return XmlElement::_quote((string)$val);
319 function AsString ($val) {
320 if (can($val, 'asString'))
321 return $val->asString();
322 elseif (is_array($val)) {
325 $str .= AsString($x);
329 return (string) $val;
334 function fmt ($fs /* , ... */) {
335 $s = new FormattedText(false);
337 $args = func_get_args();
338 $args[0] = _($args[0]);
343 // (c-file-style: "gnu")
348 // c-hanging-comment-ender-p: nil
349 // indent-tabs-mode: nil