/easyrest/pear/XML/Unserializer.php

http://easyrest.googlecode.com/ · PHP · 983 lines · 443 code · 100 blank · 440 comment · 83 complexity · 57eb48e9f504206990eb624d5729dcfd MD5 · raw file

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