PageRenderTime 430ms CodeModel.GetById 201ms app.highlight 63ms RepoModel.GetById 158ms app.codeStats 1ms

/php-webclient-ajax/server/PEAR/XML/Util.php

https://bitbucket.org/MelFlynn/zarafa
PHP | 794 lines | 300 code | 46 blank | 448 comment | 72 complexity | 34537bedaa113f7217af9c14740f25d1 MD5 | raw file
  1<?php
  2/*
  3 * Copyright 2005 - 2009  Zarafa B.V.
  4 * 
  5 * This program is free software: you can redistribute it and/or modify
  6 * it under the terms of the GNU Affero General Public License, version 3, 
  7 * as published by the Free Software Foundation with the following additional 
  8 * term according to sec. 7:
  9 *  
 10 * According to sec. 7 of the GNU Affero General Public License, version
 11 * 3, the terms of the AGPL are supplemented with the following terms:
 12 * 
 13 * "Zarafa" is a registered trademark of Zarafa B.V. The licensing of
 14 * the Program under the AGPL does not imply a trademark license.
 15 * Therefore any rights, title and interest in our trademarks remain
 16 * entirely with us.
 17 * 
 18 * However, if you propagate an unmodified version of the Program you are
 19 * allowed to use the term "Zarafa" to indicate that you distribute the
 20 * Program. Furthermore you may use our trademarks where it is necessary
 21 * to indicate the intended purpose of a product or service provided you
 22 * use it in accordance with honest practices in industrial or commercial
 23 * matters.  If you want to propagate modified versions of the Program
 24 * under the name "Zarafa" or "Zarafa Server", you may only do so if you
 25 * have a written permission by Zarafa B.V. (to acquire a permission
 26 * please contact Zarafa at trademark@zarafa.com).
 27 * 
 28 * The interactive user interface of the software displays an attribution
 29 * notice containing the term "Zarafa" and/or the logo of Zarafa.
 30 * Interactive user interfaces of unmodified and modified versions must
 31 * display Appropriate Legal Notices according to sec. 5 of the GNU
 32 * Affero General Public License, version 3, when you propagate
 33 * unmodified or modified versions of the Program. In accordance with
 34 * sec. 7 b) of the GNU Affero General Public License, version 3, these
 35 * Appropriate Legal Notices must retain the logo of Zarafa or display
 36 * the words "Initial Development by Zarafa" if the display of the logo
 37 * is not reasonably feasible for technical reasons. The use of the logo
 38 * of Zarafa in Legal Notices is allowed for unmodified and modified
 39 * versions of the software.
 40 * 
 41 * This program is distributed in the hope that it will be useful,
 42 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 44 * GNU Affero General Public License for more details.
 45 *  
 46 * You should have received a copy of the GNU Affero General Public License
 47 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 48 * 
 49 */
 50
 51?>
 52<?PHP
 53/* vim: set expandtab tabstop=4 shiftwidth=4: */
 54// +----------------------------------------------------------------------+
 55// | PHP Version 4                                                        |
 56// +----------------------------------------------------------------------+
 57// | Copyright (c) 1997-2002 The PHP Group                                |
 58// +----------------------------------------------------------------------+
 59// | This source file is subject to version 2.0 of the PHP license,       |
 60// | that is bundled with this package in the file LICENSE, and is        |
 61// | available at through the world-wide-web at                           |
 62// | http://www.php.net/license/2_02.txt.                                 |
 63// | If you did not receive a copy of the PHP license and are unable to   |
 64// | obtain it through the world-wide-web, please send a note to          |
 65// | license@php.net so we can mail you a copy immediately.               |
 66// +----------------------------------------------------------------------+
 67// | Authors: Stephan Schmidt <schst@php-tools.net>                       |
 68// +----------------------------------------------------------------------+
 69//
 70//    $Id: Util.php,v 1.24 2004/12/23 13:21:59 schst Exp $
 71
 72/**
 73 * error code for invalid chars in XML name
 74 */
 75define("XML_UTIL_ERROR_INVALID_CHARS", 51);
 76
 77/**
 78 * error code for invalid chars in XML name
 79 */
 80define("XML_UTIL_ERROR_INVALID_START", 52);
 81
 82/**
 83 * error code for non-scalar tag content
 84 */
 85define("XML_UTIL_ERROR_NON_SCALAR_CONTENT", 60);
 86    
 87/**
 88 * error code for missing tag name
 89 */
 90define("XML_UTIL_ERROR_NO_TAG_NAME", 61);
 91    
 92/**
 93 * replace XML entities
 94 */
 95define("XML_UTIL_REPLACE_ENTITIES", 1);
 96
 97/**
 98 * embedd content in a CData Section
 99 */
100define("XML_UTIL_CDATA_SECTION", 5);
101
102/**
103 * do not replace entitites
104 */
105define("XML_UTIL_ENTITIES_NONE", 0);
106
107/**
108 * replace all XML entitites
109 * This setting will replace <, >, ", ' and &
110 */
111define("XML_UTIL_ENTITIES_XML", 1);
112
113/**
114 * replace only required XML entitites
115 * This setting will replace <, " and &
116 */
117define("XML_UTIL_ENTITIES_XML_REQUIRED", 2);
118
119/**
120 * replace HTML entitites
121 * @link    http://www.php.net/htmlentities
122 */
123define("XML_UTIL_ENTITIES_HTML", 3);
124
125/**
126 * Collapse all empty tags.
127 */
128define("XML_UTIL_COLLAPSE_ALL", 1);
129
130/**
131 * Collapse only empty XHTML tags that have no end tag.
132 */
133define("XML_UTIL_COLLAPSE_XHTML_ONLY", 2);
134
135/**
136 * utility class for working with XML documents
137 *
138 * @category XML
139 * @package  XML_Util
140 * @version  1.1.0
141 * @author   Stephan Schmidt <schst@php.net>
142 */
143class XML_Util {
144
145   /**
146    * return API version
147    *
148    * @access   public
149    * @static
150    * @return   string  $version API version
151    */
152    function apiVersion()
153    {
154        return '1.1';
155    }
156
157   /**
158    * replace XML entities
159    *
160    * With the optional second parameter, you may select, which
161    * entities should be replaced.
162    *
163    * <code>
164    * require_once 'XML/Util.php';
165    * 
166    * // replace XML entites:
167    * $string = XML_Util::replaceEntities("This string contains < & >.");
168    * </code>
169    *
170    * @access   public
171    * @static
172    * @param    string  string where XML special chars should be replaced
173    * @param    integer setting for entities in attribute values (one of XML_UTIL_ENTITIES_XML, XML_UTIL_ENTITIES_XML_REQUIRED, XML_UTIL_ENTITIES_HTML)
174    * @return   string  string with replaced chars
175    * @see      reverseEntities()
176    */
177    function replaceEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML)
178    {
179        switch ($replaceEntities) {
180            case XML_UTIL_ENTITIES_XML:
181                return strtr($string,array(
182                                          '&'  => '&amp;',
183                                          '>'  => '&gt;',
184                                          '<'  => '&lt;',
185                                          '"'  => '&quot;',
186                                          '\'' => '&apos;' ));
187                break;
188            case XML_UTIL_ENTITIES_XML_REQUIRED:
189                return strtr($string,array(
190                                          '&'  => '&amp;',
191                                          '<'  => '&lt;',
192                                          '"'  => '&quot;' ));
193                break;
194            case XML_UTIL_ENTITIES_HTML:
195                return htmlentities($string);
196                break;
197        }
198        return $string;
199    }
200
201   /**
202    * reverse XML entities
203    *
204    * With the optional second parameter, you may select, which
205    * entities should be reversed.
206    *
207    * <code>
208    * require_once 'XML/Util.php';
209    * 
210    * // reverse XML entites:
211    * $string = XML_Util::reverseEntities("This string contains &lt; &amp; &gt;.");
212    * </code>
213    *
214    * @access   public
215    * @static
216    * @param    string  string where XML special chars should be replaced
217    * @param    integer setting for entities in attribute values (one of XML_UTIL_ENTITIES_XML, XML_UTIL_ENTITIES_XML_REQUIRED, XML_UTIL_ENTITIES_HTML)
218    * @return   string  string with replaced chars
219    * @see      replaceEntities()
220    */
221    function reverseEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML)
222    {
223        switch ($replaceEntities) {
224            case XML_UTIL_ENTITIES_XML:
225                return strtr($string,array(
226                                          '&amp;'  => '&',
227                                          '&gt;'   => '>',
228                                          '&lt;'   => '<',
229                                          '&quot;' => '"',
230                                          '&apos;' => '\'' ));
231                break;
232            case XML_UTIL_ENTITIES_XML_REQUIRED:
233                return strtr($string,array(
234                                          '&amp;'  => '&',
235                                          '&lt;'   => '<',
236                                          '&quot;' => '"' ));
237                break;
238            case XML_UTIL_ENTITIES_HTML:
239                $arr = array_flip(get_html_translation_table(HTML_ENTITIES));
240                return strtr($string, $arr);
241                break;
242        }
243        return $string;
244    }
245
246   /**
247    * build an xml declaration
248    *
249    * <code>
250    * require_once 'XML/Util.php';
251    * 
252    * // get an XML declaration:
253    * $xmlDecl = XML_Util::getXMLDeclaration("1.0", "UTF-8", true);
254    * </code>
255    *
256    * @access   public
257    * @static
258    * @param    string  $version     xml version
259    * @param    string  $encoding    character encoding
260    * @param    boolean $standAlone  document is standalone (or not)
261    * @return   string  $decl xml declaration
262    * @uses     XML_Util::attributesToString() to serialize the attributes of the XML declaration
263    */
264    function getXMLDeclaration($version = "1.0", $encoding = null, $standalone = null)
265    {
266        $attributes = array(
267                            "version" => $version,
268                           );
269        // add encoding
270        if ($encoding !== null) {
271            $attributes["encoding"] = $encoding;
272        }
273        // add standalone, if specified
274        if ($standalone !== null) {
275            $attributes["standalone"] = $standalone ? "yes" : "no";
276        }
277        
278        return sprintf("<?xml%s?>", XML_Util::attributesToString($attributes, false));
279    }
280
281   /**
282    * build a document type declaration
283    *
284    * <code>
285    * require_once 'XML/Util.php';
286    * 
287    * // get a doctype declaration:
288    * $xmlDecl = XML_Util::getDocTypeDeclaration("rootTag","myDocType.dtd");
289    * </code>
290    *
291    * @access   public
292    * @static
293    * @param    string  $root         name of the root tag
294    * @param    string  $uri          uri of the doctype definition (or array with uri and public id)
295    * @param    string  $internalDtd  internal dtd entries   
296    * @return   string  $decl         doctype declaration
297    * @since    0.2
298    */
299    function getDocTypeDeclaration($root, $uri = null, $internalDtd = null)
300    {
301        if (is_array($uri)) {
302            $ref = sprintf( ' PUBLIC "%s" "%s"', $uri["id"], $uri["uri"] );
303        } elseif (!empty($uri)) {
304            $ref = sprintf( ' SYSTEM "%s"', $uri );
305        } else {
306            $ref = "";
307        }
308
309        if (empty($internalDtd)) {
310            return sprintf("<!DOCTYPE %s%s>", $root, $ref);
311        } else {
312            return sprintf("<!DOCTYPE %s%s [\n%s\n]>", $root, $ref, $internalDtd);
313        }
314    }
315
316   /**
317    * create string representation of an attribute list
318    *
319    * <code>
320    * require_once 'XML/Util.php';
321    * 
322    * // build an attribute string
323    * $att = array(
324    *              "foo"   =>  "bar",
325    *              "argh"  =>  "tomato"
326    *            );
327    *
328    * $attList = XML_Util::attributesToString($att);    
329    * </code>
330    *
331    * @access   public
332    * @static
333    * @param    array         $attributes        attribute array
334    * @param    boolean|array $sort              sort attribute list alphabetically, may also be an assoc array containing the keys 'sort', 'multiline', 'indent', 'linebreak' and 'entities'
335    * @param    boolean       $multiline         use linebreaks, if more than one attribute is given
336    * @param    string        $indent            string used for indentation of multiline attributes
337    * @param    string        $linebreak         string used for linebreaks of multiline attributes
338    * @param    integer       $entities          setting for entities in attribute values (one of XML_UTIL_ENTITIES_NONE, XML_UTIL_ENTITIES_XML, XML_UTIL_ENTITIES_XML_REQUIRED, XML_UTIL_ENTITIES_HTML)
339    * @return   string                           string representation of the attributes
340    * @uses     XML_Util::replaceEntities() to replace XML entities in attribute values
341    * @todo     allow sort also to be an options array
342    */
343    function attributesToString($attributes, $sort = true, $multiline = false, $indent = '    ', $linebreak = "\n", $entities = XML_UTIL_ENTITIES_XML)
344    {
345        /**
346         * second parameter may be an array
347         */
348        if (is_array($sort)) {
349            if (isset($sort['multiline'])) {
350                $multiline = $sort['multiline'];
351            }
352            if (isset($sort['indent'])) {
353                $indent = $sort['indent'];
354            }
355            if (isset($sort['linebreak'])) {
356                $multiline = $sort['linebreak'];
357            }
358            if (isset($sort['entities'])) {
359                $entities = $sort['entities'];
360            }
361            if (isset($sort['sort'])) {
362                $sort = $sort['sort'];
363            } else {
364                $sort = true;
365            }
366        }
367        $string = '';
368        if (is_array($attributes) && !empty($attributes)) {
369            if ($sort) {
370                ksort($attributes);
371            }
372            if( !$multiline || count($attributes) == 1) {
373                foreach ($attributes as $key => $value) {
374                    if ($entities != XML_UTIL_ENTITIES_NONE) {
375                        if ($entities === XML_UTIL_CDATA_SECTION) {
376                        	$entities = XML_UTIL_ENTITIES_XML;
377                        }
378                        $value = XML_Util::replaceEntities($value, $entities);
379                    }
380                    $string .= ' '.$key.'="'.$value.'"';
381                }
382            } else {
383                $first = true;
384                foreach ($attributes as $key => $value) {
385                    if ($entities != XML_UTIL_ENTITIES_NONE) {
386                        $value = XML_Util::replaceEntities($value, $entities);
387                    }
388                    if ($first) {
389                        $string .= " ".$key.'="'.$value.'"';
390                        $first = false;
391                    } else {
392                        $string .= $linebreak.$indent.$key.'="'.$value.'"';
393                    }
394                }
395            }
396        }
397        return $string;
398    }
399
400   /**
401    * Collapses empty tags.
402    *
403    * @access   public
404    * @static
405    * @param    string  $xml  XML
406    * @param    integer $mode Whether to collapse all empty tags (XML_UTIL_COLLAPSE_ALL) or only XHTML (XML_UTIL_COLLAPSE_XHTML_ONLY) ones.
407    * @return   string  $xml  XML
408    */
409    function collapseEmptyTags($xml, $mode = XML_UTIL_COLLAPSE_ALL) {
410        if ($mode == XML_UTIL_COLLAPSE_XHTML_ONLY) {
411            return preg_replace(
412              '/<(area|base|br|col|hr|img|input|link|meta|param)([^>]*)><\/\\1>/s',
413              '<\\1\\2 />',
414              $xml
415            );
416        } else {
417            return preg_replace(
418              '/<(\w+)([^>]*)><\/\\1>/s',
419              '<\\1\\2 />',
420              $xml
421            );
422        }
423    }
424
425   /**
426    * create a tag
427    *
428    * This method will call XML_Util::createTagFromArray(), which
429    * is more flexible.
430    *
431    * <code>
432    * require_once 'XML/Util.php';
433    * 
434    * // create an XML tag:
435    * $tag = XML_Util::createTag("myNs:myTag", array("foo" => "bar"), "This is inside the tag", "http://www.w3c.org/myNs#");
436    * </code>
437    *
438    * @access   public
439    * @static
440    * @param    string  $qname             qualified tagname (including namespace)
441    * @param    array   $attributes        array containg attributes
442    * @param    mixed   $content
443    * @param    string  $namespaceUri      URI of the namespace
444    * @param    integer $replaceEntities   whether to replace XML special chars in content, embedd it in a CData section or none of both
445    * @param    boolean $multiline         whether to create a multiline tag where each attribute gets written to a single line
446    * @param    string  $indent            string used to indent attributes (_auto indents attributes so they start at the same column)
447    * @param    string  $linebreak         string used for linebreaks
448    * @return   string  $string            XML tag
449    * @see      XML_Util::createTagFromArray()
450    * @uses     XML_Util::createTagFromArray() to create the tag
451    */
452    function createTag($qname, $attributes = array(), $content = null, $namespaceUri = null, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, $multiline = false, $indent = "_auto", $linebreak = "\n")
453    {
454        $tag = array(
455                     "qname"      => $qname,
456                     "attributes" => $attributes
457                    );
458
459        // add tag content
460        if ($content !== null) {
461            $tag["content"] = $content;
462        }
463        
464        // add namespace Uri
465        if ($namespaceUri !== null) {
466            $tag["namespaceUri"] = $namespaceUri;
467        }
468
469        return XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $linebreak);
470    }
471
472   /**
473    * create a tag from an array
474    * this method awaits an array in the following format
475    * <pre>
476    * array(
477    *  "qname"        => $qname         // qualified name of the tag
478    *  "namespace"    => $namespace     // namespace prefix (optional, if qname is specified or no namespace)
479    *  "localpart"    => $localpart,    // local part of the tagname (optional, if qname is specified)
480    *  "attributes"   => array(),       // array containing all attributes (optional)
481    *  "content"      => $content,      // tag content (optional)
482    *  "namespaceUri" => $namespaceUri  // namespaceUri for the given namespace (optional)
483    *   )
484    * </pre>
485    *
486    * <code>
487    * require_once 'XML/Util.php';
488    * 
489    * $tag = array(
490    *           "qname"        => "foo:bar",
491    *           "namespaceUri" => "http://foo.com",
492    *           "attributes"   => array( "key" => "value", "argh" => "fruit&vegetable" ),
493    *           "content"      => "I'm inside the tag",
494    *            );
495    * // creating a tag with qualified name and namespaceUri
496    * $string = XML_Util::createTagFromArray($tag);
497    * </code>
498    *
499    * @access   public
500    * @static
501    * @param    array   $tag               tag definition
502    * @param    integer $replaceEntities   whether to replace XML special chars in content, embedd it in a CData section or none of both
503    * @param    boolean $multiline         whether to create a multiline tag where each attribute gets written to a single line
504    * @param    string  $indent            string used to indent attributes (_auto indents attributes so they start at the same column)
505    * @param    string  $linebreak         string used for linebreaks
506    * @return   string  $string            XML tag
507    * @see      XML_Util::createTag()
508    * @uses     XML_Util::attributesToString() to serialize the attributes of the tag
509    * @uses     XML_Util::splitQualifiedName() to get local part and namespace of a qualified name
510    */
511    function createTagFromArray($tag, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, $multiline = false, $indent = "_auto", $linebreak = "\n" )
512    {
513        if (isset($tag['content']) && !is_scalar($tag['content'])) {
514            return XML_Util::raiseError( 'Supplied non-scalar value as tag content', XML_UTIL_ERROR_NON_SCALAR_CONTENT );
515        }
516
517        if (!isset($tag['qname']) && !isset($tag['localPart'])) {
518            return XML_Util::raiseError( 'You must either supply a qualified name (qname) or local tag name (localPart).', XML_UTIL_ERROR_NO_TAG_NAME );
519        }
520
521        // if no attributes hav been set, use empty attributes
522        if (!isset($tag["attributes"]) || !is_array($tag["attributes"])) {
523            $tag["attributes"] = array();
524        }
525        
526        // qualified name is not given
527        if (!isset($tag["qname"])) {
528            // check for namespace
529            if (isset($tag["namespace"]) && !empty($tag["namespace"])) {
530                $tag["qname"] = $tag["namespace"].":".$tag["localPart"];
531            } else {
532                $tag["qname"] = $tag["localPart"];
533            }
534        // namespace URI is set, but no namespace
535        } elseif (isset($tag["namespaceUri"]) && !isset($tag["namespace"])) {
536            $parts = XML_Util::splitQualifiedName($tag["qname"]);
537            $tag["localPart"] = $parts["localPart"];
538            if (isset($parts["namespace"])) {
539                $tag["namespace"] = $parts["namespace"];
540            }
541        }
542
543        if (isset($tag["namespaceUri"]) && !empty($tag["namespaceUri"])) {
544            // is a namespace given
545            if (isset($tag["namespace"]) && !empty($tag["namespace"])) {
546                $tag["attributes"]["xmlns:".$tag["namespace"]] = $tag["namespaceUri"];
547            } else {
548                // define this Uri as the default namespace
549                $tag["attributes"]["xmlns"] = $tag["namespaceUri"];
550            }
551        }
552
553        // check for multiline attributes
554        if ($multiline === true) {
555            if ($indent === "_auto") {
556                $indent = str_repeat(" ", (strlen($tag["qname"])+2));
557            }
558        }
559        
560        // create attribute list
561        $attList    =   XML_Util::attributesToString($tag['attributes'], true, $multiline, $indent, $linebreak, $replaceEntities );
562        if (!isset($tag['content']) || (string)$tag['content'] == '') {
563            $tag    =   sprintf('<%s%s />', $tag['qname'], $attList);
564        } else {
565            switch ($replaceEntities) {
566                case XML_UTIL_ENTITIES_NONE:
567                    break;
568                case XML_UTIL_CDATA_SECTION:
569                    $tag['content'] = XML_Util::createCDataSection($tag['content']);
570                    break;
571                default:
572                    $tag['content'] = XML_Util::replaceEntities($tag['content'], $replaceEntities);
573                    break;
574            }
575            $tag    =   sprintf('<%s%s>%s</%s>', $tag['qname'], $attList, $tag['content'], $tag['qname'] );
576        }        
577        return  $tag;
578    }
579
580   /**
581    * create a start element
582    *
583    * <code>
584    * require_once 'XML/Util.php';
585    * 
586    * // create an XML start element:
587    * $tag = XML_Util::createStartElement("myNs:myTag", array("foo" => "bar") ,"http://www.w3c.org/myNs#");
588    * </code>
589    *
590    * @access   public
591    * @static
592    * @param    string  $qname             qualified tagname (including namespace)
593    * @param    array   $attributes        array containg attributes
594    * @param    string  $namespaceUri      URI of the namespace
595    * @param    boolean $multiline         whether to create a multiline tag where each attribute gets written to a single line
596    * @param    string  $indent            string used to indent attributes (_auto indents attributes so they start at the same column)
597    * @param    string  $linebreak         string used for linebreaks
598    * @return   string  $string            XML start element
599    * @see      XML_Util::createEndElement(), XML_Util::createTag()
600    */
601    function createStartElement($qname, $attributes = array(), $namespaceUri = null, $multiline = false, $indent = '_auto', $linebreak = "\n")
602    {
603        // if no attributes hav been set, use empty attributes
604        if (!isset($attributes) || !is_array($attributes)) {
605            $attributes = array();
606        }
607        
608        if ($namespaceUri != null) {
609            $parts = XML_Util::splitQualifiedName($qname);
610        }
611
612        // check for multiline attributes
613        if ($multiline === true) {
614            if ($indent === "_auto") {
615                $indent = str_repeat(" ", (strlen($qname)+2));
616            }
617        }
618
619        if ($namespaceUri != null) {
620            // is a namespace given
621            if (isset($parts["namespace"]) && !empty($parts["namespace"])) {
622                $attributes["xmlns:".$parts["namespace"]] = $namespaceUri;
623            } else {
624                // define this Uri as the default namespace
625                $attributes["xmlns"] = $namespaceUri;
626            }
627        }
628
629        // create attribute list
630        $attList    =   XML_Util::attributesToString($attributes, true, $multiline, $indent, $linebreak);
631        $element    =   sprintf("<%s%s>", $qname, $attList);
632        return  $element;
633    }
634
635   /**
636    * create an end element
637    *
638    * <code>
639    * require_once 'XML/Util.php';
640    * 
641    * // create an XML start element:
642    * $tag = XML_Util::createEndElement("myNs:myTag");
643    * </code>
644    *
645    * @access   public
646    * @static
647    * @param    string  $qname             qualified tagname (including namespace)
648    * @return   string  $string            XML end element
649    * @see      XML_Util::createStartElement(), XML_Util::createTag()
650    */
651    function createEndElement($qname)
652    {
653        $element    =   sprintf("</%s>", $qname);
654        return  $element;
655    }
656    
657   /**
658    * create an XML comment
659    *
660    * <code>
661    * require_once 'XML/Util.php';
662    * 
663    * // create an XML start element:
664    * $tag = XML_Util::createComment("I am a comment");
665    * </code>
666    *
667    * @access   public
668    * @static
669    * @param    string  $content           content of the comment
670    * @return   string  $comment           XML comment
671    */
672    function createComment($content)
673    {
674        $comment    =   sprintf("<!-- %s -->", $content);
675        return  $comment;
676    }
677    
678   /**
679    * create a CData section
680    *
681    * <code>
682    * require_once 'XML/Util.php';
683    * 
684    * // create a CData section
685    * $tag = XML_Util::createCDataSection("I am content.");
686    * </code>
687    *
688    * @access   public
689    * @static
690    * @param    string  $data              data of the CData section
691    * @return   string  $string            CData section with content
692    */
693    function createCDataSection($data)
694    {
695        return  sprintf("<![CDATA[%s]]>", $data);
696    }
697
698   /**
699    * split qualified name and return namespace and local part
700    *
701    * <code>
702    * require_once 'XML/Util.php';
703    * 
704    * // split qualified tag
705    * $parts = XML_Util::splitQualifiedName("xslt:stylesheet");
706    * </code>
707    * the returned array will contain two elements:
708    * <pre>
709    * array(
710    *       "namespace" => "xslt",
711    *       "localPart" => "stylesheet"
712    *      );
713    * </pre>
714    *
715    * @access public
716    * @static
717    * @param  string    $qname      qualified tag name
718    * @param  string    $defaultNs  default namespace (optional)
719    * @return array     $parts      array containing namespace and local part
720    */
721    function splitQualifiedName($qname, $defaultNs = null)
722    {
723        if (strstr($qname, ':')) {
724            $tmp = explode(":", $qname);
725            return array(
726                          "namespace" => $tmp[0],
727                          "localPart" => $tmp[1]
728                        );
729        }
730        return array(
731                      "namespace" => $defaultNs,
732                      "localPart" => $qname
733                    );
734    }
735
736   /**
737    * check, whether string is valid XML name
738    *
739    * <p>XML names are used for tagname, attribute names and various
740    * other, lesser known entities.</p>
741    * <p>An XML name may only consist of alphanumeric characters,
742    * dashes, undescores and periods, and has to start with a letter
743    * or an underscore.
744    * </p>
745    *
746    * <code>
747    * require_once 'XML/Util.php';
748    * 
749    * // verify tag name
750    * $result = XML_Util::isValidName("invalidTag?");
751    * if (XML_Util::isError($result)) {
752    *    print "Invalid XML name: " . $result->getMessage();
753    * }
754    * </code>
755    *
756    * @access  public
757    * @static
758    * @param   string  $string string that should be checked
759    * @return  mixed   $valid  true, if string is a valid XML name, PEAR error otherwise
760    * @todo    support for other charsets
761    */
762    function isValidName($string)
763    {
764        // check for invalid chars
765        if (!preg_match("/^[[:alnum:]_\-.]$/", $string{0})) {
766            return XML_Util::raiseError( "XML names may only start with letter or underscore", XML_UTIL_ERROR_INVALID_START );
767        }
768        
769        // check for invalid chars
770        if (!preg_match("/^([a-zA-Z_]([a-zA-Z0-9_\-\.]*)?:)?[a-zA-Z_]([a-zA-Z0-9_\-\.]+)?$/", $string)) {
771            return XML_Util::raiseError( "XML names may only contain alphanumeric chars, period, hyphen, colon and underscores", XML_UTIL_ERROR_INVALID_CHARS );
772         }
773        // XML name is valid
774        return true;
775    }
776
777   /**
778    * replacement for XML_Util::raiseError
779    *
780    * Avoids the necessity to always require
781    * PEAR.php
782    *
783    * @access   public
784    * @param    string      error message
785    * @param    integer     error code
786    * @return   object PEAR_Error
787    */
788    function raiseError($msg, $code)
789    {
790        require_once 'PEAR.php';
791        return PEAR::raiseError($msg, $code);
792    }
793}
794?>