Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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
?>