PageRenderTime 391ms CodeModel.GetById 161ms app.highlight 83ms RepoModel.GetById 138ms app.codeStats 0ms

/dependencies/include/XML/Util.php

https://github.com/AaronZhangL/Newscoop
PHP | 911 lines | 327 code | 51 blank | 533 comment | 73 complexity | 9bb265dafaaf06c86ca5c48f50368ded MD5 | raw file
  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 */
 53define('XML_UTIL_ERROR_INVALID_CHARS', 51);
 54
 55/**
 56 * error code for invalid chars in XML name
 57 */
 58define('XML_UTIL_ERROR_INVALID_START', 52);
 59
 60/**
 61 * error code for non-scalar tag content
 62 */
 63define('XML_UTIL_ERROR_NON_SCALAR_CONTENT', 60);
 64
 65/**
 66 * error code for missing tag name
 67 */
 68define('XML_UTIL_ERROR_NO_TAG_NAME', 61);
 69
 70/**
 71 * replace XML entities
 72 */
 73define('XML_UTIL_REPLACE_ENTITIES', 1);
 74
 75/**
 76 * embedd content in a CData Section
 77 */
 78define('XML_UTIL_CDATA_SECTION', 5);
 79
 80/**
 81 * do not replace entitites
 82 */
 83define('XML_UTIL_ENTITIES_NONE', 0);
 84
 85/**
 86 * replace all XML entitites
 87 * This setting will replace <, >, ", ' and &
 88 */
 89define('XML_UTIL_ENTITIES_XML', 1);
 90
 91/**
 92 * replace only required XML entitites
 93 * This setting will replace <, " and &
 94 */
 95define('XML_UTIL_ENTITIES_XML_REQUIRED', 2);
 96
 97/**
 98 * replace HTML entitites
 99 * @link http://www.php.net/htmlentities
100 */
101define('XML_UTIL_ENTITIES_HTML', 3);
102
103/**
104 * Collapse all empty tags.
105 */
106define('XML_UTIL_COLLAPSE_ALL', 1);
107
108/**
109 * Collapse only empty XHTML tags that have no end tag.
110 */
111define('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 */
125class 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?>