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_Unserializer
6
 *
7
 * Parses any XML document into PHP data structures.
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: Unserializer.php 303099 2010-09-06 16:23:06Z clockwerx $
46
 * @link      http://pear.php.net/package/XML_Serializer
47
 * @see       XML_Unserializer
48
 */
49
 
50
/**
51
 * uses PEAR error managemt
52
 */
53
require_once 'PEAR.php';
54
 
55
/**
56
 * uses XML_Parser to unserialize document
57
 */
58
require_once 'XML/Parser.php';
59
 
60
/**
61
 * option: Convert nested tags to array or object
62
 *
63
 * Possible values:
64
 * - array
65
 * - object
66
 * - associative array to define this option per tag name
67
 */
68
define('XML_UNSERIALIZER_OPTION_COMPLEXTYPE', 'complexType');
69
 
70
/**
71
 * option: Name of the attribute that stores the original key
72
 *
73
 * Possible values:
74
 * - any string
75
 */
76
define('XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY', 'keyAttribute');
77
 
78
/**
79
 * option: Name of the attribute that stores the type
80
 *
81
 * Possible values:
82
 * - any string
83
 */
84
define('XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE', 'typeAttribute');
85
 
86
/**
87
 * option: Name of the attribute that stores the class name
88
 *
89
 * Possible values:
90
 * - any string
91
 */
92
define('XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS', 'classAttribute');
93
 
94
/**
95
 * option: Whether to use the tag name as a class name
96
 *
97
 * Possible values:
98
 * - true or false
99
 */
100
define('XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME', 'tagAsClass');
101
 
102
/**
103
 * option: Name of the default class
104
 *
105
 * Possible values:
106
 * - any string
107
 */
108
define('XML_UNSERIALIZER_OPTION_DEFAULT_CLASS', 'defaultClass');
109
 
110
/**
111
 * option: Whether to parse attributes
112
 *
113
 * Possible values:
114
 * - true or false
115
 */
116
define('XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE', 'parseAttributes');
117
 
118
/**
119
 * option: Key of the array to store attributes (if any)
120
 *
121
 * Possible values:
122
 * - any string
123
 * - false (disabled)
124
 */
125
define('XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY', 'attributesArray');
126
 
127
/**
128
 * option: string to prepend attribute name (if any)
129
 *
130
 * Possible values:
131
 * - any string
132
 * - false (disabled)
133
 */
134
define('XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND', 'prependAttributes');
135
 
136
/**
137
 * option: key to store the content,
138
 * if XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE is used
139
 *
140
 * Possible values:
141
 * - any string
142
 */
143
define('XML_UNSERIALIZER_OPTION_CONTENT_KEY', 'contentName');
144
 
145
/**
146
 * option: map tag names
147
 *
148
 * Possible values:
149
 * - associative array
150
 */
151
define('XML_UNSERIALIZER_OPTION_TAG_MAP', 'tagMap');
152
 
153
/**
154
 * option: list of tags that will always be enumerated
155
 *
156
 * Possible values:
157
 * - indexed array
158
 */
159
define('XML_UNSERIALIZER_OPTION_FORCE_ENUM', 'forceEnum');
160
 
161
/**
162
 * option: Encoding of the XML document
163
 *
164
 * Possible values:
165
 * - UTF-8
166
 * - ISO-8859-1
167
 */
168
define('XML_UNSERIALIZER_OPTION_ENCODING_SOURCE', 'encoding');
169
 
170
/**
171
 * option: Desired target encoding of the data
172
 *
173
 * Possible values:
174
 * - UTF-8
175
 * - ISO-8859-1
176
 */
177
define('XML_UNSERIALIZER_OPTION_ENCODING_TARGET', 'targetEncoding');
178
 
179
/**
180
 * option: Callback that will be applied to textual data
181
 *
182
 * Possible values:
183
 * - any valid PHP callback
184
 */
185
define('XML_UNSERIALIZER_OPTION_DECODE_FUNC', 'decodeFunction');
186
 
187
/**
188
 * option: whether to return the result of the unserialization from unserialize()
189
 *
190
 * Possible values:
191
 * - true
192
 * - false (default)
193
 */
194
define('XML_UNSERIALIZER_OPTION_RETURN_RESULT', 'returnResult');
195
 
196
/**
197
 * option: set the whitespace behaviour
198
 *
199
 * Possible values:
200
 * - XML_UNSERIALIZER_WHITESPACE_KEEP
201
 * - XML_UNSERIALIZER_WHITESPACE_TRIM
202
 * - XML_UNSERIALIZER_WHITESPACE_NORMALIZE
203
 */
204
define('XML_UNSERIALIZER_OPTION_WHITESPACE', 'whitespace');
205
 
206
/**
207
 * Keep all whitespace
208
 */
209
define('XML_UNSERIALIZER_WHITESPACE_KEEP', 'keep');
210
 
211
/**
212
 * remove whitespace from start and end of the data
213
 */
214
define('XML_UNSERIALIZER_WHITESPACE_TRIM', 'trim');
215
 
216
/**
217
 * normalize whitespace
218
 */
219
define('XML_UNSERIALIZER_WHITESPACE_NORMALIZE', 'normalize');
220
 
221
/**
222
 * option: whether to ovverride all options that have been set before
223
 *
224
 * Possible values:
225
 * - true
226
 * - false (default)
227
 */
228
define('XML_UNSERIALIZER_OPTION_OVERRIDE_OPTIONS', 'overrideOptions');
229
 
230
/**
231
 * option: list of tags, that will not be used as keys
232
 */
233
define('XML_UNSERIALIZER_OPTION_IGNORE_KEYS', 'ignoreKeys');
234
 
235
/**
236
 * option: whether to use type guessing for scalar values
237
 */
238
define('XML_UNSERIALIZER_OPTION_GUESS_TYPES', 'guessTypes');
239
 
240
/**
241
 * error code for no serialization done
242
 */
243
define('XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION', 151);
244
 
245
/**
246
 * XML_Unserializer
247
 *
248
 * class to unserialize XML documents that have been created with
249
 * XML_Serializer. To unserialize an XML document you have to add
250
 * type hints to the XML_Serializer options.
251
 *
252
 * If no type hints are available, XML_Unserializer will guess how
253
 * the tags should be treated, that means complex structures will be
254
 * arrays and tags with only CData in them will be strings.
255
 *
256
 * <code>
257
 * require_once 'XML/Unserializer.php';
258
 *
259
 * //  be careful to always use the ampersand in front of the new operator
260
 * $unserializer = &new XML_Unserializer();
261
 *
262
 * $unserializer->unserialize($xml);
263
 *
264
 * $data = $unserializer->getUnserializedData();
265
 * </code>
266
 *
267
 * @category  XML
268
 * @package   XML_Serializer
269
 * @author    Stephan Schmidt <schst@php.net>
270
 * @copyright 2003-2008 Stephan Schmidt <schst@php.net>
271
 * @license   http://opensource.org/licenses/bsd-license New BSD License
272
 * @version   Release: @package_version@
273
 * @link      http://pear.php.net/package/XML_Serializer
274
 * @see       XML_Serializer
275
 */
276
class XML_Unserializer extends PEAR
277
{
278
    /**
279
     * list of all available options
280
     *
281
     * @access private
282
     * @var    array
283
     */
284
    var $_knownOptions = array(
285
                                XML_UNSERIALIZER_OPTION_COMPLEXTYPE,
286
                                XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY,
287
                                XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE,
288
                                XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS,
289
                                XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME,
290
                                XML_UNSERIALIZER_OPTION_DEFAULT_CLASS,
291
                                XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE,
292
                                XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY,
293
                                XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND,
294
                                XML_UNSERIALIZER_OPTION_CONTENT_KEY,
295
                                XML_UNSERIALIZER_OPTION_TAG_MAP,
296
                                XML_UNSERIALIZER_OPTION_FORCE_ENUM,
297
                                XML_UNSERIALIZER_OPTION_ENCODING_SOURCE,
298
                                XML_UNSERIALIZER_OPTION_ENCODING_TARGET,
299
                                XML_UNSERIALIZER_OPTION_DECODE_FUNC,
300
                                XML_UNSERIALIZER_OPTION_RETURN_RESULT,
301
                                XML_UNSERIALIZER_OPTION_WHITESPACE,
302
                                XML_UNSERIALIZER_OPTION_IGNORE_KEYS,
303
                                XML_UNSERIALIZER_OPTION_GUESS_TYPES
304
                              );
305
    /**
306
     * default options for the serialization
307
     *
308
     * @access private
309
     * @var    array
310
     */
311
    var $_defaultOptions = array(
312
        // complex types will be converted to arrays, if no type hint is given
313
        XML_UNSERIALIZER_OPTION_COMPLEXTYPE => 'array',
314
 
315
        // get array key/property name from this attribute
316
        XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY => '_originalKey',
317
 
318
        // get type from this attribute
319
        XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE => '_type',
320
 
321
        // get class from this attribute (if not given, use tag name)
322
        XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS => '_class',
323
 
324
        // use the tagname as the classname
325
        XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME => true,
326
 
327
        // name of the class that is used to create objects
328
        XML_UNSERIALIZER_OPTION_DEFAULT_CLASS => 'stdClass',
329
 
330
        // parse the attributes of the tag into an array
331
        XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE => false,
332
 
333
        // parse them into sperate array (specify name of array here)
334
        XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY => false,
335
 
336
        // prepend attribute names with this string
337
        XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND => '',
338
 
339
        // put cdata found in a tag that has been converted
340
        // to a complex type in this key
341
        XML_UNSERIALIZER_OPTION_CONTENT_KEY => '_content',
342
 
343
        // use this to map tagnames
344
        XML_UNSERIALIZER_OPTION_TAG_MAP => array(),
345
 
346
        // these tags will always be an indexed array
347
        XML_UNSERIALIZER_OPTION_FORCE_ENUM => array(),
348
 
349
        // specify the encoding character of the document to parse
350
        XML_UNSERIALIZER_OPTION_ENCODING_SOURCE => null,
351
 
352
        // specify the target encoding
353
        XML_UNSERIALIZER_OPTION_ENCODING_TARGET => null,
354
 
355
        // function used to decode data
356
        XML_UNSERIALIZER_OPTION_DECODE_FUNC => null,
357
 
358
        // unserialize() returns the result of the unserialization instead of true
359
        XML_UNSERIALIZER_OPTION_RETURN_RESULT => false,
360
 
361
        // remove whitespace around data
362
        XML_UNSERIALIZER_OPTION_WHITESPACE => XML_UNSERIALIZER_WHITESPACE_TRIM,
363
 
364
        // List of tags that will automatically be added to the parent,
365
        // instead of adding a new key
366
        XML_UNSERIALIZER_OPTION_IGNORE_KEYS => array(),
367
 
368
        // Whether to use type guessing
369
        XML_UNSERIALIZER_OPTION_GUESS_TYPES => false
370
    );
371
 
372
    /**
373
     * current options for the serialization
374
     *
375
     * @access public
376
     * @var    array
377
     */
378
    var $options = array();
379
 
380
    /**
381
     * unserialized data
382
     *
383
     * @access private
384
     * @var    string
385
     */
386
    var $_unserializedData = null;
387
 
388
    /**
389
     * name of the root tag
390
     *
391
     * @access private
392
     * @var    string
393
     */
394
    var $_root = null;
395
 
396
    /**
397
     * stack for all data that is found
398
     *
399
     * @access private
400
     * @var    array
401
     */
402
    var $_dataStack = array();
403
 
404
    /**
405
     * stack for all values that are generated
406
     *
407
     * @access private
408
     * @var    array
409
     */
410
    var $_valStack = array();
411
 
412
    /**
413
     * current tag depth
414
     *
415
     * @access private
416
     * @var    int
417
     */
418
    var $_depth = 0;
419
 
420
    /**
421
     * XML_Parser instance
422
     *
423
     * @access   private
424
     * @var      object XML_Parser
425
     */
426
    var $_parser = null;
427
 
428
    /**
429
     * constructor
430
     *
431
     * @param mixed $options array containing options for the unserialization
432
     *
433
     * @access public
434
     */
435
    function XML_Unserializer($options = null)
436
    {
437
        if (is_array($options)) {
438
            $this->options = array_merge($this->_defaultOptions, $options);
439
        } else {
440
            $this->options = $this->_defaultOptions;
441
        }
442
    }
443
 
444
    /**
445
     * return API version
446
     *
447
     * @access   public
448
     * @return string  $version API version
449
     * @static
450
     */
451
    function apiVersion()
452
    {
453
        return '@package_version@';
454
    }
455
 
456
    /**
457
     * reset all options to default options
458
     *
459
     * @return void
460
     * @access public
461
     * @see setOption(), XML_Unserializer(), setOptions()
462
     */
463
    function resetOptions()
464
    {
465
        $this->options = $this->_defaultOptions;
466
    }
467
 
468
    /**
469
     * set an option
470
     *
471
     * You can use this method if you do not want
472
     * to set all options in the constructor
473
     *
474
     * @param string $name  name of option
475
     * @param mixed  $value value of option
476
     *
477
     * @return void
478
     * @access public
479
     * @see resetOption(), XML_Unserializer(), setOptions()
480
     */
481
    function setOption($name, $value)
482
    {
483
        $this->options[$name] = $value;
484
    }
485
 
486
    /**
487
     * sets several options at once
488
     *
489
     * You can use this method if you do not want
490
     * to set all options in the constructor
491
     *
492
     * @param array $options options array
493
     *
494
     * @return void
495
     * @access public
496
     * @see resetOption(), XML_Unserializer(), setOption()
497
     */
498
    function setOptions($options)
499
    {
500
        $this->options = array_merge($this->options, $options);
501
    }
502
 
503
    /**
504
     * unserialize data
505
     *
506
     * @param mixed   $data    data to unserialize (string, filename or resource)
507
     * @param boolean $isFile  data should be treated as a file
508
     * @param array   $options options that will override
509
     *                         the global options for this call
510
     *
511
     * @return boolean $success
512
     * @access public
513
     */
514
    function unserialize($data, $isFile = false, $options = null)
515
    {
516
        $this->_unserializedData = null;
517
        $this->_root             = null;
518
 
519
        // if options have been specified, use them instead
520
        // of the previously defined ones
521
        if (is_array($options)) {
522
            $optionsBak = $this->options;
523
            if (isset($options[XML_UNSERIALIZER_OPTION_OVERRIDE_OPTIONS])
524
                && $options[XML_UNSERIALIZER_OPTION_OVERRIDE_OPTIONS] == true
525
            ) {
526
                $this->options = array_merge($this->_defaultOptions, $options);
527
            } else {
528
                $this->options = array_merge($this->options, $options);
529
            }
530
        } else {
531
            $optionsBak = null;
532
        }
533
 
534
        $this->_valStack  = array();
535
        $this->_dataStack = array();
536
        $this->_depth     = 0;
537
 
538
        $this->_createParser();
539
 
540
        if (is_string($data)) {
541
            if ($isFile) {
542
                $result = $this->_parser->setInputFile($data);
543
                if (PEAR::isError($result)) {
544
                    return $result;
545
                }
546
                $result = $this->_parser->parse();
547
            } else {
548
                $result = $this->_parser->parseString($data, true);
549
            }
550
        } else {
551
            $this->_parser->setInput($data);
552
            $result = $this->_parser->parse();
553
        }
554
 
555
        if ($this->options[XML_UNSERIALIZER_OPTION_RETURN_RESULT] === true) {
556
            $return = $this->_unserializedData;
557
        } else {
558
            $return = true;
559
        }
560
 
561
        if ($optionsBak !== null) {
562
            $this->options = $optionsBak;
563
        }
564
 
565
        if (PEAR::isError($result)) {
566
            return $result;
567
        }
568
 
569
        return $return;
570
    }
571
 
572
    /**
573
     * get the result of the serialization
574
     *
575
     * @access public
576
     * @return string  $serializedData
577
     */
578
    function getUnserializedData()
579
    {
580
        if ($this->_root === null) {
581
            return $this->raiseError('No unserialized data available. '
582
                . 'Use XML_Unserializer::unserialize() first.',
583
                XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION);
584
        }
585
        return $this->_unserializedData;
586
    }
587
 
588
    /**
589
     * get the name of the root tag
590
     *
591
     * @access public
592
     * @return string  $rootName
593
     */
594
    function getRootName()
595
    {
596
        if ($this->_root === null) {
597
            return $this->raiseError('No unserialized data available. '
598
                . 'Use XML_Unserializer::unserialize() first.',
599
                XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION);
600
        }
601
        return $this->_root;
602
    }
603
 
604
    /**
605
     * Start element handler for XML parser
606
     *
607
     * @param object $parser  XML parser object
608
     * @param string $element XML element
609
     * @param array  $attribs attributes of XML tag
610
     *
611
     * @return void
612
     * @access private
613
     */
614
    function startHandler($parser, $element, $attribs)
615
    {
616
        if (isset($attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE]])
617
        ) {
618
            $type = $attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE]];
619
 
620
            $guessType = false;
621
        } else {
622
            $type = 'string';
623
            if ($this->options[XML_UNSERIALIZER_OPTION_GUESS_TYPES] === true) {
624
                $guessType = true;
625
            } else {
626
                $guessType = false;
627
            }
628
        }
629
 
630
        if ($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC] !== null) {
631
            $attribs = array_map($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC],
632
                $attribs);
633
        }
634
 
635
        $this->_depth++;
636
        $this->_dataStack[$this->_depth] = null;
637
 
638
        if (is_array($this->options[XML_UNSERIALIZER_OPTION_TAG_MAP])
639
            && isset($this->options[XML_UNSERIALIZER_OPTION_TAG_MAP][$element])
640
        ) {
641
            $element = $this->options[XML_UNSERIALIZER_OPTION_TAG_MAP][$element];
642
        }
643
 
644
        $val = array(
645
                     'name'         => $element,
646
                     'value'        => null,
647
                     'type'         => $type,
648
                     'guessType'    => $guessType,
649
                     'childrenKeys' => array(),
650
                     'aggregKeys'   => array()
651
                    );
652
 
653
        if ($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE] == true
654
            && (count($attribs) > 0)
655
        ) {
656
            $val['children'] = array();
657
            $val['type']     = $this->_getComplexType($element);
658
            $val['class']    = $element;
659
 
660
            if ($this->options[XML_UNSERIALIZER_OPTION_GUESS_TYPES] === true) {
661
                $attribs = $this->_guessAndSetTypes($attribs);
662
            }
663
            if ($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY] != false
664
            ) {
665
                $val['children'][$this->
666
                    options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY]] = $attribs;
667
            } else {
668
                foreach ($attribs as $attrib => $value) {
669
                    $val['children'][$this->
670
                        options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND]
671
                        . $attrib] = $value;
672
                }
673
            }
674
        }
675
 
676
        $keyAttr = false;
677
 
678
        if (is_string($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY])) {
679
            $keyAttr = $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY];
680
        } elseif (is_array($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY])) {
681
            if (isset($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]
682
                [$element])
683
            ) {
684
                $keyAttr =
685
                    $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY][$element];
686
            } elseif (isset($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]
687
                ['#default'])
688
            ) {
689
                $keyAttr = $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]
690
                ['#default'];
691
            } elseif (isset($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]
692
                ['__default'])
693
            ) {
694
                // keep this for BC
695
                $keyAttr =
696
                    $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]
697
                    ['__default'];
698
            }
699
        }
700
 
701
        if ($keyAttr !== false && isset($attribs[$keyAttr])) {
702
            $val['name'] = $attribs[$keyAttr];
703
        }
704
 
705
        if (isset($attribs[$this->
706
            options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS]])
707
        ) {
708
            $val['class'] =
709
                $attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS]];
710
        }
711
 
712
        array_push($this->_valStack, $val);
713
    }
714
 
715
    /**
716
     * Try to guess the type of several values and
717
     * set them accordingly
718
     *
719
     * @param array $array array containing the values
720
     *
721
     * @return array array, containing the values with their correct types
722
     * @access private
723
     */
724
    function _guessAndSetTypes($array)
725
    {
726
        foreach ($array as $key => $value) {
727
            $array[$key] = $this->_guessAndSetType($value);
728
        }
729
        return $array;
730
    }
731
 
732
    /**
733
     * Try to guess the type of a value and
734
     * set it accordingly
735
     *
736
     * @param string $value character data
737
     *
738
     * @return mixed value with the best matching type
739
     * @access private
740
     */
741
    function _guessAndSetType($value)
742
    {
743
        if ($value === 'true') {
744
            return true;
745
        }
746
        if ($value === 'false') {
747
            return false;
748
        }
749
        if ($value === 'NULL') {
750
            return null;
751
        }
752
        if (preg_match('/^[-+]?[0-9]{1,}\\z/', $value)) {
753
            return intval($value);
754
        }
755
        if (preg_match('/^[-+]?[0-9]{1,}\.[0-9]{1,}\\z/', $value)) {
756
            return doubleval($value);
757
        }
758
        return (string)$value;
759
    }
760
 
761
    /**
762
     * End element handler for XML parser
763
     *
764
     * @param object $parser  XML parser object
765
     * @param string $element element
766
     *
767
     * @return void
768
     * @access private
769
     */
770
    function endHandler($parser, $element)
771
    {
772
        $value = array_pop($this->_valStack);
773
        switch ($this->options[XML_UNSERIALIZER_OPTION_WHITESPACE]) {
774
        case XML_UNSERIALIZER_WHITESPACE_KEEP:
775
            $data = $this->_dataStack[$this->_depth];
776
            break;
777
        case XML_UNSERIALIZER_WHITESPACE_NORMALIZE:
778
            $data = trim(preg_replace('/\s\s+/m', ' ',
779
                $this->_dataStack[$this->_depth]));
780
            break;
781
        case XML_UNSERIALIZER_WHITESPACE_TRIM:
782
        default:
783
            $data = trim($this->_dataStack[$this->_depth]);
784
            break;
785
        }
786
 
787
        // adjust type of the value
788
        switch(strtolower($value['type'])) {
789
 
790
        // unserialize an object
791
        case 'object':
792
            if (isset($value['class'])) {
793
                $classname = $value['class'];
794
            } else {
795
                $classname = '';
796
            }
797
            // instantiate the class
798
            if ($this->options[XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME] === true
799
                && class_exists($classname)
800
            ) {
801
                $value['value'] = new $classname;
802
            } else {
803
                $value['value'] =
804
                    new $this->options[XML_UNSERIALIZER_OPTION_DEFAULT_CLASS];
805
            }
806
            if (trim($data) !== '') {
807
                if ($value['guessType'] === true) {
808
                    $data = $this->_guessAndSetType($data);
809
                }
810
                $value['children'][$this->
811
                    options[XML_UNSERIALIZER_OPTION_CONTENT_KEY]] = $data;
812
            }
813
 
814
            // set properties
815
            foreach ($value['children'] as $prop => $propVal) {
816
                // check whether there is a special method to set this property
817
                $setMethod = 'set'.$prop;
818
                if (method_exists($value['value'], $setMethod)) {
819
                    call_user_func(array(&$value['value'], $setMethod), $propVal);
820
                } else {
821
                    $value['value']->$prop = $propVal;
822
                }
823
            }
824
            //  check for magic function
825
            if (method_exists($value['value'], '__wakeup')) {
826
                $value['value']->__wakeup();
827
            }
828
            break;
829
 
830
        // unserialize an array
831
        case 'array':
832
            if (trim($data) !== '') {
833
                if ($value['guessType'] === true) {
834
                    $data = $this->_guessAndSetType($data);
835
                }
836
                $value['children'][$this->
837
                    options[XML_UNSERIALIZER_OPTION_CONTENT_KEY]] = $data;
838
            }
839
            if (isset($value['children'])) {
840
                $value['value'] = $value['children'];
841
            } else {
842
                $value['value'] = array();
843
            }
844
            break;
845
 
846
        // unserialize a null value
847
        case 'null':
848
            $data = null;
849
            break;
850
 
851
        // unserialize a resource => this is not possible :-(
852
        case 'resource':
853
            $value['value'] = $data;
854
            break;
855
 
856
        // unserialize any scalar value
857
        default:
858
            if ($value['guessType'] === true) {
859
                $data = $this->_guessAndSetType($data);
860
            } else {
861
                settype($data, $value['type']);
862
            }
863
 
864
            $value['value'] = $data;
865
            break;
866
        }
867
        $parent = array_pop($this->_valStack);
868
        if ($parent === null) {
869
            $this->_unserializedData = &$value['value'];
870
            $this->_root             = &$value['name'];
871
            return true;
872
        } else {
873
            // parent has to be an array
874
            if (!isset($parent['children']) || !is_array($parent['children'])) {
875
                $parent['children'] = array();
876
                if (!in_array($parent['type'], array('array', 'object'))) {
877
                    $parent['type'] = $this->_getComplexType($parent['name']);
878
                    if ($parent['type'] == 'object') {
879
                        $parent['class'] = $parent['name'];
880
                    }
881
                }
882
            }
883
 
884
            if (in_array($element,
885
                $this->options[XML_UNSERIALIZER_OPTION_IGNORE_KEYS])
886
            ) {
887
                $ignoreKey = true;
888
            } else {
889
                $ignoreKey = false;
890
            }
891
 
892
            if (!empty($value['name']) && $ignoreKey === false) {
893
                // there already has been a tag with this name
894
                if (in_array($value['name'], $parent['childrenKeys'])
895
                    || in_array($value['name'],
896
                    $this->options[XML_UNSERIALIZER_OPTION_FORCE_ENUM])
897
                ) {
898
                    // no aggregate has been created for this tag
899
                    if (!in_array($value['name'], $parent['aggregKeys'])) {
900
                        if (isset($parent['children'][$value['name']])) {
901
                            $parent['children'][$value['name']] =
902
                                array($parent['children'][$value['name']]);
903
                        } else {
904
                            $parent['children'][$value['name']] = array();
905
                        }
906
                        array_push($parent['aggregKeys'], $value['name']);
907
                    }
908
                    array_push($parent['children'][$value['name']], $value['value']);
909
                } else {
910
                    $parent['children'][$value['name']] = &$value['value'];
911
                    array_push($parent['childrenKeys'], $value['name']);
912
                }
913
            } else {
914
                array_push($parent['children'], $value['value']);
915
            }
916
            array_push($this->_valStack, $parent);
917
        }
918
 
919
        $this->_depth--;
920
    }
921
 
922
    /**
923
     * Handler for character data
924
     *
925
     * @param object $parser XML parser object
926
     * @param string $cdata  CDATA
927
     *
928
     * @return void
929
     * @access private
930
     */
931
    function cdataHandler($parser, $cdata)
932
    {
933
        if ($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC] !== null) {
934
            $cdata = call_user_func($this->
935
                options[XML_UNSERIALIZER_OPTION_DECODE_FUNC], $cdata);
936
        }
937
        $this->_dataStack[$this->_depth] .= $cdata;
938
    }
939
 
940
    /**
941
     * get the complex type, that should be used for a specified tag
942
     *
943
     * @param string $tagname name of the tag
944
     *
945
     * @return string complex type ('array' or 'object')
946
     * @access private
947
     */
948
    function _getComplexType($tagname)
949
    {
950
        if (is_string($this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE])) {
951
            return $this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE];
952
        }
953
        if (isset($this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE][$tagname])) {
954
            return $this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE][$tagname];
955
        }
956
        if (isset($this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE]['#default'])) {
957
            return $this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE]['#default'];
958
        }
959
        return 'array';
960
    }
961
 
962
    /**
963
     * create the XML_Parser instance
964
     *
965
     * @return boolean
966
     * @access private
967
     */
968
    function _createParser()
969
    {
970
        if (is_object($this->_parser)) {
971
            $this->_parser->free();
972
            unset($this->_parser);
973
        }
974
        $this->_parser = new XML_Parser($this->
975
            options[XML_UNSERIALIZER_OPTION_ENCODING_SOURCE],
976
            'event', $this->options[XML_UNSERIALIZER_OPTION_ENCODING_TARGET]);
977
 
978
        $this->_parser->folding = false;
979
        $this->_parser->setHandlerObj($this);
980
        return true;
981
    }
982
}
983
?>