source: branches/version-2_12-dev/data/module/XML/Serializer.php @ 21713

Revision 21713, 39.9 KB checked in by AMUAMU, 12 years ago (diff)

#1604 (外部連携用APIの実装) 基本機構実装、Amazon API相当の機能の実装(商品一覧系)

RevLine 
[21713]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 */
53require_once 'PEAR.php';
54
55/**
56 * uses XML_Util to create XML tags
57 */
58require_once 'XML/Util.php';
59
60/**
61 * option: string used for indentation
62 *
63 * Possible values:
64 * - any string (default is any string)
65 */
66define('XML_SERIALIZER_OPTION_INDENT', 'indent');
67
68/**
69 * option: string used for linebreaks
70 *
71 * Possible values:
72 * - any string (default is \n)
73 */
74define('XML_SERIALIZER_OPTION_LINEBREAKS', 'linebreak');
75
76/**
77 * option: enable type hints
78 *
79 * Possible values:
80 * - true
81 * - false
82 */
83define('XML_SERIALIZER_OPTION_TYPEHINTS', 'typeHints');
84
85/**
86 * option: add an XML declaration
87 *
88 * Possible values:
89 * - true
90 * - false
91 */
92define('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 */
101define('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 */
109define('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 */
118define('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 */
126define('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 */
134define('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 */
142define('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 */
152define('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 */
160define('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 */
171define('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 */
181define('XML_SERIALIZER_OPTION_MODE', 'mode');
182
183/**
184 * option: add a doctype declaration
185 *
186 * Possible values:
187 * - true
188 * - false (default)
189 */
190define('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 */
200define('XML_SERIALIZER_OPTION_DOCTYPE', 'doctype');
201
202/**
203 * option: name of the root tag
204 *
205 * Possible values:
206 * - string
207 * - null (default)
208 */
209define('XML_SERIALIZER_OPTION_ROOT_NAME', 'rootName');
210
211/**
212 * option: attributes of the root tag
213 *
214 * Possible values:
215 * - array
216 */
217define('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 */
225define('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 */
236define('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 */
245define('XML_SERIALIZER_OPTION_COMMENT_KEY', 'commentName');
246
247/**
248 * option: tag names that will be changed
249 *
250 * Possible values:
251 * - array
252 */
253define('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 */
261define('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 */
270define('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 */
281define('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 */
290define('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 */
299define('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 */
308define('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 */
319define('XML_SERIALIZER_OPTION_FALSE_AS_STRING', 'falseAsString');
320
321/**
322 * default mode
323 */
324define('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 */
331define('XML_SERIALIZER_MODE_SIMPLEXML', 'simplexml');
332
333/**
334 * error code for no serialization done
335 */
336define('XML_SERIALIZER_ERROR_NO_SERIALIZATION', 51);
337
338/**
339 * do not replace entitites
340 */
341define('XML_SERIALIZER_ENTITIES_NONE', XML_UTIL_ENTITIES_NONE);
342
343/**
344 * replace all XML entitites
345 * This setting will replace <, >, ", ' and &
346 */
347define('XML_SERIALIZER_ENTITIES_XML', XML_UTIL_ENTITIES_XML);
348
349/**
350 * replace only required XML entitites
351 * This setting will replace <, " and &
352 */
353define('XML_SERIALIZER_ENTITIES_XML_REQUIRED', XML_UTIL_ENTITIES_XML_REQUIRED);
354
355/**
356 * replace HTML entitites
357 * @link    http://www.php.net/htmlentities
358 */
359define('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 */
428class 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?>
Note: See TracBrowser for help on using the repository browser.