Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?PHP
2
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
 
4
/**
5
 * XML_Serializer
6
 *
7
 * Creates XML documents from PHP data structures like arrays, objects or scalars.
8
 *
9
 * PHP versions 4 and 5
10
 *
11
 * LICENSE:
12
 *
13
 * Copyright (c) 2003-2008 Stephan Schmidt <schst@php.net>
14
 * All rights reserved.
15
 *
16
 * Redistribution and use in source and binary forms, with or without
17
 * modification, are permitted provided that the following conditions
18
 * are met:
19
 *
20
 *    * Redistributions of source code must retain the above copyright
21
 *      notice, this list of conditions and the following disclaimer.
22
 *    * Redistributions in binary form must reproduce the above copyright
23
 *      notice, this list of conditions and the following disclaimer in the
24
 *      documentation and/or other materials provided with the distribution.
25
 *    * The name of the author may not be used to endorse or promote products
26
 *      derived from this software without specific prior written permission.
27
 *
28
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
29
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
30
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
32
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
33
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
34
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
35
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
36
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
37
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
38
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
 *
40
 * @category  XML
41
 * @package   XML_Serializer
42
 * @author    Stephan Schmidt <schst@php.net>
43
 * @copyright 2003-2008 Stephan Schmidt <schst@php.net>
44
 * @license   http://opensource.org/licenses/bsd-license New BSD License
45
 * @version   CVS: $Id: Serializer.php 294967 2010-02-12 03:10:19Z clockwerx $
46
 * @link      http://pear.php.net/package/XML_Serializer
47
 * @see       XML_Unserializer
48
 */
49
 
50
/**
51
 * uses PEAR error management
52
 */
53
require_once 'PEAR.php';
54
 
55
/**
56
 * uses XML_Util to create XML tags
57
 */
58
require_once 'XML/Util.php';
59
 
60
/**
61
 * option: string used for indentation
62
 *
63
 * Possible values:
64
 * - any string (default is any string)
65
 */
66
define('XML_SERIALIZER_OPTION_INDENT', 'indent');
67
 
68
/**
69
 * option: string used for linebreaks
70
 *
71
 * Possible values:
72
 * - any string (default is \n)
73
 */
74
define('XML_SERIALIZER_OPTION_LINEBREAKS', 'linebreak');
75
 
76
/**
77
 * option: enable type hints
78
 *
79
 * Possible values:
80
 * - true
81
 * - false
82
 */
83
define('XML_SERIALIZER_OPTION_TYPEHINTS', 'typeHints');
84
 
85
/**
86
 * option: add an XML declaration
87
 *
88
 * Possible values:
89
 * - true
90
 * - false
91
 */
92
define('XML_SERIALIZER_OPTION_XML_DECL_ENABLED', 'addDecl');
93
 
94
/**
95
 * option: encoding of the document
96
 *
97
 * Possible values:
98
 * - any valid encoding
99
 * - null (default)
100
 */
101
define('XML_SERIALIZER_OPTION_XML_ENCODING', 'encoding');
102
 
103
/**
104
 * option: default name for tags
105
 *
106
 * Possible values:
107
 * - any string (XML_Serializer_Tag is default)
108
 */
109
define('XML_SERIALIZER_OPTION_DEFAULT_TAG', 'defaultTagName');
110
 
111
/**
112
 * option: use classname for objects in indexed arrays
113
 *
114
 * Possible values:
115
 * - true
116
 * - false (default)
117
 */
118
define('XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME', 'classAsTagName');
119
 
120
/**
121
 * option: attribute where original key is stored
122
 *
123
 * Possible values:
124
 * - any string (default is _originalKey)
125
 */
126
define('XML_SERIALIZER_OPTION_ATTRIBUTE_KEY', 'keyAttribute');
127
 
128
/**
129
 * option: attribute for type (only if typeHints => true)
130
 *
131
 * Possible values:
132
 * - any string (default is _type)
133
 */
134
define('XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE', 'typeAttribute');
135
 
136
/**
137
 * option: attribute for class (only if typeHints => true)
138
 *
139
 * Possible values:
140
 * - any string (default is _class)
141
 */
142
define('XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS', 'classAttribute');
143
 
144
/**
145
 * option: scalar values (strings, ints,..) will be serialized as attribute
146
 *
147
 * Possible values:
148
 * - true
149
 * - false (default)
150
 * - array which sets this option on a per-tag basis
151
 */
152
define('XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES', 'scalarAsAttributes');
153
 
154
/**
155
 * option: prepend string for attributes
156
 *
157
 * Possible values:
158
 * - any string (default is any string)
159
 */
160
define('XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES', 'prependAttributes');
161
 
162
/**
163
 * option: indent the attributes, if set to '_auto',
164
 * it will indent attributes so they all start at the same column
165
 *
166
 * Possible values:
167
 * - true
168
 * - false (default)
169
 * - '_auto'
170
 */
171
define('XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES', 'indentAttributes');
172
 
173
/**
174
 * option: use 'simplexml' to use parent name as tagname
175
 * if transforming an indexed array
176
 *
177
 * Possible values:
178
 * - XML_SERIALIZER_MODE_DEFAULT (default)
179
 * - XML_SERIALIZER_MODE_SIMPLEXML
180
 */
181
define('XML_SERIALIZER_OPTION_MODE', 'mode');
182
 
183
/**
184
 * option: add a doctype declaration
185
 *
186
 * Possible values:
187
 * - true
188
 * - false (default)
189
 */
190
define('XML_SERIALIZER_OPTION_DOCTYPE_ENABLED', 'addDoctype');
191
 
192
/**
193
 * option: supply a string or an array with id and uri
194
 * ({@see XML_Util::getDoctypeDeclaration()}
195
 *
196
 * Possible values:
197
 * - string
198
 * - array
199
 */
200
define('XML_SERIALIZER_OPTION_DOCTYPE', 'doctype');
201
 
202
/**
203
 * option: name of the root tag
204
 *
205
 * Possible values:
206
 * - string
207
 * - null (default)
208
 */
209
define('XML_SERIALIZER_OPTION_ROOT_NAME', 'rootName');
210
 
211
/**
212
 * option: attributes of the root tag
213
 *
214
 * Possible values:
215
 * - array
216
 */
217
define('XML_SERIALIZER_OPTION_ROOT_ATTRIBS', 'rootAttributes');
218
 
219
/**
220
 * option: all values in this key will be treated as attributes
221
 *
222
 * Possible values:
223
 * - string
224
 */
225
define('XML_SERIALIZER_OPTION_ATTRIBUTES_KEY', 'attributesArray');
226
 
227
/**
228
 * option: this value will be used directly as content,
229
 * instead of creating a new tag, may only be used
230
 * in conjuction with attributesArray
231
 *
232
 * Possible values:
233
 * - string
234
 * - null (default)
235
 */
236
define('XML_SERIALIZER_OPTION_CONTENT_KEY', 'contentName');
237
 
238
/**
239
 * option: this value will be used in a comment, instead of creating a new tag
240
 *
241
 * Possible values:
242
 * - string
243
 * - null (default)
244
 */
245
define('XML_SERIALIZER_OPTION_COMMENT_KEY', 'commentName');
246
 
247
/**
248
 * option: tag names that will be changed
249
 *
250
 * Possible values:
251
 * - array
252
 */
253
define('XML_SERIALIZER_OPTION_TAGMAP', 'tagMap');
254
 
255
/**
256
 * option: function that will be applied before serializing
257
 *
258
 * Possible values:
259
 * - any valid PHP callback
260
 */
261
define('XML_SERIALIZER_OPTION_ENCODE_FUNC', 'encodeFunction');
262
 
263
/**
264
 * option: namespace to use for the document
265
 *
266
 * Possible values:
267
 * - string
268
 * - null (default)
269
 */
270
define('XML_SERIALIZER_OPTION_NAMESPACE', 'namespace');
271
 
272
/**
273
 * option: type of entities to replace
274
 *
275
 * Possible values:
276
 * - XML_SERIALIZER_ENTITIES_NONE
277
 * - XML_SERIALIZER_ENTITIES_XML (default)
278
 * - XML_SERIALIZER_ENTITIES_XML_REQUIRED
279
 * - XML_SERIALIZER_ENTITIES_HTML
280
 */
281
define('XML_SERIALIZER_OPTION_ENTITIES', 'replaceEntities');
282
 
283
/**
284
 * option: whether to return the result of the serialization from serialize()
285
 *
286
 * Possible values:
287
 * - true
288
 * - false (default)
289
 */
290
define('XML_SERIALIZER_OPTION_RETURN_RESULT', 'returnResult');
291
 
292
/**
293
 * option: whether to ignore properties that are set to null
294
 *
295
 * Possible values:
296
 * - true
297
 * - false (default)
298
 */
299
define('XML_SERIALIZER_OPTION_IGNORE_NULL', 'ignoreNull');
300
 
301
/**
302
 * option: whether to use cdata sections for character data
303
 *
304
 * Possible values:
305
 * - true
306
 * - false (default)
307
 */
308
define('XML_SERIALIZER_OPTION_CDATA_SECTIONS', 'cdata');
309
 
310
/**
311
 * option: whether a boolean FALSE value should become a string
312
 *
313
 * Possible values:
314
 * - true
315
 * - false (default)
316
 *
317
 * @since 0.20.0
318
 */
319
define('XML_SERIALIZER_OPTION_FALSE_AS_STRING', 'falseAsString');
320
 
321
/**
322
 * default mode
323
 */
324
define('XML_SERIALIZER_MODE_DEFAULT', 'default');
325
 
326
/**
327
 * SimpleXML mode
328
 *
329
 * When serializing indexed arrays, the key of the parent value is used as a tagname.
330
 */
331
define('XML_SERIALIZER_MODE_SIMPLEXML', 'simplexml');
332
 
333
/**
334
 * error code for no serialization done
335
 */
336
define('XML_SERIALIZER_ERROR_NO_SERIALIZATION', 51);
337
 
338
/**
339
 * do not replace entitites
340
 */
341
define('XML_SERIALIZER_ENTITIES_NONE', XML_UTIL_ENTITIES_NONE);
342
 
343
/**
344
 * replace all XML entitites
345
 * This setting will replace <, >, ", ' and &
346
 */
347
define('XML_SERIALIZER_ENTITIES_XML', XML_UTIL_ENTITIES_XML);
348
 
349
/**
350
 * replace only required XML entitites
351
 * This setting will replace <, " and &
352
 */
353
define('XML_SERIALIZER_ENTITIES_XML_REQUIRED', XML_UTIL_ENTITIES_XML_REQUIRED);
354
 
355
/**
356
 * replace HTML entitites
357
 * @link    http://www.php.net/htmlentities
358
 */
359
define('XML_SERIALIZER_ENTITIES_HTML', XML_UTIL_ENTITIES_HTML);
360
 
361
/**
362
 * Creates XML documents from PHP data structures like arrays, objects or scalars.
363
 *
364
 * This class can be used in two modes:
365
 *
366
 *  1. Create an XML document from an array or object that is processed by other
367
 *    applications. That means you can create an RDF document from an array in the
368
 *    following format:
369
 *    <code>
370
 *    $data = array(
371
 *        'channel' => array(
372
 *            'title' => 'Example RDF channel',
373
 *            'link'  => 'http://www.php-tools.de',
374
 *            'image' => array(
375
 *                'title' => 'Example image',
376
 *                'url'   => 'http://www.php-tools.de/image.gif',
377
 *                'link'  => 'http://www.php-tools.de'
378
 *            ),
379
 *            array(
380
 *                'title' => 'Example item',
381
 *                'link'  => 'http://example.com'
382
 *            ),
383
 *            array(
384
 *                'title' => 'Another Example item',
385
 *                'link'  => 'http://example.org'
386
 *            )
387
 *        )
388
 *    );
389
 *    </code>
390
 *
391
 *    To create an RDF document from this array, do the following:
392
 *
393
 *    <code>
394
 *    require_once 'XML/Serializer.php';
395
 *    $options = array(
396
 *       XML_SERIALIZER_OPTION_INDENT      => "\t",     // indent with tabs
397
 *       XML_SERIALIZER_OPTION_LINEBREAKS  => "\n",     // use UNIX line breaks
398
 *       XML_SERIALIZER_OPTION_ROOT_NAME   => 'rdf:RDF',// root tag
399
 *       XML_SERIALIZER_OPTION_DEFAULT_TAG => 'item'    // tag for values
400
 *                                                      // with numeric keys
401
 *    );
402
 *    $serializer = new XML_Serializer($options);
403
 *    $rdf        = $serializer->serialize($data);
404
 *    </code>
405
 *
406
 *    You will get a complete XML document that can be processed like any RDF document.
407
 *
408
 * 2. This class can be used to serialize any data structure in a way that it can
409
 *    later be unserialized again.  XML_Serializer will store the type of the value
410
 *    and additional meta information in attributes of the surrounding tag. This
411
 *    meta information can later be used to restore the original data structure
412
 *    in PHP. If you want XML_Serializer to add meta information to the tags, add
413
 *    <code>
414
 *      XML_SERIALIZER_OPTION_TYPEHINTS => true
415
 *    </code>
416
 *    to the options array in the constructor.
417
 *
418
 * @category  XML
419
 * @package   XML_Serializer
420
 * @author    Stephan Schmidt <schst@php.net>
421
 * @copyright 2003-2008 Stephan Schmidt <schst@php.net>
422
 * @license   http://opensource.org/licenses/bsd-license New BSD License
423
 * @version   Release: @package_version@
424
 * @link      http://pear.php.net/package/XML_Serializer
425
 * @see       XML_Unserializer
426
 * @todo      replace extending PEAR class with instead using a PEAR_Error object
427
 */
428
class XML_Serializer extends PEAR
429
{
430
    /**
431
     * List of all available options
432
     *
433
     * @access private
434
     * @var    array
435
     */
436
    var $_knownOptions = array(
437
        XML_SERIALIZER_OPTION_INDENT,
438
        XML_SERIALIZER_OPTION_LINEBREAKS,
439
        XML_SERIALIZER_OPTION_TYPEHINTS,
440
        XML_SERIALIZER_OPTION_XML_DECL_ENABLED,
441
        XML_SERIALIZER_OPTION_XML_ENCODING,
442
        XML_SERIALIZER_OPTION_DEFAULT_TAG,
443
        XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME,
444
        XML_SERIALIZER_OPTION_ATTRIBUTE_KEY,
445
        XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE,
446
        XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS,
447
        XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES,
448
        XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES,
449
        XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES,
450
        XML_SERIALIZER_OPTION_MODE,
451
        XML_SERIALIZER_OPTION_DOCTYPE_ENABLED,
452
        XML_SERIALIZER_OPTION_DOCTYPE,
453
        XML_SERIALIZER_OPTION_ROOT_NAME,
454
        XML_SERIALIZER_OPTION_ROOT_ATTRIBS,
455
        XML_SERIALIZER_OPTION_ATTRIBUTES_KEY,
456
        XML_SERIALIZER_OPTION_CONTENT_KEY,
457
        XML_SERIALIZER_OPTION_COMMENT_KEY,
458
        XML_SERIALIZER_OPTION_TAGMAP,
459
        XML_SERIALIZER_OPTION_ENCODE_FUNC,
460
        XML_SERIALIZER_OPTION_NAMESPACE,
461
        XML_SERIALIZER_OPTION_ENTITIES,
462
        XML_SERIALIZER_OPTION_RETURN_RESULT,
463
        XML_SERIALIZER_OPTION_IGNORE_NULL,
464
        XML_SERIALIZER_OPTION_CDATA_SECTIONS,
465
    );
466
 
467
    /**
468
     * Default options for the serialization
469
     *
470
     * @access private
471
     * @var    array
472
     */
473
    var $_defaultOptions = array(
474
 
475
        // string used for indentation
476
        XML_SERIALIZER_OPTION_INDENT => '',
477
 
478
        // string used for newlines
479
        XML_SERIALIZER_OPTION_LINEBREAKS => "\n",
480
 
481
        // automatically add type hin attributes
482
        XML_SERIALIZER_OPTION_TYPEHINTS => false,
483
 
484
        // add an XML declaration
485
        XML_SERIALIZER_OPTION_XML_DECL_ENABLED => false,
486
 
487
        // encoding specified in the XML declaration
488
        XML_SERIALIZER_OPTION_XML_ENCODING => null,
489
 
490
        // tag used for indexed arrays or invalid names
491
        XML_SERIALIZER_OPTION_DEFAULT_TAG => 'XML_Serializer_Tag',
492
 
493
        // use classname for objects in indexed arrays
494
        XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME => false,
495
 
496
        // attribute where original key is stored
497
        XML_SERIALIZER_OPTION_ATTRIBUTE_KEY => '_originalKey',
498
 
499
        // attribute for type (only if typeHints => true)
500
        XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE => '_type',
501
 
502
        // attribute for class of objects (only if typeHints => true)
503
        XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS => '_class',
504
 
505
        // scalar values (strings, ints,..) will be serialized as attribute
506
        XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES => false,
507
 
508
        // prepend string for attributes
509
        XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES => '',
510
 
511
        // indent the attributes, if set to '_auto',
512
        // it will indent attributes so they all start at the same column
513
        XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES => false,
514
 
515
        // use XML_SERIALIZER_MODE_SIMPLEXML to use parent name as tagname
516
        // if transforming an indexed array
517
        XML_SERIALIZER_OPTION_MODE => XML_SERIALIZER_MODE_DEFAULT,
518
 
519
        // add a doctype declaration
520
        XML_SERIALIZER_OPTION_DOCTYPE_ENABLED => false,
521
 
522
        // supply a string or an array with id and uri
523
        // ({@see XML_Util::getDoctypeDeclaration()}
524
        XML_SERIALIZER_OPTION_DOCTYPE => null,
525
 
526
        // name of the root tag
527
        XML_SERIALIZER_OPTION_ROOT_NAME => null,
528
 
529
        // attributes of the root tag
530
        XML_SERIALIZER_OPTION_ROOT_ATTRIBS => array(),
531
 
532
        // all values in this key will be treated as attributes
533
        XML_SERIALIZER_OPTION_ATTRIBUTES_KEY => null,
534
 
535
        // this value will be used directly as content,
536
        // instead of creating a new tag, may only be used
537
        // in conjuction with attributesArray
538
        XML_SERIALIZER_OPTION_CONTENT_KEY => null,
539
 
540
        // this value will be used directly as comment,
541
        // instead of creating a new tag, may only be used
542
        // in conjuction with attributesArray
543
        XML_SERIALIZER_OPTION_COMMENT_KEY => null,
544
 
545
        // tag names that will be changed
546
        XML_SERIALIZER_OPTION_TAGMAP => array(),
547
 
548
        // function that will be applied before serializing
549
        XML_SERIALIZER_OPTION_ENCODE_FUNC => null,
550
 
551
        // namespace to use
552
        XML_SERIALIZER_OPTION_NAMESPACE => null,
553
 
554
        // type of entities to replace,
555
        XML_SERIALIZER_OPTION_ENTITIES => XML_SERIALIZER_ENTITIES_XML,
556
 
557
        // serialize() returns the result of the serialization instead of true
558
        XML_SERIALIZER_OPTION_RETURN_RESULT => false,
559
 
560
        // ignore properties that are set to null
561
        XML_SERIALIZER_OPTION_IGNORE_NULL => false,
562
 
563
        // Whether to use cdata sections for plain character data
564
        XML_SERIALIZER_OPTION_CDATA_SECTIONS => false,
565
 
566
        // Whether to convert a boolean FALSE into a string
567
        XML_SERIALIZER_OPTION_FALSE_AS_STRING => false,
568
    );
569
 
570
    /**
571
     * Options for the serialization
572
     *
573
     * @access public
574
     * @var    array
575
     */
576
    var $options = array();
577
 
578
    /**
579
     * Current tag depth
580
     *
581
     * @access private
582
     * @var    integer
583
     */
584
    var $_tagDepth = 0;
585
 
586
    /**
587
     * Serialized representation of the data
588
     *
589
     * @access private
590
     * @var    string
591
     */
592
    var $_serializedData = null;
593
 
594
    /**
595
     * Constructor
596
     *
597
     * @param mixed $options array containing options for the serialization
598
     *
599
     * @access public
600
     */
601
    function XML_Serializer( $options = null )
602
    {
603
        $this->PEAR();
604
        if (is_array($options)) {
605
            $this->options = array_merge($this->_defaultOptions, $options);
606
        } else {
607
            $this->options = $this->_defaultOptions;
608
        }
609
    }
610
 
611
    /**
612
     * Return the package version number
613
     *
614
     * @access public
615
     * @static
616
     * @return string the version number of XML_Serializer
617
     */
618
    function apiVersion()
619
    {
620
        return '@package_version@';
621
    }
622
 
623
    /**
624
     * Reset all options to default options
625
     *
626
     * @return void
627
     * @access public
628
     */
629
    function resetOptions()
630
    {
631
        $this->options = $this->_defaultOptions;
632
    }
633
 
634
    /**
635
     * Set an option
636
     *
637
     * You can use this method if you do not want
638
     * to set all options in the constructor.
639
     *
640
     * @param string $name  option name
641
     * @param mixed  $value option value
642
     *
643
     * @return void
644
     * @access public
645
     */
646
    function setOption($name, $value)
647
    {
648
        $this->options[$name] = $value;
649
    }
650
 
651
    /**
652
     * Sets several options at once
653
     *
654
     * You can use this method if you do not want
655
     * to set all options in the constructor.
656
     *
657
     * @param array $options options array
658
     *
659
     * @return void
660
     * @access public
661
     */
662
    function setOptions($options)
663
    {
664
        $this->options = array_merge($this->options, $options);
665
    }
666
 
667
    /**
668
     * serialize data
669
     *
670
     * @param mixed $data    data to serialize
671
     * @param array $options options array
672
     *
673
     * @return boolean true on success, pear error on failure
674
     * @access public
675
     * @uses XML_Util::getDoctypeDeclaration()
676
     * @uses XML_Util::getXMLDeclaration()
677
     * @internal uses error suppression "@settype()"
678
     */
679
    function serialize($data, $options = null)
680
    {
681
        // if options have been specified, use them instead
682
        // of the previously defined ones
683
        if (is_array($options)) {
684
            $optionsBak = $this->options;
685
            if (isset($options['overrideOptions'])
686
                && $options['overrideOptions'] == true
687
            ) {
688
                $this->options = array_merge($this->_defaultOptions, $options);
689
            } else {
690
                $this->options = array_merge($this->options, $options);
691
            }
692
        } else {
693
            $optionsBak = null;
694
        }
695
 
696
        //  start depth is zero
697
        $this->_tagDepth = 0;
698
 
699
        $rootAttributes = $this->options[XML_SERIALIZER_OPTION_ROOT_ATTRIBS];
700
        if (isset($this->options[XML_SERIALIZER_OPTION_NAMESPACE])
701
            && is_array($this->options[XML_SERIALIZER_OPTION_NAMESPACE])
702
        ) {
703
            $rootAttributes['xmlns:'
704
                . $this->options[XML_SERIALIZER_OPTION_NAMESPACE][0]] =
705
                $this->options[XML_SERIALIZER_OPTION_NAMESPACE][1];
706
        }
707
 
708
        $this->_serializedData = '';
709
        // serialize an array
710
        if (is_array($data)) {
711
            if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) {
712
                $tagName = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME];
713
            } else {
714
                $tagName = 'array';
715
            }
716
 
717
            $this->_serializedData .=
718
                $this->_serializeArray($data, $tagName, $rootAttributes);
719
        } elseif (is_object($data)) {
720
            // serialize an object
721
            if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) {
722
                $tagName = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME];
723
            } else {
724
                $tagName = get_class($data);
725
            }
726
            $this->_serializedData .=
727
                $this->_serializeObject($data, $tagName, $rootAttributes);
728
        } else {
729
            $tag = array();
730
            if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) {
731
                $tag['qname'] = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME];
732
            } else {
733
                $tag['qname'] = gettype($data);
734
            }
735
            $tagName = $tag['qname'];
736
            if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
737
                $rootAttributes[$this->
738
                    options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = gettype($data);
739
            }
740
 
741
            if (!is_bool($data)) {
742
                $tag['content'] = $data;
743
            } elseif ($data === false) {
744
                if ($this->options[XML_SERIALIZER_OPTION_FALSE_AS_STRING] === true) {
745
                    $tag['content'] = '0';
746
                } else {
747
                    $tag['content'] = '';
748
                }
749
            } else {
750
                $tag['content'] = $data;
751
            }
752
 
753
            @settype($data, 'string');
754
            $tag['attributes']     = $rootAttributes;
755
            $this->_serializedData = $this->_createXMLTag($tag);
756
        }
757
 
758
        // add doctype declaration
759
        if ($this->options[XML_SERIALIZER_OPTION_DOCTYPE_ENABLED] === true) {
760
            $this->_serializedData =
761
                XML_Util::getDoctypeDeclaration($tagName,
762
                $this->options[XML_SERIALIZER_OPTION_DOCTYPE])
763
                . $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]
764
                . $this->_serializedData;
765
        }
766
 
767
        //  build xml declaration
768
        if ($this->options[XML_SERIALIZER_OPTION_XML_DECL_ENABLED]) {
769
            $atts                  = array();
770
            $this->_serializedData = XML_Util::getXMLDeclaration('1.0',
771
                $this->options[XML_SERIALIZER_OPTION_XML_ENCODING])
772
                . $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]
773
                . $this->_serializedData;
774
        }
775
 
776
        if ($this->options[XML_SERIALIZER_OPTION_RETURN_RESULT] === true) {
777
            $result = $this->_serializedData;
778
        } else {
779
            $result = true;
780
        }
781
 
782
        if ($optionsBak !== null) {
783
            $this->options = $optionsBak;
784
        }
785
 
786
        return $result;
787
    }
788
 
789
    /**
790
     * get the result of the serialization
791
     *
792
     * @access public
793
     * @return string serialized XML
794
     */
795
    function getSerializedData()
796
    {
797
        if ($this->_serializedData == null) {
798
            return $this->raiseError('No serialized data available. '
799
                . 'Use XML_Serializer::serialize() first.',
800
                XML_SERIALIZER_ERROR_NO_SERIALIZATION);
801
        }
802
        return $this->_serializedData;
803
    }
804
 
805
    /**
806
     * serialize any value
807
     *
808
     * This method checks for the type of the value and calls the appropriate method
809
     *
810
     * @param mixed  $value      tag value
811
     * @param string $tagName    tag name
812
     * @param array  $attributes attributes
813
     *
814
     * @return string
815
     * @access private
816
     */
817
    function _serializeValue($value, $tagName = null, $attributes = array())
818
    {
819
        if (is_array($value)) {
820
            $xml = $this->_serializeArray($value, $tagName, $attributes);
821
        } elseif (is_object($value)) {
822
            $xml = $this->_serializeObject($value, $tagName);
823
        } else {
824
            $tag = array(
825
                          'qname'      => $tagName,
826
                          'attributes' => $attributes,
827
                          'content'    => $value
828
                        );
829
            $xml = $this->_createXMLTag($tag);
830
        }
831
        return $xml;
832
    }
833
 
834
    /**
835
     * serialize an array
836
     *
837
     * @param array  &$array     array to serialize
838
     * @param string $tagName    name of the root tag
839
     * @param array  $attributes attributes for the root tag
840
     *
841
     * @return string $string serialized data
842
     * @access private
843
     * @uses XML_Util::isValidName() to check, whether key has to be substituted
844
     * @uses XML_Util::replaceEntities()
845
     * @uses XML_Util::createComment()
846
     * @uses PEAR::popExpect()
847
     * @uses PEAR::expectError()
848
     */
849
    function _serializeArray(&$array, $tagName = null, $attributes = array())
850
    {
851
        $_content = null;
852
        $_comment = null;
853
 
854
        // check for comment
855
        if ($this->options[XML_SERIALIZER_OPTION_COMMENT_KEY] !== null) {
856
            if (isset($array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]])
857
            ) {
858
                $_comment =
859
                    $array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]];
860
                unset($array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]]);
861
            }
862
        }
863
 
864
        /**
865
         * check for special attributes
866
         */
867
        if ($this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY] !== null) {
868
            if (isset($array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]])
869
            ) {
870
                $attributes =
871
                    $array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]];
872
                unset($array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]]);
873
            }
874
            /**
875
             * check for special content
876
             */
877
            if ($this->options[XML_SERIALIZER_OPTION_CONTENT_KEY] !== null) {
878
                if (isset($array[$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]])
879
                ) {
880
                    $_content =
881
                        XML_Util::replaceEntities($array
882
                        [$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]]);
883
                    unset($array[$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]]);
884
                }
885
            }
886
        }
887
 
888
        if ($this->options[XML_SERIALIZER_OPTION_IGNORE_NULL] === true) {
889
            foreach (array_keys($array) as $key) {
890
                if (is_null($array[$key])) {
891
                    unset($array[$key]);
892
                }
893
            }
894
        }
895
 
896
        /*
897
        * if mode is set to simpleXML, check whether
898
        * the array is associative or indexed
899
        */
900
        if (is_array($array) && !empty($array)
901
            && $this->options[XML_SERIALIZER_OPTION_MODE]
902
            == XML_SERIALIZER_MODE_SIMPLEXML
903
        ) {
904
            $indexed = true;
905
            foreach ($array as $key => $val) {
906
                if (!is_int($key)) {
907
                    $indexed = false;
908
                    break;
909
                }
910
            }
911
 
912
            if ($indexed
913
                && $this->options[XML_SERIALIZER_OPTION_MODE]
914
                == XML_SERIALIZER_MODE_SIMPLEXML
915
            ) {
916
                $string = '';
917
                foreach ($array as $key => $val) {
918
                    $string .= $this->_serializeValue($val, $tagName, $attributes);
919
 
920
                    $string .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
921
                    // do indentation
922
                    if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null
923
                        && $this->_tagDepth>0
924
                    ) {
925
                        $string .=
926
                            str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT],
927
                            $this->_tagDepth);
928
                    }
929
                }
930
                return rtrim($string);
931
            }
932
        }
933
 
934
        $scalarAsAttributes = false;
935
        if (is_array($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES])
936
            && isset($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES]
937
            [$tagName])
938
        ) {
939
            $scalarAsAttributes =
940
                $this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES][$tagName];
941
        } elseif ($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES] === true
942
        ) {
943
            $scalarAsAttributes = true;
944
        }
945
 
946
        if ($scalarAsAttributes === true) {
947
            $this->expectError('*');
948
            foreach ($array as $key => $value) {
949
                if (is_scalar($value) && (XML_Util::isValidName($key) === true)) {
950
                    unset($array[$key]);
951
                    $attributes[$this->options
952
                        [XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES].$key] = $value;
953
                }
954
            }
955
            $this->popExpect();
956
        } elseif (is_array($scalarAsAttributes)) {
957
            $this->expectError('*');
958
            foreach ($scalarAsAttributes as $key) {
959
                if (!isset($array[$key])) {
960
                    continue;
961
                }
962
                $value = $array[$key];
963
                if (is_scalar($value) && (XML_Util::isValidName($key) === true)) {
964
                    unset($array[$key]);
965
                    $attributes[$this->options
966
                        [XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES].$key] = $value;
967
                }
968
            }
969
            $this->popExpect();
970
        }
971
 
972
        // check for empty array => create empty tag
973
        if (empty($array)) {
974
            $tag = array(
975
                            'qname'      => $tagName,
976
                            'content'    => $_content,
977
                            'attributes' => $attributes
978
                        );
979
        } else {
980
            $this->_tagDepth++;
981
            $tmp = $_content . $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
982
            foreach ($array as $key => $value) {
983
                // do indentation
984
                if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null
985
                    && $this->_tagDepth>0
986
                ) {
987
                    $tmp .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT],
988
                        $this->_tagDepth);
989
                }
990
 
991
                // copy key
992
                $origKey = $key;
993
                $this->expectError('*');
994
                // key cannot be used as tagname => use default tag
995
                $valid = XML_Util::isValidName($key);
996
                $this->popExpect();
997
                if (PEAR::isError($valid)) {
998
                    if ($this->options[XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME]
999
                        && is_object($value)
1000
                    ) {
1001
                        $key = get_class($value);
1002
                    } else {
1003
                        $key = $this->_getDefaultTagname($tagName);
1004
                    }
1005
                }
1006
 
1007
                // once we've established the true $key, is there a tagmap for it?
1008
                if (isset($this->options[XML_SERIALIZER_OPTION_TAGMAP][$key])) {
1009
                    $key = $this->options[XML_SERIALIZER_OPTION_TAGMAP][$key];
1010
                }
1011
 
1012
                $atts = array();
1013
                if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
1014
                    $atts[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] =
1015
                        gettype($value);
1016
                    if ($key !== $origKey) {
1017
                        $atts[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_KEY]] =
1018
                            (string)$origKey;
1019
                    }
1020
                }
1021
 
1022
                $tmp .= $this->_createXMLTag(array(
1023
                    'qname'      => $key,
1024
                    'attributes' => $atts,
1025
                    'content'    => $value
1026
                ));
1027
                $tmp .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
1028
            }
1029
 
1030
            $this->_tagDepth--;
1031
            if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null
1032
                && $this->_tagDepth>0
1033
            ) {
1034
                $tmp .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT],
1035
                    $this->_tagDepth);
1036
            }
1037
 
1038
            if (trim($tmp) === '') {
1039
                $tmp = null;
1040
            }
1041
 
1042
            $tag = array(
1043
                          'qname'      => $tagName,
1044
                          'content'    => $tmp,
1045
                          'attributes' => $attributes
1046
                        );
1047
        }
1048
        if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
1049
            if (!isset($tag['attributes']
1050
                [$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]])
1051
            ) {
1052
                $tag['attributes']
1053
                    [$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = 'array';
1054
            }
1055
        }
1056
 
1057
        $string = '';
1058
        if (!is_null($_comment)) {
1059
            $string .= XML_Util::createComment($_comment);
1060
            $string .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
1061
            if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null
1062
                && $this->_tagDepth>0
1063
            ) {
1064
                $string .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT],
1065
                    $this->_tagDepth);
1066
            }
1067
        }
1068
        $string .= $this->_createXMLTag($tag, false);
1069
        return $string;
1070
    }
1071
 
1072
    /**
1073
     * get the name of the default tag.
1074
     *
1075
     * The name of the parent tag needs to be passed as the
1076
     * default name can depend on the context.
1077
     *
1078
     * @param string $parent name of the parent tag
1079
     *
1080
     * @return string default tag name
1081
     */
1082
    function _getDefaultTagname($parent)
1083
    {
1084
        if (is_string($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG])) {
1085
            return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG];
1086
        }
1087
        if (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG][$parent])) {
1088
            return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG][$parent];
1089
        } elseif (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]
1090
            ['#default'])
1091
        ) {
1092
            return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['#default'];
1093
        } elseif (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]
1094
            ['__default'])
1095
        ) {
1096
            // keep this for BC
1097
            return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['__default'];
1098
        }
1099
        return 'XML_Serializer_Tag';
1100
    }
1101
 
1102
    /**
1103
     * serialize an object
1104
     *
1105
     * @param object &$object    object to serialize
1106
     * @param string $tagName    tag name
1107
     * @param array  $attributes attributes
1108
     *
1109
     * @return string $string serialized data
1110
     * @access private
1111
     */
1112
    function _serializeObject(&$object, $tagName = null, $attributes = array())
1113
    {
1114
        // check for magic function
1115
        if (method_exists($object, '__sleep')) {
1116
            $propNames = $object->__sleep();
1117
            if (is_array($propNames)) {
1118
                $properties = array();
1119
                foreach ($propNames as $propName) {
1120
                    $properties[$propName] = $object->$propName;
1121
                }
1122
            } else {
1123
                $properties = get_object_vars($object);
1124
            }
1125
        } else {
1126
            $properties = get_object_vars($object);
1127
        }
1128
 
1129
        if (empty($tagName)) {
1130
            $tagName = get_class($object);
1131
        }
1132
 
1133
        // typehints activated?
1134
        if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
1135
            $attributes[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]]  =
1136
                'object';
1137
            $attributes[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS]] =
1138
                get_class($object);
1139
        }
1140
        $string = $this->_serializeArray($properties, $tagName, $attributes);
1141
        return $string;
1142
    }
1143
 
1144
    /**
1145
     * create a tag from an array
1146
     * this method awaits an array in the following format
1147
     * array(
1148
     *       'qname'        => $tagName,
1149
     *       'attributes'   => array(),
1150
     *       'content'      => $content,      // optional
1151
     *       'namespace'    => $namespace     // optional
1152
     *       'namespaceUri' => $namespaceUri  // optional
1153
     *   )
1154
     *
1155
     * @param array   $tag       tag definition
1156
     * @param boolean $firstCall whether or not this is the first call
1157
     *
1158
     * @return string $string XML tag
1159
     * @access private
1160
     */
1161
    function _createXMLTag($tag, $firstCall = true)
1162
    {
1163
        // build fully qualified tag name
1164
        if ($this->options[XML_SERIALIZER_OPTION_NAMESPACE] !== null) {
1165
            if (is_array($this->options[XML_SERIALIZER_OPTION_NAMESPACE])) {
1166
                $tag['qname'] = $this->options[XML_SERIALIZER_OPTION_NAMESPACE][0]
1167
                    . ':' . $tag['qname'];
1168
            } else {
1169
                $tag['qname'] = $this->options[XML_SERIALIZER_OPTION_NAMESPACE]
1170
                    . ':' . $tag['qname'];
1171
            }
1172
        }
1173
 
1174
        // attribute indentation
1175
        if ($this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES] !== false) {
1176
            $multiline = true;
1177
            $indent    = str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT],
1178
                $this->_tagDepth);
1179
 
1180
            if ($this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES] == '_auto') {
1181
                $indent .= str_repeat(' ', (strlen($tag['qname'])+2));
1182
 
1183
            } else {
1184
                $indent .= $this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES];
1185
            }
1186
        } else {
1187
            $multiline = false;
1188
            $indent    = false;
1189
        }
1190
 
1191
        if (is_array($tag['content'])) {
1192
            if (empty($tag['content'])) {
1193
                $tag['content'] =   '';
1194
            }
1195
} elseif (XML_SERIALIZER_OPTION_FALSE_AS_STRING && $tag['content'] === false) {
1196
$tag['content'] = '0';
1197
        } elseif (is_scalar($tag['content']) && (string)$tag['content'] == '') {
1198
            $tag['content'] =   '';
1199
        }
1200
 
1201
        // replace XML entities
1202
        if ($firstCall === true) {
1203
            if ($this->options[XML_SERIALIZER_OPTION_CDATA_SECTIONS] === true) {
1204
                $replaceEntities = XML_UTIL_CDATA_SECTION;
1205
            } else {
1206
                $replaceEntities = $this->options[XML_SERIALIZER_OPTION_ENTITIES];
1207
            }
1208
        } else {
1209
            // this is a nested call, so value is already encoded
1210
            // and must not be encoded again
1211
            $replaceEntities = XML_SERIALIZER_ENTITIES_NONE;
1212
            // but attributes need to be encoded anyways
1213
            // (done here because the rest of the code assumes the same encoding
1214
            // can be used both for attributes and content)
1215
            foreach ($tag['attributes'] as $k => $v) {
1216
                $v = XML_Util::replaceEntities($v,
1217
                    $this->options[XML_SERIALIZER_OPTION_ENTITIES]);
1218
 
1219
                $tag['attributes'][$k] = $v;
1220
            }
1221
        }
1222
        if (is_scalar($tag['content']) || is_null($tag['content'])) {
1223
            if ($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC]) {
1224
                if ($firstCall === true) {
1225
                    $tag['content'] = call_user_func($this->
1226
                        options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['content']);
1227
                }
1228
                $tag['attributes'] = array_map($this->
1229
                    options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['attributes']);
1230
            }
1231
            $tag = XML_Util::createTagFromArray($tag, $replaceEntities, $multiline,
1232
                $indent, $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]);
1233
        } elseif (is_array($tag['content'])) {
1234
            $tag = $this->_serializeArray($tag['content'], $tag['qname'],
1235
                $tag['attributes']);
1236
        } elseif (is_object($tag['content'])) {
1237
            $tag = $this->_serializeObject($tag['content'], $tag['qname'],
1238
                $tag['attributes']);
1239
        } elseif (is_resource($tag['content'])) {
1240
            settype($tag['content'], 'string');
1241
            if ($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC]) {
1242
                if ($replaceEntities === true) {
1243
                    $tag['content'] = call_user_func($this->
1244
                        options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['content']);
1245
                }
1246
                $tag['attributes'] = array_map($this->
1247
                    options[XML_SERIALIZER_OPTION_ENCODE_FUNC],
1248
                    $tag['attributes']);
1249
            }
1250
            $tag = XML_Util::createTagFromArray($tag, $replaceEntities);
1251
        }
1252
        return  $tag;
1253
    }
1254
}
1255
?>