commiting uncommited changes on live site
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / sites / all / modules-old / civicrm / packages / XML / Util.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * XML_Util
7 *
8 * XML Utilities package
9 *
10 * PHP versions 4 and 5
11 *
12 * LICENSE:
13 *
14 * Copyright (c) 2003-2008 Stephan Schmidt <schst@php.net>
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
19 * are met:
20 *
21 * * Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
23 * * Redistributions in binary form must reproduce the above copyright
24 * notice, this list of conditions and the following disclaimer in the
25 * documentation and/or other materials provided with the distribution.
26 * * The name of the author may not be used to endorse or promote products
27 * derived from this software without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
30 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
31 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
33 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
34 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
37 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 *
41 * @category XML
42 * @package XML_Util
43 * @author Stephan Schmidt <schst@php.net>
44 * @copyright 2003-2008 Stephan Schmidt <schst@php.net>
45 * @license http://opensource.org/licenses/bsd-license New BSD License
46 * @version CVS: $Id: Util.php,v 1.38 2008/11/13 00:03:38 ashnazg Exp $
47 * @link http://pear.php.net/package/XML_Util
48 */
49
50 /**
51 * error code for invalid chars in XML name
52 */
53 define('XML_UTIL_ERROR_INVALID_CHARS', 51);
54
55 /**
56 * error code for invalid chars in XML name
57 */
58 define('XML_UTIL_ERROR_INVALID_START', 52);
59
60 /**
61 * error code for non-scalar tag content
62 */
63 define('XML_UTIL_ERROR_NON_SCALAR_CONTENT', 60);
64
65 /**
66 * error code for missing tag name
67 */
68 define('XML_UTIL_ERROR_NO_TAG_NAME', 61);
69
70 /**
71 * replace XML entities
72 */
73 define('XML_UTIL_REPLACE_ENTITIES', 1);
74
75 /**
76 * embedd content in a CData Section
77 */
78 define('XML_UTIL_CDATA_SECTION', 5);
79
80 /**
81 * do not replace entitites
82 */
83 define('XML_UTIL_ENTITIES_NONE', 0);
84
85 /**
86 * replace all XML entitites
87 * This setting will replace <, >, ", ' and &
88 */
89 define('XML_UTIL_ENTITIES_XML', 1);
90
91 /**
92 * replace only required XML entitites
93 * This setting will replace <, " and &
94 */
95 define('XML_UTIL_ENTITIES_XML_REQUIRED', 2);
96
97 /**
98 * replace HTML entitites
99 * @link http://www.php.net/htmlentities
100 */
101 define('XML_UTIL_ENTITIES_HTML', 3);
102
103 /**
104 * Collapse all empty tags.
105 */
106 define('XML_UTIL_COLLAPSE_ALL', 1);
107
108 /**
109 * Collapse only empty XHTML tags that have no end tag.
110 */
111 define('XML_UTIL_COLLAPSE_XHTML_ONLY', 2);
112
113 /**
114 * utility class for working with XML documents
115 *
116
117 * @category XML
118 * @package XML_Util
119 * @author Stephan Schmidt <schst@php.net>
120 * @copyright 2003-2008 Stephan Schmidt <schst@php.net>
121 * @license http://opensource.org/licenses/bsd-license New BSD License
122 * @version Release: 1.2.1
123 * @link http://pear.php.net/package/XML_Util
124 */
125 class XML_Util
126 {
127 /**
128 * return API version
129 *
130 * @return string $version API version
131 * @access public
132 * @static
133 */
134 function apiVersion()
135 {
136 return '1.1';
137 }
138
139 /**
140 * replace XML entities
141 *
142 * With the optional second parameter, you may select, which
143 * entities should be replaced.
144 *
145 * <code>
146 * require_once 'XML/Util.php';
147 *
148 * // replace XML entites:
149 * $string = XML_Util::replaceEntities('This string contains < & >.');
150 * </code>
151 *
152 * With the optional third parameter, you may pass the character encoding
153 * <code>
154 * require_once 'XML/Util.php';
155 *
156 * // replace XML entites in UTF-8:
157 * $string = XML_Util::replaceEntities(
158 * 'This string contains < & > as well as ä, ö, ß, à and ê',
159 * XML_UTIL_ENTITIES_HTML,
160 * 'UTF-8'
161 * );
162 * </code>
163 *
164 * @param string $string string where XML special chars
165 * should be replaced
166 * @param int $replaceEntities setting for entities in attribute values
167 * (one of XML_UTIL_ENTITIES_XML,
168 * XML_UTIL_ENTITIES_XML_REQUIRED,
169 * XML_UTIL_ENTITIES_HTML)
170 * @param string $encoding encoding value (if any)...
171 * must be a valid encoding as determined
172 * by the htmlentities() function
173 *
174 * @return string string with replaced chars
175 * @access public
176 * @static
177 * @see reverseEntities()
178 */
179 function replaceEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML,
180 $encoding = 'ISO-8859-1')
181 {
182 switch ($replaceEntities) {
183 case XML_UTIL_ENTITIES_XML:
184 return strtr($string, array(
185 '&' => '&amp;',
186 '>' => '&gt;',
187 '<' => '&lt;',
188 '"' => '&quot;',
189 '\'' => '&apos;' ));
190 break;
191 case XML_UTIL_ENTITIES_XML_REQUIRED:
192 return strtr($string, array(
193 '&' => '&amp;',
194 '<' => '&lt;',
195 '"' => '&quot;' ));
196 break;
197 case XML_UTIL_ENTITIES_HTML:
198 return htmlentities($string, ENT_COMPAT, $encoding);
199 break;
200 }
201 return $string;
202 }
203
204 /**
205 * reverse XML entities
206 *
207 * With the optional second parameter, you may select, which
208 * entities should be reversed.
209 *
210 * <code>
211 * require_once 'XML/Util.php';
212 *
213 * // reverse XML entites:
214 * $string = XML_Util::reverseEntities('This string contains &lt; &amp; &gt;.');
215 * </code>
216 *
217 * With the optional third parameter, you may pass the character encoding
218 * <code>
219 * require_once 'XML/Util.php';
220 *
221 * // reverse XML entites in UTF-8:
222 * $string = XML_Util::reverseEntities(
223 * 'This string contains &lt; &amp; &gt; as well as'
224 * . ' &auml;, &ouml;, &szlig;, &agrave; and &ecirc;',
225 * XML_UTIL_ENTITIES_HTML,
226 * 'UTF-8'
227 * );
228 * </code>
229 *
230 * @param string $string string where XML special chars
231 * should be replaced
232 * @param int $replaceEntities setting for entities in attribute values
233 * (one of XML_UTIL_ENTITIES_XML,
234 * XML_UTIL_ENTITIES_XML_REQUIRED,
235 * XML_UTIL_ENTITIES_HTML)
236 * @param string $encoding encoding value (if any)...
237 * must be a valid encoding as determined
238 * by the html_entity_decode() function
239 *
240 * @return string string with replaced chars
241 * @access public
242 * @static
243 * @see replaceEntities()
244 */
245 function reverseEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML,
246 $encoding = 'ISO-8859-1')
247 {
248 switch ($replaceEntities) {
249 case XML_UTIL_ENTITIES_XML:
250 return strtr($string, array(
251 '&amp;' => '&',
252 '&gt;' => '>',
253 '&lt;' => '<',
254 '&quot;' => '"',
255 '&apos;' => '\'' ));
256 break;
257 case XML_UTIL_ENTITIES_XML_REQUIRED:
258 return strtr($string, array(
259 '&amp;' => '&',
260 '&lt;' => '<',
261 '&quot;' => '"' ));
262 break;
263 case XML_UTIL_ENTITIES_HTML:
264 return html_entity_decode($string, ENT_COMPAT, $encoding);
265 break;
266 }
267 return $string;
268 }
269
270 /**
271 * build an xml declaration
272 *
273 * <code>
274 * require_once 'XML/Util.php';
275 *
276 * // get an XML declaration:
277 * $xmlDecl = XML_Util::getXMLDeclaration('1.0', 'UTF-8', true);
278 * </code>
279 *
280 * @param string $version xml version
281 * @param string $encoding character encoding
282 * @param bool $standalone document is standalone (or not)
283 *
284 * @return string xml declaration
285 * @access public
286 * @static
287 * @uses attributesToString() to serialize the attributes of the XML declaration
288 */
289 function getXMLDeclaration($version = '1.0', $encoding = null,
290 $standalone = null)
291 {
292 $attributes = array(
293 'version' => $version,
294 );
295 // add encoding
296 if ($encoding !== null) {
297 $attributes['encoding'] = $encoding;
298 }
299 // add standalone, if specified
300 if ($standalone !== null) {
301 $attributes['standalone'] = $standalone ? 'yes' : 'no';
302 }
303
304 return sprintf('<?xml%s?>',
305 XML_Util::attributesToString($attributes, false));
306 }
307
308 /**
309 * build a document type declaration
310 *
311 * <code>
312 * require_once 'XML/Util.php';
313 *
314 * // get a doctype declaration:
315 * $xmlDecl = XML_Util::getDocTypeDeclaration('rootTag','myDocType.dtd');
316 * </code>
317 *
318 * @param string $root name of the root tag
319 * @param string $uri uri of the doctype definition
320 * (or array with uri and public id)
321 * @param string $internalDtd internal dtd entries
322 *
323 * @return string doctype declaration
324 * @access public
325 * @static
326 * @since 0.2
327 */
328 function getDocTypeDeclaration($root, $uri = null, $internalDtd = null)
329 {
330 if (is_array($uri)) {
331 $ref = sprintf(' PUBLIC "%s" "%s"', $uri['id'], $uri['uri']);
332 } elseif (!empty($uri)) {
333 $ref = sprintf(' SYSTEM "%s"', $uri);
334 } else {
335 $ref = '';
336 }
337
338 if (empty($internalDtd)) {
339 return sprintf('<!DOCTYPE %s%s>', $root, $ref);
340 } else {
341 return sprintf("<!DOCTYPE %s%s [\n%s\n]>", $root, $ref, $internalDtd);
342 }
343 }
344
345 /**
346 * create string representation of an attribute list
347 *
348 * <code>
349 * require_once 'XML/Util.php';
350 *
351 * // build an attribute string
352 * $att = array(
353 * 'foo' => 'bar',
354 * 'argh' => 'tomato'
355 * );
356 *
357 * $attList = XML_Util::attributesToString($att);
358 * </code>
359 *
360 * @param array $attributes attribute array
361 * @param bool|array $sort sort attribute list alphabetically,
362 * may also be an assoc array containing
363 * the keys 'sort', 'multiline', 'indent',
364 * 'linebreak' and 'entities'
365 * @param bool $multiline use linebreaks, if more than
366 * one attribute is given
367 * @param string $indent string used for indentation of
368 * multiline attributes
369 * @param string $linebreak string used for linebreaks of
370 * multiline attributes
371 * @param int $entities setting for entities in attribute values
372 * (one of XML_UTIL_ENTITIES_NONE,
373 * XML_UTIL_ENTITIES_XML,
374 * XML_UTIL_ENTITIES_XML_REQUIRED,
375 * XML_UTIL_ENTITIES_HTML)
376 *
377 * @return string string representation of the attributes
378 * @access public
379 * @static
380 * @uses replaceEntities() to replace XML entities in attribute values
381 * @todo allow sort also to be an options array
382 */
383 function attributesToString($attributes, $sort = true, $multiline = false,
384 $indent = ' ', $linebreak = "\n", $entities = XML_UTIL_ENTITIES_XML)
385 {
386 /*
387 * second parameter may be an array
388 */
389 if (is_array($sort)) {
390 if (isset($sort['multiline'])) {
391 $multiline = $sort['multiline'];
392 }
393 if (isset($sort['indent'])) {
394 $indent = $sort['indent'];
395 }
396 if (isset($sort['linebreak'])) {
397 $multiline = $sort['linebreak'];
398 }
399 if (isset($sort['entities'])) {
400 $entities = $sort['entities'];
401 }
402 if (isset($sort['sort'])) {
403 $sort = $sort['sort'];
404 } else {
405 $sort = true;
406 }
407 }
408 $string = '';
409 if (is_array($attributes) && !empty($attributes)) {
410 if ($sort) {
411 ksort($attributes);
412 }
413 if ( !$multiline || count($attributes) == 1) {
414 foreach ($attributes as $key => $value) {
415 if ($entities != XML_UTIL_ENTITIES_NONE) {
416 if ($entities === XML_UTIL_CDATA_SECTION) {
417 $entities = XML_UTIL_ENTITIES_XML;
418 }
419 $value = XML_Util::replaceEntities($value, $entities);
420 }
421 $string .= ' ' . $key . '="' . $value . '"';
422 }
423 } else {
424 $first = true;
425 foreach ($attributes as $key => $value) {
426 if ($entities != XML_UTIL_ENTITIES_NONE) {
427 $value = XML_Util::replaceEntities($value, $entities);
428 }
429 if ($first) {
430 $string .= ' ' . $key . '="' . $value . '"';
431 $first = false;
432 } else {
433 $string .= $linebreak . $indent . $key . '="' . $value . '"';
434 }
435 }
436 }
437 }
438 return $string;
439 }
440
441 /**
442 * Collapses empty tags.
443 *
444 * @param string $xml XML
445 * @param int $mode Whether to collapse all empty tags (XML_UTIL_COLLAPSE_ALL)
446 * or only XHTML (XML_UTIL_COLLAPSE_XHTML_ONLY) ones.
447 *
448 * @return string XML
449 * @access public
450 * @static
451 * @todo PEAR CS - unable to avoid "space after open parens" error
452 * in the IF branch
453 */
454 function collapseEmptyTags($xml, $mode = XML_UTIL_COLLAPSE_ALL)
455 {
456 if ($mode == XML_UTIL_COLLAPSE_XHTML_ONLY) {
457 return preg_replace(
458 '/<(area|base(?:font)?|br|col|frame|hr|img|input|isindex|link|meta|'
459 . 'param)([^>]*)><\/\\1>/s',
460 '<\\1\\2 />',
461 $xml);
462 } else {
463 return preg_replace('/<(\w+)([^>]*)><\/\\1>/s', '<\\1\\2 />', $xml);
464 }
465 }
466
467 /**
468 * create a tag
469 *
470 * This method will call XML_Util::createTagFromArray(), which
471 * is more flexible.
472 *
473 * <code>
474 * require_once 'XML/Util.php';
475 *
476 * // create an XML tag:
477 * $tag = XML_Util::createTag('myNs:myTag',
478 * array('foo' => 'bar'),
479 * 'This is inside the tag',
480 * 'http://www.w3c.org/myNs#');
481 * </code>
482 *
483 * @param string $qname qualified tagname (including namespace)
484 * @param array $attributes array containg attributes
485 * @param mixed $content the content
486 * @param string $namespaceUri URI of the namespace
487 * @param int $replaceEntities whether to replace XML special chars in
488 * content, embedd it in a CData section
489 * or none of both
490 * @param bool $multiline whether to create a multiline tag where
491 * each attribute gets written to a single line
492 * @param string $indent string used to indent attributes
493 * (_auto indents attributes so they start
494 * at the same column)
495 * @param string $linebreak string used for linebreaks
496 * @param bool $sortAttributes Whether to sort the attributes or not
497 *
498 * @return string XML tag
499 * @access public
500 * @static
501 * @see createTagFromArray()
502 * @uses createTagFromArray() to create the tag
503 */
504 function createTag($qname, $attributes = array(), $content = null,
505 $namespaceUri = null, $replaceEntities = XML_UTIL_REPLACE_ENTITIES,
506 $multiline = false, $indent = '_auto', $linebreak = "\n",
507 $sortAttributes = true)
508 {
509 $tag = array(
510 'qname' => $qname,
511 'attributes' => $attributes
512 );
513
514 // add tag content
515 if ($content !== null) {
516 $tag['content'] = $content;
517 }
518
519 // add namespace Uri
520 if ($namespaceUri !== null) {
521 $tag['namespaceUri'] = $namespaceUri;
522 }
523
524 return XML_Util::createTagFromArray($tag, $replaceEntities, $multiline,
525 $indent, $linebreak, $sortAttributes);
526 }
527
528 /**
529 * create a tag from an array
530 * this method awaits an array in the following format
531 * <pre>
532 * array(
533 * // qualified name of the tag
534 * 'qname' => $qname
535 *
536 * // namespace prefix (optional, if qname is specified or no namespace)
537 * 'namespace' => $namespace
538 *
539 * // local part of the tagname (optional, if qname is specified)
540 * 'localpart' => $localpart,
541 *
542 * // array containing all attributes (optional)
543 * 'attributes' => array(),
544 *
545 * // tag content (optional)
546 * 'content' => $content,
547 *
548 * // namespaceUri for the given namespace (optional)
549 * 'namespaceUri' => $namespaceUri
550 * )
551 * </pre>
552 *
553 * <code>
554 * require_once 'XML/Util.php';
555 *
556 * $tag = array(
557 * 'qname' => 'foo:bar',
558 * 'namespaceUri' => 'http://foo.com',
559 * 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'),
560 * 'content' => 'I\'m inside the tag',
561 * );
562 * // creating a tag with qualified name and namespaceUri
563 * $string = XML_Util::createTagFromArray($tag);
564 * </code>
565 *
566 * @param array $tag tag definition
567 * @param int $replaceEntities whether to replace XML special chars in
568 * content, embedd it in a CData section
569 * or none of both
570 * @param bool $multiline whether to create a multiline tag where each
571 * attribute gets written to a single line
572 * @param string $indent string used to indent attributes
573 * (_auto indents attributes so they start
574 * at the same column)
575 * @param string $linebreak string used for linebreaks
576 * @param bool $sortAttributes Whether to sort the attributes or not
577 *
578 * @return string XML tag
579 * @access public
580 * @static
581 * @see createTag()
582 * @uses attributesToString() to serialize the attributes of the tag
583 * @uses splitQualifiedName() to get local part and namespace of a qualified name
584 * @uses createCDataSection()
585 * @uses raiseError()
586 */
587 function createTagFromArray($tag, $replaceEntities = XML_UTIL_REPLACE_ENTITIES,
588 $multiline = false, $indent = '_auto', $linebreak = "\n",
589 $sortAttributes = true)
590 {
591 if (isset($tag['content']) && !is_scalar($tag['content'])) {
592 return XML_Util::raiseError('Supplied non-scalar value as tag content',
593 XML_UTIL_ERROR_NON_SCALAR_CONTENT);
594 }
595
596 if (!isset($tag['qname']) && !isset($tag['localPart'])) {
597 return XML_Util::raiseError('You must either supply a qualified name '
598 . '(qname) or local tag name (localPart).',
599 XML_UTIL_ERROR_NO_TAG_NAME);
600 }
601
602 // if no attributes hav been set, use empty attributes
603 if (!isset($tag['attributes']) || !is_array($tag['attributes'])) {
604 $tag['attributes'] = array();
605 }
606
607 if (isset($tag['namespaces'])) {
608 foreach ($tag['namespaces'] as $ns => $uri) {
609 $tag['attributes']['xmlns:' . $ns] = $uri;
610 }
611 }
612
613 if (!isset($tag['qname'])) {
614 // qualified name is not given
615
616 // check for namespace
617 if (isset($tag['namespace']) && !empty($tag['namespace'])) {
618 $tag['qname'] = $tag['namespace'] . ':' . $tag['localPart'];
619 } else {
620 $tag['qname'] = $tag['localPart'];
621 }
622 } elseif (isset($tag['namespaceUri']) && !isset($tag['namespace'])) {
623 // namespace URI is set, but no namespace
624
625 $parts = XML_Util::splitQualifiedName($tag['qname']);
626
627 $tag['localPart'] = $parts['localPart'];
628 if (isset($parts['namespace'])) {
629 $tag['namespace'] = $parts['namespace'];
630 }
631 }
632
633 if (isset($tag['namespaceUri']) && !empty($tag['namespaceUri'])) {
634 // is a namespace given
635 if (isset($tag['namespace']) && !empty($tag['namespace'])) {
636 $tag['attributes']['xmlns:' . $tag['namespace']] =
637 $tag['namespaceUri'];
638 } else {
639 // define this Uri as the default namespace
640 $tag['attributes']['xmlns'] = $tag['namespaceUri'];
641 }
642 }
643
644 // check for multiline attributes
645 if ($multiline === true) {
646 if ($indent === '_auto') {
647 $indent = str_repeat(' ', (strlen($tag['qname'])+2));
648 }
649 }
650
651 // create attribute list
652 $attList = XML_Util::attributesToString($tag['attributes'],
653 $sortAttributes, $multiline, $indent, $linebreak, $replaceEntities);
654 if (!isset($tag['content']) || (string)$tag['content'] == '') {
655 $tag = sprintf('<%s%s />', $tag['qname'], $attList);
656 } else {
657 switch ($replaceEntities) {
658 case XML_UTIL_ENTITIES_NONE:
659 break;
660 case XML_UTIL_CDATA_SECTION:
661 $tag['content'] = XML_Util::createCDataSection($tag['content']);
662 break;
663 default:
664 $tag['content'] = XML_Util::replaceEntities($tag['content'],
665 $replaceEntities);
666 break;
667 }
668 $tag = sprintf('<%s%s>%s</%s>', $tag['qname'], $attList, $tag['content'],
669 $tag['qname']);
670 }
671 return $tag;
672 }
673
674 /**
675 * create a start element
676 *
677 * <code>
678 * require_once 'XML/Util.php';
679 *
680 * // create an XML start element:
681 * $tag = XML_Util::createStartElement('myNs:myTag',
682 * array('foo' => 'bar') ,'http://www.w3c.org/myNs#');
683 * </code>
684 *
685 * @param string $qname qualified tagname (including namespace)
686 * @param array $attributes array containg attributes
687 * @param string $namespaceUri URI of the namespace
688 * @param bool $multiline whether to create a multiline tag where each
689 * attribute gets written to a single line
690 * @param string $indent string used to indent attributes (_auto indents
691 * attributes so they start at the same column)
692 * @param string $linebreak string used for linebreaks
693 * @param bool $sortAttributes Whether to sort the attributes or not
694 *
695 * @return string XML start element
696 * @access public
697 * @static
698 * @see createEndElement(), createTag()
699 */
700 function createStartElement($qname, $attributes = array(), $namespaceUri = null,
701 $multiline = false, $indent = '_auto', $linebreak = "\n",
702 $sortAttributes = true)
703 {
704 // if no attributes hav been set, use empty attributes
705 if (!isset($attributes) || !is_array($attributes)) {
706 $attributes = array();
707 }
708
709 if ($namespaceUri != null) {
710 $parts = XML_Util::splitQualifiedName($qname);
711 }
712
713 // check for multiline attributes
714 if ($multiline === true) {
715 if ($indent === '_auto') {
716 $indent = str_repeat(' ', (strlen($qname)+2));
717 }
718 }
719
720 if ($namespaceUri != null) {
721 // is a namespace given
722 if (isset($parts['namespace']) && !empty($parts['namespace'])) {
723 $attributes['xmlns:' . $parts['namespace']] = $namespaceUri;
724 } else {
725 // define this Uri as the default namespace
726 $attributes['xmlns'] = $namespaceUri;
727 }
728 }
729
730 // create attribute list
731 $attList = XML_Util::attributesToString($attributes, $sortAttributes,
732 $multiline, $indent, $linebreak);
733 $element = sprintf('<%s%s>', $qname, $attList);
734 return $element;
735 }
736
737 /**
738 * create an end element
739 *
740 * <code>
741 * require_once 'XML/Util.php';
742 *
743 * // create an XML start element:
744 * $tag = XML_Util::createEndElement('myNs:myTag');
745 * </code>
746 *
747 * @param string $qname qualified tagname (including namespace)
748 *
749 * @return string XML end element
750 * @access public
751 * @static
752 * @see createStartElement(), createTag()
753 */
754 function createEndElement($qname)
755 {
756 $element = sprintf('</%s>', $qname);
757 return $element;
758 }
759
760 /**
761 * create an XML comment
762 *
763 * <code>
764 * require_once 'XML/Util.php';
765 *
766 * // create an XML start element:
767 * $tag = XML_Util::createComment('I am a comment');
768 * </code>
769 *
770 * @param string $content content of the comment
771 *
772 * @return string XML comment
773 * @access public
774 * @static
775 */
776 function createComment($content)
777 {
778 $comment = sprintf('<!-- %s -->', $content);
779 return $comment;
780 }
781
782 /**
783 * create a CData section
784 *
785 * <code>
786 * require_once 'XML/Util.php';
787 *
788 * // create a CData section
789 * $tag = XML_Util::createCDataSection('I am content.');
790 * </code>
791 *
792 * @param string $data data of the CData section
793 *
794 * @return string CData section with content
795 * @access public
796 * @static
797 */
798 function createCDataSection($data)
799 {
800 return sprintf('<![CDATA[%s]]>',
801 preg_replace('/\]\]>/', ']]]]><![CDATA[>', strval($data)));
802
803 }
804
805 /**
806 * split qualified name and return namespace and local part
807 *
808 * <code>
809 * require_once 'XML/Util.php';
810 *
811 * // split qualified tag
812 * $parts = XML_Util::splitQualifiedName('xslt:stylesheet');
813 * </code>
814 * the returned array will contain two elements:
815 * <pre>
816 * array(
817 * 'namespace' => 'xslt',
818 * 'localPart' => 'stylesheet'
819 * );
820 * </pre>
821 *
822 * @param string $qname qualified tag name
823 * @param string $defaultNs default namespace (optional)
824 *
825 * @return array array containing namespace and local part
826 * @access public
827 * @static
828 */
829 function splitQualifiedName($qname, $defaultNs = null)
830 {
831 if (strstr($qname, ':')) {
832 $tmp = explode(':', $qname);
833 return array(
834 'namespace' => $tmp[0],
835 'localPart' => $tmp[1]
836 );
837 }
838 return array(
839 'namespace' => $defaultNs,
840 'localPart' => $qname
841 );
842 }
843
844 /**
845 * check, whether string is valid XML name
846 *
847 * <p>XML names are used for tagname, attribute names and various
848 * other, lesser known entities.</p>
849 * <p>An XML name may only consist of alphanumeric characters,
850 * dashes, undescores and periods, and has to start with a letter
851 * or an underscore.</p>
852 *
853 * <code>
854 * require_once 'XML/Util.php';
855 *
856 * // verify tag name
857 * $result = XML_Util::isValidName('invalidTag?');
858 * if (is_a($result, 'PEAR_Error')) {
859 * print 'Invalid XML name: ' . $result->getMessage();
860 * }
861 * </code>
862 *
863 * @param string $string string that should be checked
864 *
865 * @return mixed true, if string is a valid XML name, PEAR error otherwise
866 * @access public
867 * @static
868 * @todo support for other charsets
869 * @todo PEAR CS - unable to avoid 85-char limit on second preg_match
870 */
871 function isValidName($string)
872 {
873 // check for invalid chars
874 if (!preg_match('/^[[:alpha:]_]$/', $string{0})) {
875 return XML_Util::raiseError('XML names may only start with letter '
876 . 'or underscore', XML_UTIL_ERROR_INVALID_START);
877 }
878
879 // check for invalid chars
880 if (!preg_match('/^([[:alpha:]_]([[:alnum:]\-\.]*)?:)?[[:alpha:]_]([[:alnum:]\_\-\.]+)?$/',
881 $string)
882 ) {
883 return XML_Util::raiseError('XML names may only contain alphanumeric '
884 . 'chars, period, hyphen, colon and underscores',
885 XML_UTIL_ERROR_INVALID_CHARS);
886 }
887 // XML name is valid
888 return true;
889 }
890
891 /**
892 * replacement for XML_Util::raiseError
893 *
894 * Avoids the necessity to always require
895 * PEAR.php
896 *
897 * @param string $msg error message
898 * @param int $code error code
899 *
900 * @return PEAR_Error
901 * @access public
902 * @static
903 * @todo PEAR CS - should this use include_once instead?
904 */
905 function raiseError($msg, $code)
906 {
907 require_once 'PEAR.php';
908 return PEAR::raiseError($msg, $code);
909 }
910 }
911 ?>