diff options
author | matthieu_ <matthieu_@59fd770c-687e-43c8-a1e3-f5a4ff64c105> | 2007-08-25 00:27:48 +0400 |
---|---|---|
committer | matthieu_ <matthieu_@59fd770c-687e-43c8-a1e3-f5a4ff64c105> | 2007-08-25 00:27:48 +0400 |
commit | c12bdaa5e95b2f6dba577b3b2c614279f68dc24a (patch) | |
tree | bc955fc3dbc61d4cf2bf0612c770d31c3e6a8666 /libs | |
parent | 8f7f5857cbe329e88b8104c98be3035f79f7fde4 (diff) |
- Translation
- filter to name the columns
- API for the modules actions, provider, UserCountry, Visitfrquency, visitinterest, time,
- HALF of the api for referers
- improved XML export nwo using PEAR xml serializer
Diffstat (limited to 'libs')
-rwxr-xr-x | libs/PEAR.php | 10 | ||||
-rw-r--r-- | libs/XML/Serializer.php | 1025 | ||||
-rw-r--r-- | libs/XML/Unserializer.php | 856 | ||||
-rw-r--r-- | libs/XML/Util.php | 752 |
4 files changed, 2638 insertions, 5 deletions
diff --git a/libs/PEAR.php b/libs/PEAR.php index fc879a0a78..f14d8a06f1 100755 --- a/libs/PEAR.php +++ b/libs/PEAR.php @@ -227,7 +227,7 @@ class PEAR * @return mixed A reference to the variable. If not set it will be * auto initialised to NULL. */ - function &getStaticProperty($class, $var) + static function &getStaticProperty($class, $var) { static $properties; if (!isset($properties[$class])) { @@ -278,7 +278,7 @@ class PEAR */ function isError($data, $code = null) { - if (is_a($data, 'PEAR_Error')) { + if ($data instanceof PEAR_Error) { if (is_null($code)) { return true; } elseif (is_string($code)) { @@ -521,7 +521,7 @@ class PEAR * @see PEAR::setErrorHandling * @since PHP 4.0.5 */ - function &raiseError($message = null, + static function &raiseError($message = null, $code = null, $mode = null, $options = null, @@ -566,10 +566,10 @@ class PEAR $ec = 'PEAR_Error'; } if ($skipmsg) { - $a = &new $ec($code, $mode, $options, $userinfo); + $a = new $ec($code, $mode, $options, $userinfo); return $a; } else { - $a = &new $ec($message, $code, $mode, $options, $userinfo); + $a = new $ec($message, $code, $mode, $options, $userinfo); return $a; } } diff --git a/libs/XML/Serializer.php b/libs/XML/Serializer.php new file mode 100644 index 0000000000..9b2e9f6cfb --- /dev/null +++ b/libs/XML/Serializer.php @@ -0,0 +1,1025 @@ +<?PHP +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * XML_Serializer + * + * Creates XML documents from PHP data structures like arrays, objects or scalars. + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category XML + * @package XML_Serializer + * @author Stephan Schmidt <schst@php.net> + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: Serializer.php,v 1.47 2005/09/30 13:40:30 schst Exp $ + * @link http://pear.php.net/package/XML_Serializer + * @see XML_Unserializer + */ + +/** + * uses PEAR error management + */ +require_once 'PEAR.php'; + +/** + * uses XML_Util to create XML tags + */ +require_once 'XML/Util.php'; + +/** + * option: string used for indentation + * + * Possible values: + * - any string (default is any string) + */ +define('XML_SERIALIZER_OPTION_INDENT', 'indent'); + +/** + * option: string used for linebreaks + * + * Possible values: + * - any string (default is \n) + */ +define('XML_SERIALIZER_OPTION_LINEBREAKS', 'linebreak'); + +/** + * option: enable type hints + * + * Possible values: + * - true + * - false + */ +define('XML_SERIALIZER_OPTION_TYPEHINTS', 'typeHints'); + +/** + * option: add an XML declaration + * + * Possible values: + * - true + * - false + */ +define('XML_SERIALIZER_OPTION_XML_DECL_ENABLED', 'addDecl'); + +/** + * option: encoding of the document + * + * Possible values: + * - any valid encoding + * - null (default) + */ +define('XML_SERIALIZER_OPTION_XML_ENCODING', 'encoding'); + +/** + * option: default name for tags + * + * Possible values: + * - any string (XML_Serializer_Tag is default) + */ +define('XML_SERIALIZER_OPTION_DEFAULT_TAG', 'defaultTagName'); + +/** + * option: use classname for objects in indexed arrays + * + * Possible values: + * - true (default) + * - false + */ +define('XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME', 'classAsTagName'); + +/** + * option: attribute where original key is stored + * + * Possible values: + * - any string (default is _originalKey) + */ +define('XML_SERIALIZER_OPTION_ATTRIBUTE_KEY', 'keyAttribute'); + +/** + * option: attribute for type (only if typeHints => true) + * + * Possible values: + * - any string (default is _type) + */ +define('XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE', 'typeAttribute'); + +/** + * option: attribute for class (only if typeHints => true) + * + * Possible values: + * - any string (default is _class) + */ +define('XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS', 'classAttribute'); + +/** + * option: scalar values (strings, ints,..) will be serialized as attribute + * + * Possible values: + * - true + * - false (default) + * - array which sets this option on a per-tag basis + */ +define('XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES', 'scalarAsAttributes'); + +/** + * option: prepend string for attributes + * + * Possible values: + * - any string (default is any string) + */ +define('XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES', 'prependAttributes'); + +/** + * option: indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column + * + * Possible values: + * - true + * - false (default) + */ +define('XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES', 'indentAttributes'); + +/** + * option: use 'simplexml' to use parent name as tagname if transforming an indexed array + * + * Possible values: + * - XML_SERIALIZER_MODE_DEFAULT (default) + * - XML_SERIALIZER_MODE_SIMPLEXML + */ +define('XML_SERIALIZER_OPTION_MODE', 'mode'); + +/** + * option: add a doctype declaration + * + * Possible values: + * - true + * - false (default) + */ +define('XML_SERIALIZER_OPTION_DOCTYPE_ENABLED', 'addDoctype'); + +/** + * option: supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()} + * + * Possible values: + * - string + * - array + */ +define('XML_SERIALIZER_OPTION_DOCTYPE', 'doctype'); + +/** + * option: name of the root tag + * + * Possible values: + * - string + * - null (default) + */ +define('XML_SERIALIZER_OPTION_ROOT_NAME', 'rootName'); + +/** + * option: attributes of the root tag + * + * Possible values: + * - array + */ +define('XML_SERIALIZER_OPTION_ROOT_ATTRIBS', 'rootAttributes'); + +/** + * option: all values in this key will be treated as attributes + * + * Possible values: + * - array + */ +define('XML_SERIALIZER_OPTION_ATTRIBUTES_KEY', 'attributesArray'); + +/** + * option: this value will be used directly as content, instead of creating a new tag, may only be used in conjuction with attributesArray + * + * Possible values: + * - string + * - null (default) + */ +define('XML_SERIALIZER_OPTION_CONTENT_KEY', 'contentName'); + +/** + * option: this value will be used in a comment, instead of creating a new tag + * + * Possible values: + * - string + * - null (default) + */ +define('XML_SERIALIZER_OPTION_COMMENT_KEY', 'commentName'); + +/** + * option: tag names that will be changed + * + * Possible values: + * - array + */ +define('XML_SERIALIZER_OPTION_TAGMAP', 'tagMap'); + +/** + * option: function that will be applied before serializing + * + * Possible values: + * - any valid PHP callback + */ +define('XML_SERIALIZER_OPTION_ENCODE_FUNC', 'encodeFunction'); + +/** + * option: function that will be applied before serializing + * + * Possible values: + * - string + * - null (default) + */ +define('XML_SERIALIZER_OPTION_NAMESPACE', 'namespace'); + +/** + * option: type of entities to replace + * + * Possible values: + * - XML_SERIALIZER_ENTITIES_NONE + * - XML_SERIALIZER_ENTITIES_XML (default) + * - XML_SERIALIZER_ENTITIES_XML_REQUIRED + * - XML_SERIALIZER_ENTITIES_HTML + */ +define('XML_SERIALIZER_OPTION_ENTITIES', 'replaceEntities'); + +/** + * option: whether to return the result of the serialization from serialize() + * + * Possible values: + * - true + * - false (default) + */ +define('XML_SERIALIZER_OPTION_RETURN_RESULT', 'returnResult'); + +/** + * option: whether to ignore properties that are set to null + * + * Possible values: + * - true + * - false (default) + */ +define('XML_SERIALIZER_OPTION_IGNORE_NULL', 'ignoreNull'); + +/** + * option: whether to use cdata sections for character data + * + * Possible values: + * - true + * - false (default) + */ +define('XML_SERIALIZER_OPTION_CDATA_SECTIONS', 'cdata'); + + +/** + * default mode + */ +define('XML_SERIALIZER_MODE_DEFAULT', 'default'); + +/** + * SimpleXML mode + * + * When serializing indexed arrays, the key of the parent value is used as a tagname. + */ +define('XML_SERIALIZER_MODE_SIMPLEXML', 'simplexml'); + +/** + * error code for no serialization done + */ +define('XML_SERIALIZER_ERROR_NO_SERIALIZATION', 51); + +/** + * do not replace entitites + */ +define('XML_SERIALIZER_ENTITIES_NONE', XML_UTIL_ENTITIES_NONE); + +/** + * replace all XML entitites + * This setting will replace <, >, ", ' and & + */ +define('XML_SERIALIZER_ENTITIES_XML', XML_UTIL_ENTITIES_XML); + +/** + * replace only required XML entitites + * This setting will replace <, " and & + */ +define('XML_SERIALIZER_ENTITIES_XML_REQUIRED', XML_UTIL_ENTITIES_XML_REQUIRED); + +/** + * replace HTML entitites + * @link http://www.php.net/htmlentities + */ +define('XML_SERIALIZER_ENTITIES_HTML', XML_UTIL_ENTITIES_HTML); + +/** + * Creates XML documents from PHP data structures like arrays, objects or scalars. + * + * this class can be used in two modes: + * + * 1. create an XML document from an array or object that is processed by other + * applications. That means, you can create a RDF document from an array in the + * following format: + * + * $data = array( + * 'channel' => array( + * 'title' => 'Example RDF channel', + * 'link' => 'http://www.php-tools.de', + * 'image' => array( + * 'title' => 'Example image', + * 'url' => 'http://www.php-tools.de/image.gif', + * 'link' => 'http://www.php-tools.de' + * ), + * array( + * 'title' => 'Example item', + * 'link' => 'http://example.com' + * ), + * array( + * 'title' => 'Another Example item', + * 'link' => 'http://example.org' + * ) + * ) + * ); + * + * to create a RDF document from this array do the following: + * + * require_once 'XML/Serializer.php'; + * + * $options = array( + * XML_SERIALIZER_OPTION_INDENT => "\t", // indent with tabs + * XML_SERIALIZER_OPTION_LINEBREAKS => "\n", // use UNIX line breaks + * XML_SERIALIZER_OPTION_ROOT_NAME => 'rdf:RDF', // root tag + * XML_SERIALIZER_OPTION_DEFAULT_TAG => 'item' // tag for values with numeric keys + * ); + * + * $serializer = new XML_Serializer($options); + * $rdf = $serializer->serialize($data); + * + * You will get a complete XML document that can be processed like any RDF document. + * + * 2. this classes can be used to serialize any data structure in a way that it can + * later be unserialized again. + * XML_Serializer will store the type of the value and additional meta information + * in attributes of the surrounding tag. This meat information can later be used + * to restore the original data structure in PHP. If you want XML_Serializer + * to add meta information to the tags, add + * + * XML_SERIALIZER_OPTION_TYPEHINTS => true + * + * to the options array in the constructor. + * + * @category XML + * @package XML_Serializer + * @author Stephan Schmidt <schst@php.net> + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/XML_Serializer + * @see XML_Unserializer + */ +class XML_Serializer extends PEAR +{ + /** + * list of all available options + * + * @access private + * @var array + */ + var $_knownOptions = array( + XML_SERIALIZER_OPTION_INDENT, + XML_SERIALIZER_OPTION_LINEBREAKS, + XML_SERIALIZER_OPTION_TYPEHINTS, + XML_SERIALIZER_OPTION_XML_DECL_ENABLED, + XML_SERIALIZER_OPTION_XML_ENCODING, + XML_SERIALIZER_OPTION_DEFAULT_TAG, + XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME, + XML_SERIALIZER_OPTION_ATTRIBUTE_KEY, + XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE, + XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS, + XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES, + XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES, + XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES, + XML_SERIALIZER_OPTION_MODE, + XML_SERIALIZER_OPTION_DOCTYPE_ENABLED, + XML_SERIALIZER_OPTION_DOCTYPE, + XML_SERIALIZER_OPTION_ROOT_NAME, + XML_SERIALIZER_OPTION_ROOT_ATTRIBS, + XML_SERIALIZER_OPTION_ATTRIBUTES_KEY, + XML_SERIALIZER_OPTION_CONTENT_KEY, + XML_SERIALIZER_OPTION_COMMENT_KEY, + XML_SERIALIZER_OPTION_TAGMAP, + XML_SERIALIZER_OPTION_ENCODE_FUNC, + XML_SERIALIZER_OPTION_NAMESPACE, + XML_SERIALIZER_OPTION_ENTITIES, + XML_SERIALIZER_OPTION_RETURN_RESULT, + XML_SERIALIZER_OPTION_IGNORE_NULL, + XML_SERIALIZER_OPTION_CDATA_SECTIONS + ); + + /** + * default options for the serialization + * + * @access private + * @var array + */ + var $_defaultOptions = array( + XML_SERIALIZER_OPTION_INDENT => '', // string used for indentation + XML_SERIALIZER_OPTION_LINEBREAKS => "\n", // string used for newlines + XML_SERIALIZER_OPTION_TYPEHINTS => false, // automatically add type hin attributes + XML_SERIALIZER_OPTION_XML_DECL_ENABLED => false, // add an XML declaration + XML_SERIALIZER_OPTION_XML_ENCODING => null, // encoding specified in the XML declaration + XML_SERIALIZER_OPTION_DEFAULT_TAG => 'XML_Serializer_Tag', // tag used for indexed arrays or invalid names + XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME => false, // use classname for objects in indexed arrays + XML_SERIALIZER_OPTION_ATTRIBUTE_KEY => '_originalKey', // attribute where original key is stored + XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE => '_type', // attribute for type (only if typeHints => true) + XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS => '_class', // attribute for class of objects (only if typeHints => true) + XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES => false, // scalar values (strings, ints,..) will be serialized as attribute + XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES => '', // prepend string for attributes + XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES => false, // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column + XML_SERIALIZER_OPTION_MODE => XML_SERIALIZER_MODE_DEFAULT, // use XML_SERIALIZER_MODE_SIMPLEXML to use parent name as tagname if transforming an indexed array + XML_SERIALIZER_OPTION_DOCTYPE_ENABLED => false, // add a doctype declaration + XML_SERIALIZER_OPTION_DOCTYPE => null, // supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()} + XML_SERIALIZER_OPTION_ROOT_NAME => null, // name of the root tag + XML_SERIALIZER_OPTION_ROOT_ATTRIBS => array(), // attributes of the root tag + XML_SERIALIZER_OPTION_ATTRIBUTES_KEY => null, // all values in this key will be treated as attributes + XML_SERIALIZER_OPTION_CONTENT_KEY => null, // this value will be used directly as content, instead of creating a new tag, may only be used in conjuction with attributesArray + XML_SERIALIZER_OPTION_COMMENT_KEY => null, // this value will be used directly as comment, instead of creating a new tag, may only be used in conjuction with attributesArray + XML_SERIALIZER_OPTION_TAGMAP => array(), // tag names that will be changed + XML_SERIALIZER_OPTION_ENCODE_FUNC => null, // function that will be applied before serializing + XML_SERIALIZER_OPTION_NAMESPACE => null, // namespace to use + XML_SERIALIZER_OPTION_ENTITIES => XML_SERIALIZER_ENTITIES_XML, // type of entities to replace, + XML_SERIALIZER_OPTION_RETURN_RESULT => false, // serialize() returns the result of the serialization instead of true + XML_SERIALIZER_OPTION_IGNORE_NULL => false, // ignore properties that are set to null + XML_SERIALIZER_OPTION_CDATA_SECTIONS => false // Whether to use cdata sections for plain character data + ); + + /** + * options for the serialization + * + * @access public + * @var array + */ + var $options = array(); + + /** + * current tag depth + * + * @access private + * @var integer + */ + var $_tagDepth = 0; + + /** + * serilialized representation of the data + * + * @access private + * @var string + */ + var $_serializedData = null; + + /** + * constructor + * + * @access public + * @param mixed $options array containing options for the serialization + */ + function XML_Serializer( $options = null ) + { + $this->PEAR(); + if (is_array($options)) { + $this->options = array_merge($this->_defaultOptions, $options); + } else { + $this->options = $this->_defaultOptions; + } + } + + /** + * return API version + * + * @access public + * @static + * @return string $version API version + */ + function apiVersion() + { + return '@package_version@'; + } + + /** + * reset all options to default options + * + * @access public + * @see setOption(), XML_Serializer() + */ + function resetOptions() + { + $this->options = $this->_defaultOptions; + } + + /** + * set an option + * + * You can use this method if you do not want to set all options in the constructor + * + * @access public + * @see resetOption(), XML_Serializer() + */ + function setOption($name, $value) + { + $this->options[$name] = $value; + } + + /** + * sets several options at once + * + * You can use this method if you do not want to set all options in the constructor + * + * @access public + * @see resetOption(), XML_Unserializer(), setOption() + */ + function setOptions($options) + { + $this->options = array_merge($this->options, $options); + } + + /** + * serialize data + * + * @access public + * @param mixed $data data to serialize + * @return boolean true on success, pear error on failure + */ + function serialize($data, $options = null) + { + // if options have been specified, use them instead + // of the previously defined ones + if (is_array($options)) { + $optionsBak = $this->options; + if (isset($options['overrideOptions']) && $options['overrideOptions'] == true) { + $this->options = array_merge($this->_defaultOptions, $options); + } else { + $this->options = array_merge($this->options, $options); + } + } else { + $optionsBak = null; + } + + // start depth is zero + $this->_tagDepth = 0; + + $rootAttributes = $this->options[XML_SERIALIZER_OPTION_ROOT_ATTRIBS]; + if (isset($this->options[XML_SERIALIZER_OPTION_NAMESPACE]) && is_array($this->options[XML_SERIALIZER_OPTION_NAMESPACE])) { + $rootAttributes['xmlns:'.$this->options[XML_SERIALIZER_OPTION_NAMESPACE][0]] = $this->options[XML_SERIALIZER_OPTION_NAMESPACE][1]; + } + + $this->_serializedData = ''; + // serialize an array + if (is_array($data)) { + if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) { + $tagName = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME]; + } else { + $tagName = 'array'; + } + + $this->_serializedData .= $this->_serializeArray($data, $tagName, $rootAttributes); + } elseif (is_object($data)) { + // serialize an object + if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) { + $tagName = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME]; + } else { + $tagName = get_class($data); + } + $this->_serializedData .= $this->_serializeObject($data, $tagName, $rootAttributes); + } else { + $tag = array(); + if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) { + $tag['qname'] = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME]; + } else { + $tag['qname'] = gettype($data); + } + if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) { + $rootAttributes[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = gettype($data); + } + @settype($data, 'string'); + $tag['content'] = $data; + $tag['attributes'] = $rootAttributes; + $this->_serializedData = $this->_createXMLTag($tag); + } + + // add doctype declaration + if ($this->options[XML_SERIALIZER_OPTION_DOCTYPE_ENABLED] === true) { + $this->_serializedData = XML_Util::getDoctypeDeclaration($tagName, $this->options[XML_SERIALIZER_OPTION_DOCTYPE]) + . $this->options[XML_SERIALIZER_OPTION_LINEBREAKS] + . $this->_serializedData; + } + + // build xml declaration + if ($this->options[XML_SERIALIZER_OPTION_XML_DECL_ENABLED]) { + $atts = array(); + $this->_serializedData = XML_Util::getXMLDeclaration('1.0', $this->options[XML_SERIALIZER_OPTION_XML_ENCODING]) + . $this->options[XML_SERIALIZER_OPTION_LINEBREAKS] + . $this->_serializedData; + } + + if ($this->options[XML_SERIALIZER_OPTION_RETURN_RESULT] === true) { + $result = $this->_serializedData; + } else { + $result = true; + } + + if ($optionsBak !== null) { + $this->options = $optionsBak; + } + + return $result; + } + + /** + * get the result of the serialization + * + * @access public + * @return string serialized XML + */ + function getSerializedData() + { + if ($this->_serializedData == null) { + return $this->raiseError('No serialized data available. Use XML_Serializer::serialize() first.', XML_SERIALIZER_ERROR_NO_SERIALIZATION); + } + return $this->_serializedData; + } + + /** + * serialize any value + * + * This method checks for the type of the value and calls the appropriate method + * + * @access private + * @param mixed $value + * @param string $tagName + * @param array $attributes + * @return string + */ + function _serializeValue($value, $tagName = null, $attributes = array()) + { + if (is_array($value)) { + $xml = $this->_serializeArray($value, $tagName, $attributes); + } elseif (is_object($value)) { + $xml = $this->_serializeObject($value, $tagName); + } else { + $tag = array( + 'qname' => $tagName, + 'attributes' => $attributes, + 'content' => $value + ); + $xml = $this->_createXMLTag($tag); + } + return $xml; + } + + /** + * serialize an array + * + * @access private + * @param array $array array to serialize + * @param string $tagName name of the root tag + * @param array $attributes attributes for the root tag + * @return string $string serialized data + * @uses XML_Util::isValidName() to check, whether key has to be substituted + */ + function _serializeArray(&$array, $tagName = null, $attributes = array()) + { + $_content = null; + $_comment = null; + + // check for comment + if ($this->options[XML_SERIALIZER_OPTION_COMMENT_KEY] !== null) { + if (isset($array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]])) { + $_comment = $array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]]; + unset($array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]]); + } + } + + /** + * check for special attributes + */ + if ($this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY] !== null) { + if (isset($array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]])) { + $attributes = $array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]]; + unset($array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]]); + } + /** + * check for special content + */ + if ($this->options[XML_SERIALIZER_OPTION_CONTENT_KEY] !== null) { + if (isset($array[$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]])) { + $_content = $array[$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]]; + unset($array[$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]]); + } + } + } + + if ($this->options[XML_SERIALIZER_OPTION_IGNORE_NULL] === true) { + foreach (array_keys($array) as $key) { + if (is_null($array[$key])) { + unset($array[$key]); + } + } + } + + /* + * if mode is set to simpleXML, check whether + * the array is associative or indexed + */ + if (is_array($array) && !empty($array) && $this->options[XML_SERIALIZER_OPTION_MODE] == XML_SERIALIZER_MODE_SIMPLEXML) { + $indexed = true; + foreach ($array as $key => $val) { + if (!is_int($key)) { + $indexed = false; + break; + } + } + + if ($indexed && $this->options[XML_SERIALIZER_OPTION_MODE] == XML_SERIALIZER_MODE_SIMPLEXML) { + $string = ''; + foreach ($array as $key => $val) { + $string .= $this->_serializeValue( $val, $tagName, $attributes); + + $string .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]; + // do indentation + if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null && $this->_tagDepth>0) { + $string .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth); + } + } + return rtrim($string); + } + } + + $scalarAsAttributes = false; + if (is_array($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES]) && isset($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES][$tagName])) { + $scalarAsAttributes = $this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES][$tagName]; + } elseif ($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES] === true) { + $scalarAsAttributes = true; + } + + if ($scalarAsAttributes === true) { + $this->expectError('*'); + foreach ($array as $key => $value) { + if (is_scalar($value) && (XML_Util::isValidName($key) === true)) { + unset($array[$key]); + $attributes[$this->options[XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES].$key] = $value; + } + } + $this->popExpect(); + } elseif (is_array($scalarAsAttributes)) { + $this->expectError('*'); + foreach ($scalarAsAttributes as $key) { + if (!isset($array[$key])) { + continue; + } + $value = $array[$key]; + if (is_scalar($value) && (XML_Util::isValidName($key) === true)) { + unset($array[$key]); + $attributes[$this->options[XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES].$key] = $value; + } + } + $this->popExpect(); + } + + // check for empty array => create empty tag + if (empty($array)) { + $tag = array( + 'qname' => $tagName, + 'content' => $_content, + 'attributes' => $attributes + ); + } else { + $this->_tagDepth++; + $tmp = $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]; + foreach ($array as $key => $value) { + // do indentation + if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null && $this->_tagDepth>0) { + $tmp .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth); + } + + if (isset($this->options[XML_SERIALIZER_OPTION_TAGMAP][$key])) { + $key = $this->options[XML_SERIALIZER_OPTION_TAGMAP][$key]; + } + + // copy key + $origKey = $key; + $this->expectError('*'); + // key cannot be used as tagname => use default tag + $valid = XML_Util::isValidName($key); + $this->popExpect(); + if (PEAR::isError($valid)) { + if ($this->options[XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME] && is_object($value)) { + $key = get_class($value); + } else { + $key = $this->_getDefaultTagname($tagName); + } + } + $atts = array(); + if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) { + $atts[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = gettype($value); + if ($key !== $origKey) { + $atts[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_KEY]] = (string)$origKey; + } + } + + $tmp .= $this->_createXMLTag(array( + 'qname' => $key, + 'attributes' => $atts, + 'content' => $value ) + ); + $tmp .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]; + } + + $this->_tagDepth--; + if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null && $this->_tagDepth>0) { + $tmp .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth); + } + + if (trim($tmp) === '') { + $tmp = null; + } + + $tag = array( + 'qname' => $tagName, + 'content' => $tmp, + 'attributes' => $attributes + ); + } + if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) { + if (!isset($tag['attributes'][$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]])) { + $tag['attributes'][$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = 'array'; + } + } + + $string = ''; + if (!is_null($_comment)) { + $string .= XML_Util::createComment($_comment); + $string .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]; + if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null && $this->_tagDepth>0) { + $string .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth); + } + } + $string .= $this->_createXMLTag($tag, false); + return $string; + } + + /** + * get the name of the default tag. + * + * The name of the parent tag needs to be passed as the + * default name can depend on the context. + * + * @param string name of the parent tag + * @return string default tag name + */ + function _getDefaultTagname($parent) + { + if (is_string($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG])) { + return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]; + } + if (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG][$parent])) { + return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG][$parent]; + } elseif (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['#default'])) { + return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['#default']; + } elseif (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['__default'])) { + // keep this for BC + return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['__default']; + } + return 'XML_Serializer_Tag'; + } + + /** + * serialize an object + * + * @access private + * @param object $object object to serialize + * @return string $string serialized data + */ + function _serializeObject(&$object, $tagName = null, $attributes = array()) + { + // check for magic function + if (method_exists($object, '__sleep')) { + $propNames = $object->__sleep(); + if (is_array($propNames)) { + $properties = array(); + foreach ($propNames as $propName) { + $properties[$propName] = $object->$propName; + } + } else { + $properties = get_object_vars($object); + } + } else { + $properties = get_object_vars($object); + } + + if (empty($tagName)) { + $tagName = get_class($object); + } + + // typehints activated? + if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) { + $attributes[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = 'object'; + $attributes[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS]] = get_class($object); + } + $string = $this->_serializeArray($properties, $tagName, $attributes); + return $string; + } + + /** + * create a tag from an array + * this method awaits an array in the following format + * array( + * 'qname' => $tagName, + * 'attributes' => array(), + * 'content' => $content, // optional + * 'namespace' => $namespace // optional + * 'namespaceUri' => $namespaceUri // optional + * ) + * + * @access private + * @param array $tag tag definition + * @param boolean $replaceEntities whether to replace XML entities in content or not + * @return string $string XML tag + */ + function _createXMLTag($tag, $firstCall = true) + { + // build fully qualified tag name + if ($this->options[XML_SERIALIZER_OPTION_NAMESPACE] !== null) { + if (is_array($this->options[XML_SERIALIZER_OPTION_NAMESPACE])) { + $tag['qname'] = $this->options[XML_SERIALIZER_OPTION_NAMESPACE][0] . ':' . $tag['qname']; + } else { + $tag['qname'] = $this->options[XML_SERIALIZER_OPTION_NAMESPACE] . ':' . $tag['qname']; + } + } + + // attribute indentation + if ($this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES] !== false) { + $multiline = true; + $indent = str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth); + + if ($this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES] == '_auto') { + $indent .= str_repeat(' ', (strlen($tag['qname'])+2)); + + } else { + $indent .= $this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES]; + } + } else { + $multiline = false; + $indent = false; + } + + if (is_array($tag['content'])) { + if (empty($tag['content'])) { + $tag['content'] = ''; + } + } elseif(is_scalar($tag['content']) && (string)$tag['content'] == '') { + $tag['content'] = ''; + } + + // replace XML entities (only needed, if this is not a nested call) + if ($firstCall === true) { + if ($this->options[XML_SERIALIZER_OPTION_CDATA_SECTIONS] === true) { + $replaceEntities = XML_UTIL_CDATA_SECTION; + } else { + $replaceEntities = $this->options[XML_SERIALIZER_OPTION_ENTITIES]; + } + } else { + $replaceEntities = XML_SERIALIZER_ENTITIES_NONE; + } + if (is_scalar($tag['content']) || is_null($tag['content'])) { + if ($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC]) { + if ($firstCall === true) { + $tag['content'] = call_user_func($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['content']); + } + $tag['attributes'] = array_map($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['attributes']); + } + $tag = XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]); + } elseif (is_array($tag['content'])) { + $tag = $this->_serializeArray($tag['content'], $tag['qname'], $tag['attributes']); + } elseif (is_object($tag['content'])) { + $tag = $this->_serializeObject($tag['content'], $tag['qname'], $tag['attributes']); + } elseif (is_resource($tag['content'])) { + settype($tag['content'], 'string'); + if ($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC]) { + if ($replaceEntities === true) { + $tag['content'] = call_user_func($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['content']); + } + $tag['attributes'] = array_map($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['attributes']); + } + $tag = XML_Util::createTagFromArray($tag, $replaceEntities); + } + return $tag; + } +} +?>
\ No newline at end of file diff --git a/libs/XML/Unserializer.php b/libs/XML/Unserializer.php new file mode 100644 index 0000000000..a5a03dcfc6 --- /dev/null +++ b/libs/XML/Unserializer.php @@ -0,0 +1,856 @@ +<?PHP +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * XML_Unserializer + * + * Parses any XML document into PHP data structures. + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category XML + * @package XML_Serializer + * @author Stephan Schmidt <schst@php.net> + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: Unserializer.php,v 1.39 2005/09/28 11:19:56 schst Exp $ + * @link http://pear.php.net/package/XML_Serializer + * @see XML_Unserializer + */ + +/** + * uses PEAR error managemt + */ +require_once 'PEAR.php'; + +/** + * uses XML_Parser to unserialize document + */ +require_once 'XML/Parser.php'; + +/** + * option: Convert nested tags to array or object + * + * Possible values: + * - array + * - object + * - associative array to define this option per tag name + */ +define('XML_UNSERIALIZER_OPTION_COMPLEXTYPE', 'complexType'); + +/** + * option: Name of the attribute that stores the original key + * + * Possible values: + * - any string + */ +define('XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY', 'keyAttribute'); + +/** + * option: Name of the attribute that stores the type + * + * Possible values: + * - any string + */ +define('XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE', 'typeAttribute'); + +/** + * option: Name of the attribute that stores the class name + * + * Possible values: + * - any string + */ +define('XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS', 'classAttribute'); + +/** + * option: Whether to use the tag name as a class name + * + * Possible values: + * - true or false + */ +define('XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME', 'tagAsClass'); + +/** + * option: Name of the default class + * + * Possible values: + * - any string + */ +define('XML_UNSERIALIZER_OPTION_DEFAULT_CLASS', 'defaultClass'); + +/** + * option: Whether to parse attributes + * + * Possible values: + * - true or false + */ +define('XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE', 'parseAttributes'); + +/** + * option: Key of the array to store attributes (if any) + * + * Possible values: + * - any string + * - false (disabled) + */ +define('XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY', 'attributesArray'); + +/** + * option: string to prepend attribute name (if any) + * + * Possible values: + * - any string + * - false (disabled) + */ +define('XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND', 'prependAttributes'); + +/** + * option: key to store the content, if XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE is used + * + * Possible values: + * - any string + */ +define('XML_UNSERIALIZER_OPTION_CONTENT_KEY', 'contentName'); + +/** + * option: map tag names + * + * Possible values: + * - associative array + */ +define('XML_UNSERIALIZER_OPTION_TAG_MAP', 'tagMap'); + +/** + * option: list of tags that will always be enumerated + * + * Possible values: + * - indexed array + */ +define('XML_UNSERIALIZER_OPTION_FORCE_ENUM', 'forceEnum'); + +/** + * option: Encoding of the XML document + * + * Possible values: + * - UTF-8 + * - ISO-8859-1 + */ +define('XML_UNSERIALIZER_OPTION_ENCODING_SOURCE', 'encoding'); + +/** + * option: Desired target encoding of the data + * + * Possible values: + * - UTF-8 + * - ISO-8859-1 + */ +define('XML_UNSERIALIZER_OPTION_ENCODING_TARGET', 'targetEncoding'); + +/** + * option: Callback that will be applied to textual data + * + * Possible values: + * - any valid PHP callback + */ +define('XML_UNSERIALIZER_OPTION_DECODE_FUNC', 'decodeFunction'); + +/** + * option: whether to return the result of the unserialization from unserialize() + * + * Possible values: + * - true + * - false (default) + */ +define('XML_UNSERIALIZER_OPTION_RETURN_RESULT', 'returnResult'); + +/** + * option: set the whitespace behaviour + * + * Possible values: + * - XML_UNSERIALIZER_WHITESPACE_KEEP + * - XML_UNSERIALIZER_WHITESPACE_TRIM + * - XML_UNSERIALIZER_WHITESPACE_NORMALIZE + */ +define('XML_UNSERIALIZER_OPTION_WHITESPACE', 'whitespace'); + +/** + * Keep all whitespace + */ +define('XML_UNSERIALIZER_WHITESPACE_KEEP', 'keep'); + +/** + * remove whitespace from start and end of the data + */ +define('XML_UNSERIALIZER_WHITESPACE_TRIM', 'trim'); + +/** + * normalize whitespace + */ +define('XML_UNSERIALIZER_WHITESPACE_NORMALIZE', 'normalize'); + +/** + * option: whether to ovverride all options that have been set before + * + * Possible values: + * - true + * - false (default) + */ +define('XML_UNSERIALIZER_OPTION_OVERRIDE_OPTIONS', 'overrideOptions'); + +/** + * option: list of tags, that will not be used as keys + */ +define('XML_UNSERIALIZER_OPTION_IGNORE_KEYS', 'ignoreKeys'); + +/** + * option: whether to use type guessing for scalar values + */ +define('XML_UNSERIALIZER_OPTION_GUESS_TYPES', 'guessTypes'); + +/** + * error code for no serialization done + */ +define('XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION', 151); + +/** + * XML_Unserializer + * + * class to unserialize XML documents that have been created with + * XML_Serializer. To unserialize an XML document you have to add + * type hints to the XML_Serializer options. + * + * If no type hints are available, XML_Unserializer will guess how + * the tags should be treated, that means complex structures will be + * arrays and tags with only CData in them will be strings. + * + * <code> + * require_once 'XML/Unserializer.php'; + * + * // be careful to always use the ampersand in front of the new operator + * $unserializer = &new XML_Unserializer(); + * + * $unserializer->unserialize($xml); + * + * $data = $unserializer->getUnserializedData(); + * <code> + * + * + * @category XML + * @package XML_Serializer + * @author Stephan Schmidt <schst@php.net> + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/XML_Serializer + * @see XML_Serializer + */ +class XML_Unserializer extends PEAR +{ + /** + * list of all available options + * + * @access private + * @var array + */ + var $_knownOptions = array( + XML_UNSERIALIZER_OPTION_COMPLEXTYPE, + XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY, + XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE, + XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS, + XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME, + XML_UNSERIALIZER_OPTION_DEFAULT_CLASS, + XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE, + XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY, + XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND, + XML_UNSERIALIZER_OPTION_CONTENT_KEY, + XML_UNSERIALIZER_OPTION_TAG_MAP, + XML_UNSERIALIZER_OPTION_FORCE_ENUM, + XML_UNSERIALIZER_OPTION_ENCODING_SOURCE, + XML_UNSERIALIZER_OPTION_ENCODING_TARGET, + XML_UNSERIALIZER_OPTION_DECODE_FUNC, + XML_UNSERIALIZER_OPTION_RETURN_RESULT, + XML_UNSERIALIZER_OPTION_WHITESPACE, + XML_UNSERIALIZER_OPTION_IGNORE_KEYS, + XML_UNSERIALIZER_OPTION_GUESS_TYPES + ); + /** + * default options for the serialization + * + * @access private + * @var array + */ + var $_defaultOptions = array( + XML_UNSERIALIZER_OPTION_COMPLEXTYPE => 'array', // complex types will be converted to arrays, if no type hint is given + XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY => '_originalKey', // get array key/property name from this attribute + XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE => '_type', // get type from this attribute + XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS => '_class', // get class from this attribute (if not given, use tag name) + XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME => true, // use the tagname as the classname + XML_UNSERIALIZER_OPTION_DEFAULT_CLASS => 'stdClass', // name of the class that is used to create objects + XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE => false, // parse the attributes of the tag into an array + XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY => false, // parse them into sperate array (specify name of array here) + XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND => '', // prepend attribute names with this string + XML_UNSERIALIZER_OPTION_CONTENT_KEY => '_content', // put cdata found in a tag that has been converted to a complex type in this key + XML_UNSERIALIZER_OPTION_TAG_MAP => array(), // use this to map tagnames + XML_UNSERIALIZER_OPTION_FORCE_ENUM => array(), // these tags will always be an indexed array + XML_UNSERIALIZER_OPTION_ENCODING_SOURCE => null, // specify the encoding character of the document to parse + XML_UNSERIALIZER_OPTION_ENCODING_TARGET => null, // specify the target encoding + XML_UNSERIALIZER_OPTION_DECODE_FUNC => null, // function used to decode data + XML_UNSERIALIZER_OPTION_RETURN_RESULT => false, // unserialize() returns the result of the unserialization instead of true + XML_UNSERIALIZER_OPTION_WHITESPACE => XML_UNSERIALIZER_WHITESPACE_TRIM, // remove whitespace around data + XML_UNSERIALIZER_OPTION_IGNORE_KEYS => array(), // List of tags that will automatically be added to the parent, instead of adding a new key + XML_UNSERIALIZER_OPTION_GUESS_TYPES => false // Whether to use type guessing + ); + + /** + * current options for the serialization + * + * @access public + * @var array + */ + var $options = array(); + + /** + * unserialized data + * + * @access private + * @var string + */ + var $_unserializedData = null; + + /** + * name of the root tag + * + * @access private + * @var string + */ + var $_root = null; + + /** + * stack for all data that is found + * + * @access private + * @var array + */ + var $_dataStack = array(); + + /** + * stack for all values that are generated + * + * @access private + * @var array + */ + var $_valStack = array(); + + /** + * current tag depth + * + * @access private + * @var int + */ + var $_depth = 0; + + /** + * XML_Parser instance + * + * @access private + * @var object XML_Parser + */ + var $_parser = null; + + /** + * constructor + * + * @access public + * @param mixed $options array containing options for the unserialization + */ + function XML_Unserializer($options = null) + { + if (is_array($options)) { + $this->options = array_merge($this->_defaultOptions, $options); + } else { + $this->options = $this->_defaultOptions; + } + } + + /** + * return API version + * + * @access public + * @static + * @return string $version API version + */ + function apiVersion() + { + return '@package_version@'; + } + + /** + * reset all options to default options + * + * @access public + * @see setOption(), XML_Unserializer(), setOptions() + */ + function resetOptions() + { + $this->options = $this->_defaultOptions; + } + + /** + * set an option + * + * You can use this method if you do not want to set all options in the constructor + * + * @access public + * @see resetOption(), XML_Unserializer(), setOptions() + */ + function setOption($name, $value) + { + $this->options[$name] = $value; + } + + /** + * sets several options at once + * + * You can use this method if you do not want to set all options in the constructor + * + * @access public + * @see resetOption(), XML_Unserializer(), setOption() + */ + function setOptions($options) + { + $this->options = array_merge($this->options, $options); + } + + /** + * unserialize data + * + * @access public + * @param mixed $data data to unserialize (string, filename or resource) + * @param boolean $isFile data should be treated as a file + * @param array $options options that will override the global options for this call + * @return boolean $success + */ + function unserialize($data, $isFile = false, $options = null) + { + $this->_unserializedData = null; + $this->_root = null; + + // if options have been specified, use them instead + // of the previously defined ones + if (is_array($options)) { + $optionsBak = $this->options; + if (isset($options[XML_UNSERIALIZER_OPTION_OVERRIDE_OPTIONS]) && $options[XML_UNSERIALIZER_OPTION_OVERRIDE_OPTIONS] == true) { + $this->options = array_merge($this->_defaultOptions, $options); + } else { + $this->options = array_merge($this->options, $options); + } + } else { + $optionsBak = null; + } + + $this->_valStack = array(); + $this->_dataStack = array(); + $this->_depth = 0; + + $this->_createParser(); + + if (is_string($data)) { + if ($isFile) { + $result = $this->_parser->setInputFile($data); + if (PEAR::isError($result)) { + return $result; + } + $result = $this->_parser->parse(); + } else { + $result = $this->_parser->parseString($data,true); + } + } else { + $this->_parser->setInput($data); + $result = $this->_parser->parse(); + } + + if ($this->options[XML_UNSERIALIZER_OPTION_RETURN_RESULT] === true) { + $return = $this->_unserializedData; + } else { + $return = true; + } + + if ($optionsBak !== null) { + $this->options = $optionsBak; + } + + if (PEAR::isError($result)) { + return $result; + } + + return $return; + } + + /** + * get the result of the serialization + * + * @access public + * @return string $serializedData + */ + function getUnserializedData() + { + if ($this->_root === null) { + return $this->raiseError('No unserialized data available. Use XML_Unserializer::unserialize() first.', XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION); + } + return $this->_unserializedData; + } + + /** + * get the name of the root tag + * + * @access public + * @return string $rootName + */ + function getRootName() + { + if ($this->_root === null) { + return $this->raiseError('No unserialized data available. Use XML_Unserializer::unserialize() first.', XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION); + } + return $this->_root; + } + + /** + * Start element handler for XML parser + * + * @access private + * @param object $parser XML parser object + * @param string $element XML element + * @param array $attribs attributes of XML tag + * @return void + */ + function startHandler($parser, $element, $attribs) + { + if (isset($attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE]])) { + $type = $attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE]]; + $guessType = false; + } else { + $type = 'string'; + if ($this->options[XML_UNSERIALIZER_OPTION_GUESS_TYPES] === true) { + $guessType = true; + } else { + $guessType = false; + } + } + + if ($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC] !== null) { + $attribs = array_map($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC], $attribs); + } + + $this->_depth++; + $this->_dataStack[$this->_depth] = null; + + if (is_array($this->options[XML_UNSERIALIZER_OPTION_TAG_MAP]) && isset($this->options[XML_UNSERIALIZER_OPTION_TAG_MAP][$element])) { + $element = $this->options[XML_UNSERIALIZER_OPTION_TAG_MAP][$element]; + } + + $val = array( + 'name' => $element, + 'value' => null, + 'type' => $type, + 'guessType' => $guessType, + 'childrenKeys' => array(), + 'aggregKeys' => array() + ); + + if ($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE] == true && (count($attribs) > 0)) { + $val['children'] = array(); + $val['type'] = $this->_getComplexType($element); + $val['class'] = $element; + + if ($this->options[XML_UNSERIALIZER_OPTION_GUESS_TYPES] === true) { + $attribs = $this->_guessAndSetTypes($attribs); + } + if ($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY] != false) { + $val['children'][$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY]] = $attribs; + } else { + foreach ($attribs as $attrib => $value) { + $val['children'][$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND].$attrib] = $value; + } + } + } + + $keyAttr = false; + + if (is_string($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY])) { + $keyAttr = $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]; + } elseif (is_array($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY])) { + if (isset($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY][$element])) { + $keyAttr = $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY][$element]; + } elseif (isset($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]['#default'])) { + $keyAttr = $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]['#default']; + } elseif (isset($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]['__default'])) { + // keep this for BC + $keyAttr = $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]['__default']; + } + } + + if ($keyAttr !== false && isset($attribs[$keyAttr])) { + $val['name'] = $attribs[$keyAttr]; + } + + if (isset($attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS]])) { + $val['class'] = $attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS]]; + } + + array_push($this->_valStack, $val); + } + + /** + * Try to guess the type of several values and + * set them accordingly + * + * @access private + * @param array array, containing the values + * @return array array, containing the values with their correct types + */ + function _guessAndSetTypes($array) + { + foreach ($array as $key => $value) { + $array[$key] = $this->_guessAndSetType($value); + } + return $array; + } + + /** + * Try to guess the type of a value and + * set it accordingly + * + * @access private + * @param string character data + * @return mixed value with the best matching type + */ + function _guessAndSetType($value) + { + if ($value === 'true') { + return true; + } + if ($value === 'false') { + return false; + } + if ($value === 'NULL') { + return null; + } + if (preg_match('/^[-+]?[0-9]{1,}$/', $value)) { + return intval($value); + } + if (preg_match('/^[-+]?[0-9]{1,}\.[0-9]{1,}$/', $value)) { + return doubleval($value); + } + return (string)$value; + } + + /** + * End element handler for XML parser + * + * @access private + * @param object XML parser object + * @param string + * @return void + */ + function endHandler($parser, $element) + { + $value = array_pop($this->_valStack); + switch ($this->options[XML_UNSERIALIZER_OPTION_WHITESPACE]) { + case XML_UNSERIALIZER_WHITESPACE_KEEP: + $data = $this->_dataStack[$this->_depth]; + break; + case XML_UNSERIALIZER_WHITESPACE_NORMALIZE: + $data = trim(preg_replace('/\s\s+/m', ' ', $this->_dataStack[$this->_depth])); + break; + case XML_UNSERIALIZER_WHITESPACE_TRIM: + default: + $data = trim($this->_dataStack[$this->_depth]); + break; + } + + // adjust type of the value + switch(strtolower($value['type'])) { + + // unserialize an object + case 'object': + if (isset($value['class'])) { + $classname = $value['class']; + } else { + $classname = ''; + } + // instantiate the class + if ($this->options[XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME] === true && class_exists($classname)) { + $value['value'] = &new $classname; + } else { + $value['value'] = &new $this->options[XML_UNSERIALIZER_OPTION_DEFAULT_CLASS]; + } + if (trim($data) !== '') { + if ($value['guessType'] === true) { + $data = $this->_guessAndSetType($data); + } + $value['children'][$this->options[XML_UNSERIALIZER_OPTION_CONTENT_KEY]] = $data; + } + + // set properties + foreach ($value['children'] as $prop => $propVal) { + // check whether there is a special method to set this property + $setMethod = 'set'.$prop; + if (method_exists($value['value'], $setMethod)) { + call_user_func(array(&$value['value'], $setMethod), $propVal); + } else { + $value['value']->$prop = $propVal; + } + } + // check for magic function + if (method_exists($value['value'], '__wakeup')) { + $value['value']->__wakeup(); + } + break; + + // unserialize an array + case 'array': + if (trim($data) !== '') { + if ($value['guessType'] === true) { + $data = $this->_guessAndSetType($data); + } + $value['children'][$this->options[XML_UNSERIALIZER_OPTION_CONTENT_KEY]] = $data; + } + if (isset($value['children'])) { + $value['value'] = $value['children']; + } else { + $value['value'] = array(); + } + break; + + // unserialize a null value + case 'null': + $data = null; + break; + + // unserialize a resource => this is not possible :-( + case 'resource': + $value['value'] = $data; + break; + + // unserialize any scalar value + default: + if ($value['guessType'] === true) { + $data = $this->_guessAndSetType($data); + } else { + settype($data, $value['type']); + } + + $value['value'] = $data; + break; + } + $parent = array_pop($this->_valStack); + if ($parent === null) { + $this->_unserializedData = &$value['value']; + $this->_root = &$value['name']; + return true; + } else { + // parent has to be an array + if (!isset($parent['children']) || !is_array($parent['children'])) { + $parent['children'] = array(); + if (!in_array($parent['type'], array('array', 'object'))) { + $parent['type'] = $this->_getComplexType($parent['name']); + if ($parent['type'] == 'object') { + $parent['class'] = $parent['name']; + } + } + } + + if (in_array($element, $this->options[XML_UNSERIALIZER_OPTION_IGNORE_KEYS])) { + $ignoreKey = true; + } else { + $ignoreKey = false; + } + + if (!empty($value['name']) && $ignoreKey === false) { + // there already has been a tag with this name + if (in_array($value['name'], $parent['childrenKeys']) || in_array($value['name'], $this->options[XML_UNSERIALIZER_OPTION_FORCE_ENUM])) { + // no aggregate has been created for this tag + if (!in_array($value['name'], $parent['aggregKeys'])) { + if (isset($parent['children'][$value['name']])) { + $parent['children'][$value['name']] = array($parent['children'][$value['name']]); + } else { + $parent['children'][$value['name']] = array(); + } + array_push($parent['aggregKeys'], $value['name']); + } + array_push($parent['children'][$value['name']], $value['value']); + } else { + $parent['children'][$value['name']] = &$value['value']; + array_push($parent['childrenKeys'], $value['name']); + } + } else { + array_push($parent['children'], $value['value']); + } + array_push($this->_valStack, $parent); + } + + $this->_depth--; + } + + /** + * Handler for character data + * + * @access private + * @param object XML parser object + * @param string CDATA + * @return void + */ + function cdataHandler($parser, $cdata) + { + if ($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC] !== null) { + $cdata = call_user_func($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC], $cdata); + } + $this->_dataStack[$this->_depth] .= $cdata; + } + + /** + * get the complex type, that should be used for a specified tag + * + * @access private + * @param string name of the tag + * @return string complex type ('array' or 'object') + */ + function _getComplexType($tagname) + { + if (is_string($this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE])) { + return $this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE]; + } + if (isset($this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE][$tagname])) { + return $this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE][$tagname]; + } + if (isset($this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE]['#default'])) { + return $this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE]['#default']; + } + return 'array'; + } + + /** + * create the XML_Parser instance + * + * @access private + * @return boolean + */ + function _createParser() + { + if (is_object($this->_parser)) { + $this->_parser->free(); + unset($this->_parser); + } + $this->_parser = &new XML_Parser($this->options[XML_UNSERIALIZER_OPTION_ENCODING_SOURCE], 'event', $this->options[XML_UNSERIALIZER_OPTION_ENCODING_TARGET]); + $this->_parser->folding = false; + $this->_parser->setHandlerObj($this); + return true; + } +} +?>
\ No newline at end of file diff --git a/libs/XML/Util.php b/libs/XML/Util.php new file mode 100644 index 0000000000..133de882c9 --- /dev/null +++ b/libs/XML/Util.php @@ -0,0 +1,752 @@ +<?PHP
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stephan Schmidt <schst@php-tools.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Util.php,v 1.28 2006/12/16 09:42:56 schst Exp $
+
+/**
+ * error code for invalid chars in XML name
+ */
+define("XML_UTIL_ERROR_INVALID_CHARS", 51);
+
+/**
+ * error code for invalid chars in XML name
+ */
+define("XML_UTIL_ERROR_INVALID_START", 52);
+
+/**
+ * error code for non-scalar tag content
+ */
+define("XML_UTIL_ERROR_NON_SCALAR_CONTENT", 60);
+
+/**
+ * error code for missing tag name
+ */
+define("XML_UTIL_ERROR_NO_TAG_NAME", 61);
+
+/**
+ * replace XML entities
+ */
+define("XML_UTIL_REPLACE_ENTITIES", 1);
+
+/**
+ * embedd content in a CData Section
+ */
+define("XML_UTIL_CDATA_SECTION", 5);
+
+/**
+ * do not replace entitites
+ */
+define("XML_UTIL_ENTITIES_NONE", 0);
+
+/**
+ * replace all XML entitites
+ * This setting will replace <, >, ", ' and &
+ */
+define("XML_UTIL_ENTITIES_XML", 1);
+
+/**
+ * replace only required XML entitites
+ * This setting will replace <, " and &
+ */
+define("XML_UTIL_ENTITIES_XML_REQUIRED", 2);
+
+/**
+ * replace HTML entitites
+ * @link http://www.php.net/htmlentities
+ */
+define("XML_UTIL_ENTITIES_HTML", 3);
+
+/**
+ * Collapse all empty tags.
+ */
+define("XML_UTIL_COLLAPSE_ALL", 1);
+
+/**
+ * Collapse only empty XHTML tags that have no end tag.
+ */
+define("XML_UTIL_COLLAPSE_XHTML_ONLY", 2);
+
+/**
+ * utility class for working with XML documents
+ *
+ * @category XML
+ * @package XML_Util
+ * @version 1.1.0
+ * @author Stephan Schmidt <schst@php.net>
+ */
+class XML_Util {
+
+ /**
+ * return API version
+ *
+ * @access public
+ * @static
+ * @return string $version API version
+ */
+ static function apiVersion()
+ {
+ return '1.1';
+ }
+
+ /**
+ * replace XML entities
+ *
+ * With the optional second parameter, you may select, which
+ * entities should be replaced.
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // replace XML entites:
+ * $string = XML_Util::replaceEntities("This string contains < & >.");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string string where XML special chars should be replaced
+ * @param integer setting for entities in attribute values (one of XML_UTIL_ENTITIES_XML, XML_UTIL_ENTITIES_XML_REQUIRED, XML_UTIL_ENTITIES_HTML)
+ * @return string string with replaced chars
+ * @see reverseEntities()
+ */
+ static function replaceEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML)
+ {
+ switch ($replaceEntities) {
+ case XML_UTIL_ENTITIES_XML:
+ return strtr($string,array(
+ '&' => '&',
+ '>' => '>',
+ '<' => '<',
+ '"' => '"',
+ '\'' => ''' ));
+ break;
+ case XML_UTIL_ENTITIES_XML_REQUIRED:
+ return strtr($string,array(
+ '&' => '&',
+ '<' => '<',
+ '"' => '"' ));
+ break;
+ case XML_UTIL_ENTITIES_HTML:
+ return htmlentities($string);
+ break;
+ }
+ return $string;
+ }
+
+ /**
+ * reverse XML entities
+ *
+ * With the optional second parameter, you may select, which
+ * entities should be reversed.
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // reverse XML entites:
+ * $string = XML_Util::reverseEntities("This string contains < & >.");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string string where XML special chars should be replaced
+ * @param integer setting for entities in attribute values (one of XML_UTIL_ENTITIES_XML, XML_UTIL_ENTITIES_XML_REQUIRED, XML_UTIL_ENTITIES_HTML)
+ * @return string string with replaced chars
+ * @see replaceEntities()
+ */
+ static function reverseEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML)
+ {
+ switch ($replaceEntities) {
+ case XML_UTIL_ENTITIES_XML:
+ return strtr($string,array(
+ '&' => '&',
+ '>' => '>',
+ '<' => '<',
+ '"' => '"',
+ ''' => '\'' ));
+ break;
+ case XML_UTIL_ENTITIES_XML_REQUIRED:
+ return strtr($string,array(
+ '&' => '&',
+ '<' => '<',
+ '"' => '"' ));
+ break;
+ case XML_UTIL_ENTITIES_HTML:
+ $arr = array_flip(get_html_translation_table(HTML_ENTITIES));
+ return strtr($string, $arr);
+ break;
+ }
+ return $string;
+ }
+
+ /**
+ * build an xml declaration
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // get an XML declaration:
+ * $xmlDecl = XML_Util::getXMLDeclaration("1.0", "UTF-8", true);
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $version xml version
+ * @param string $encoding character encoding
+ * @param boolean $standAlone document is standalone (or not)
+ * @return string $decl xml declaration
+ * @uses XML_Util::attributesToString() to serialize the attributes of the XML declaration
+ */
+ static function getXMLDeclaration($version = "1.0", $encoding = null, $standalone = null)
+ {
+ $attributes = array(
+ "version" => $version,
+ );
+ // add encoding
+ if ($encoding !== null) {
+ $attributes["encoding"] = $encoding;
+ }
+ // add standalone, if specified
+ if ($standalone !== null) {
+ $attributes["standalone"] = $standalone ? "yes" : "no";
+ }
+
+ return sprintf("<?xml%s?>", XML_Util::attributesToString($attributes, false));
+ }
+
+ /**
+ * build a document type declaration
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // get a doctype declaration:
+ * $xmlDecl = XML_Util::getDocTypeDeclaration("rootTag","myDocType.dtd");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $root name of the root tag
+ * @param string $uri uri of the doctype definition (or array with uri and public id)
+ * @param string $internalDtd internal dtd entries
+ * @return string $decl doctype declaration
+ * @since 0.2
+ */
+ static function getDocTypeDeclaration($root, $uri = null, $internalDtd = null)
+ {
+ if (is_array($uri)) {
+ $ref = sprintf( ' PUBLIC "%s" "%s"', $uri["id"], $uri["uri"] );
+ } elseif (!empty($uri)) {
+ $ref = sprintf( ' SYSTEM "%s"', $uri );
+ } else {
+ $ref = "";
+ }
+
+ if (empty($internalDtd)) {
+ return sprintf("<!DOCTYPE %s%s>", $root, $ref);
+ } else {
+ return sprintf("<!DOCTYPE %s%s [\n%s\n]>", $root, $ref, $internalDtd);
+ }
+ }
+
+ /**
+ * create string representation of an attribute list
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // build an attribute string
+ * $att = array(
+ * "foo" => "bar",
+ * "argh" => "tomato"
+ * );
+ *
+ * $attList = XML_Util::attributesToString($att);
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param array $attributes attribute array
+ * @param boolean|array $sort sort attribute list alphabetically, may also be an assoc array containing the keys 'sort', 'multiline', 'indent', 'linebreak' and 'entities'
+ * @param boolean $multiline use linebreaks, if more than one attribute is given
+ * @param string $indent string used for indentation of multiline attributes
+ * @param string $linebreak string used for linebreaks of multiline attributes
+ * @param integer $entities setting for entities in attribute values (one of XML_UTIL_ENTITIES_NONE, XML_UTIL_ENTITIES_XML, XML_UTIL_ENTITIES_XML_REQUIRED, XML_UTIL_ENTITIES_HTML)
+ * @return string string representation of the attributes
+ * @uses XML_Util::replaceEntities() to replace XML entities in attribute values
+ * @todo allow sort also to be an options array
+ */
+ static function attributesToString($attributes, $sort = true, $multiline = false, $indent = ' ', $linebreak = "\n", $entities = XML_UTIL_ENTITIES_XML)
+ {
+ /**
+ * second parameter may be an array
+ */
+ if (is_array($sort)) {
+ if (isset($sort['multiline'])) {
+ $multiline = $sort['multiline'];
+ }
+ if (isset($sort['indent'])) {
+ $indent = $sort['indent'];
+ }
+ if (isset($sort['linebreak'])) {
+ $multiline = $sort['linebreak'];
+ }
+ if (isset($sort['entities'])) {
+ $entities = $sort['entities'];
+ }
+ if (isset($sort['sort'])) {
+ $sort = $sort['sort'];
+ } else {
+ $sort = true;
+ }
+ }
+ $string = '';
+ if (is_array($attributes) && !empty($attributes)) {
+ if ($sort) {
+ ksort($attributes);
+ }
+ if( !$multiline || count($attributes) == 1) {
+ foreach ($attributes as $key => $value) {
+ if ($entities != XML_UTIL_ENTITIES_NONE) {
+ if ($entities === XML_UTIL_CDATA_SECTION) {
+ $entities = XML_UTIL_ENTITIES_XML;
+ }
+ $value = XML_Util::replaceEntities($value, $entities);
+ }
+ $string .= ' '.$key.'="'.$value.'"';
+ }
+ } else {
+ $first = true;
+ foreach ($attributes as $key => $value) {
+ if ($entities != XML_UTIL_ENTITIES_NONE) {
+ $value = XML_Util::replaceEntities($value, $entities);
+ }
+ if ($first) {
+ $string .= " ".$key.'="'.$value.'"';
+ $first = false;
+ } else {
+ $string .= $linebreak.$indent.$key.'="'.$value.'"';
+ }
+ }
+ }
+ }
+ return $string;
+ }
+
+ /**
+ * Collapses empty tags.
+ *
+ * @access public
+ * @static
+ * @param string $xml XML
+ * @param integer $mode Whether to collapse all empty tags (XML_UTIL_COLLAPSE_ALL) or only XHTML (XML_UTIL_COLLAPSE_XHTML_ONLY) ones.
+ * @return string $xml XML
+ */
+ static function collapseEmptyTags($xml, $mode = XML_UTIL_COLLAPSE_ALL) {
+ if ($mode == XML_UTIL_COLLAPSE_XHTML_ONLY) {
+ return preg_replace(
+ '/<(area|base|br|col|hr|img|input|link|meta|param)([^>]*)><\/\\1>/s',
+ '<\\1\\2 />',
+ $xml
+ );
+ } else {
+ return preg_replace(
+ '/<(\w+)([^>]*)><\/\\1>/s',
+ '<\\1\\2 />',
+ $xml
+ );
+ }
+ }
+
+ /**
+ * create a tag
+ *
+ * This method will call XML_Util::createTagFromArray(), which
+ * is more flexible.
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // create an XML tag:
+ * $tag = XML_Util::createTag("myNs:myTag", array("foo" => "bar"), "This is inside the tag", "http://www.w3c.org/myNs#");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $qname qualified tagname (including namespace)
+ * @param array $attributes array containg attributes
+ * @param mixed $content
+ * @param string $namespaceUri URI of the namespace
+ * @param integer $replaceEntities whether to replace XML special chars in content, embedd it in a CData section or none of both
+ * @param boolean $multiline whether to create a multiline tag where each attribute gets written to a single line
+ * @param string $indent string used to indent attributes (_auto indents attributes so they start at the same column)
+ * @param string $linebreak string used for linebreaks
+ * @param boolean $sortAttributes Whether to sort the attributes or not
+ * @return string $string XML tag
+ * @see XML_Util::createTagFromArray()
+ * @uses XML_Util::createTagFromArray() to create the tag
+ */
+ static function createTag($qname, $attributes = array(), $content = null, $namespaceUri = null, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, $multiline = false, $indent = "_auto", $linebreak = "\n", $sortAttributes = true)
+ {
+ $tag = array(
+ "qname" => $qname,
+ "attributes" => $attributes
+ );
+
+ // add tag content
+ if ($content !== null) {
+ $tag["content"] = $content;
+ }
+
+ // add namespace Uri
+ if ($namespaceUri !== null) {
+ $tag["namespaceUri"] = $namespaceUri;
+ }
+
+ return XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $linebreak, $sortAttributes);
+ }
+
+ /**
+ * create a tag from an array
+ * this method awaits an array in the following format
+ * <pre>
+ * array(
+ * "qname" => $qname // qualified name of the tag
+ * "namespace" => $namespace // namespace prefix (optional, if qname is specified or no namespace)
+ * "localpart" => $localpart, // local part of the tagname (optional, if qname is specified)
+ * "attributes" => array(), // array containing all attributes (optional)
+ * "content" => $content, // tag content (optional)
+ * "namespaceUri" => $namespaceUri // namespaceUri for the given namespace (optional)
+ * )
+ * </pre>
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * $tag = array(
+ * "qname" => "foo:bar",
+ * "namespaceUri" => "http://foo.com",
+ * "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ),
+ * "content" => "I'm inside the tag",
+ * );
+ * // creating a tag with qualified name and namespaceUri
+ * $string = XML_Util::createTagFromArray($tag);
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param array $tag tag definition
+ * @param integer $replaceEntities whether to replace XML special chars in content, embedd it in a CData section or none of both
+ * @param boolean $multiline whether to create a multiline tag where each attribute gets written to a single line
+ * @param string $indent string used to indent attributes (_auto indents attributes so they start at the same column)
+ * @param string $linebreak string used for linebreaks
+ * @param boolean $sortAttributes Whether to sort the attributes or not
+ * @return string $string XML tag
+ * @see XML_Util::createTag()
+ * @uses XML_Util::attributesToString() to serialize the attributes of the tag
+ * @uses XML_Util::splitQualifiedName() to get local part and namespace of a qualified name
+ */
+ static function createTagFromArray($tag, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, $multiline = false, $indent = "_auto", $linebreak = "\n", $sortAttributes = true)
+ {
+ if (isset($tag['content']) && !is_scalar($tag['content'])) {
+ return XML_Util::raiseError( 'Supplied non-scalar value as tag content', XML_UTIL_ERROR_NON_SCALAR_CONTENT );
+ }
+
+ if (!isset($tag['qname']) && !isset($tag['localPart'])) {
+ return XML_Util::raiseError( 'You must either supply a qualified name (qname) or local tag name (localPart).', XML_UTIL_ERROR_NO_TAG_NAME );
+ }
+
+ // if no attributes hav been set, use empty attributes
+ if (!isset($tag["attributes"]) || !is_array($tag["attributes"])) {
+ $tag["attributes"] = array();
+ }
+
+ if (isset($tag['namespaces'])) {
+ foreach ($tag['namespaces'] as $ns => $uri) {
+ $tag['attributes']['xmlns:'.$ns] = $uri;
+ }
+ }
+
+ // qualified name is not given
+ if (!isset($tag["qname"])) {
+ // check for namespace
+ if (isset($tag["namespace"]) && !empty($tag["namespace"])) {
+ $tag["qname"] = $tag["namespace"].":".$tag["localPart"];
+ } else {
+ $tag["qname"] = $tag["localPart"];
+ }
+ // namespace URI is set, but no namespace
+ } elseif (isset($tag["namespaceUri"]) && !isset($tag["namespace"])) {
+ $parts = XML_Util::splitQualifiedName($tag["qname"]);
+ $tag["localPart"] = $parts["localPart"];
+ if (isset($parts["namespace"])) {
+ $tag["namespace"] = $parts["namespace"];
+ }
+ }
+
+ if (isset($tag["namespaceUri"]) && !empty($tag["namespaceUri"])) {
+ // is a namespace given
+ if (isset($tag["namespace"]) && !empty($tag["namespace"])) {
+ $tag["attributes"]["xmlns:".$tag["namespace"]] = $tag["namespaceUri"];
+ } else {
+ // define this Uri as the default namespace
+ $tag["attributes"]["xmlns"] = $tag["namespaceUri"];
+ }
+ }
+
+ // check for multiline attributes
+ if ($multiline === true) {
+ if ($indent === "_auto") {
+ $indent = str_repeat(" ", (strlen($tag["qname"])+2));
+ }
+ }
+
+ // create attribute list
+ $attList = XML_Util::attributesToString($tag['attributes'], $sortAttributes, $multiline, $indent, $linebreak, $replaceEntities );
+ if (!isset($tag['content']) || (string)$tag['content'] == '') {
+ $tag = sprintf('<%s%s />', $tag['qname'], $attList);
+ } else {
+ switch ($replaceEntities) {
+ case XML_UTIL_ENTITIES_NONE:
+ break;
+ case XML_UTIL_CDATA_SECTION:
+ $tag['content'] = XML_Util::createCDataSection($tag['content']);
+ break;
+ default:
+ $tag['content'] = XML_Util::replaceEntities($tag['content'], $replaceEntities);
+ break;
+ }
+ $tag = sprintf('<%s%s>%s</%s>', $tag['qname'], $attList, $tag['content'], $tag['qname'] );
+ }
+ return $tag;
+ }
+
+ /**
+ * create a start element
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // create an XML start element:
+ * $tag = XML_Util::createStartElement("myNs:myTag", array("foo" => "bar") ,"http://www.w3c.org/myNs#");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $qname qualified tagname (including namespace)
+ * @param array $attributes array containg attributes
+ * @param string $namespaceUri URI of the namespace
+ * @param boolean $multiline whether to create a multiline tag where each attribute gets written to a single line
+ * @param string $indent string used to indent attributes (_auto indents attributes so they start at the same column)
+ * @param string $linebreak string used for linebreaks
+ * @param boolean $sortAttributes Whether to sort the attributes or not
+ * @return string $string XML start element
+ * @see XML_Util::createEndElement(), XML_Util::createTag()
+ */
+ static function createStartElement($qname, $attributes = array(), $namespaceUri = null, $multiline = false, $indent = '_auto', $linebreak = "\n", $sortAttributes = true)
+ {
+ // if no attributes hav been set, use empty attributes
+ if (!isset($attributes) || !is_array($attributes)) {
+ $attributes = array();
+ }
+
+ if ($namespaceUri != null) {
+ $parts = XML_Util::splitQualifiedName($qname);
+ }
+
+ // check for multiline attributes
+ if ($multiline === true) {
+ if ($indent === "_auto") {
+ $indent = str_repeat(" ", (strlen($qname)+2));
+ }
+ }
+
+ if ($namespaceUri != null) {
+ // is a namespace given
+ if (isset($parts["namespace"]) && !empty($parts["namespace"])) {
+ $attributes["xmlns:".$parts["namespace"]] = $namespaceUri;
+ } else {
+ // define this Uri as the default namespace
+ $attributes["xmlns"] = $namespaceUri;
+ }
+ }
+
+ // create attribute list
+ $attList = XML_Util::attributesToString($attributes, $sortAttributes, $multiline, $indent, $linebreak);
+ $element = sprintf("<%s%s>", $qname, $attList);
+ return $element;
+ }
+
+ /**
+ * create an end element
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // create an XML start element:
+ * $tag = XML_Util::createEndElement("myNs:myTag");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $qname qualified tagname (including namespace)
+ * @return string $string XML end element
+ * @see XML_Util::createStartElement(), XML_Util::createTag()
+ */
+ static function createEndElement($qname)
+ {
+ $element = sprintf("</%s>", $qname);
+ return $element;
+ }
+
+ /**
+ * create an XML comment
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // create an XML start element:
+ * $tag = XML_Util::createComment("I am a comment");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $content content of the comment
+ * @return string $comment XML comment
+ */
+ static function createComment($content)
+ {
+ $comment = sprintf("<!-- %s -->", $content);
+ return $comment;
+ }
+
+ /**
+ * create a CData section
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // create a CData section
+ * $tag = XML_Util::createCDataSection("I am content.");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $data data of the CData section
+ * @return string $string CData section with content
+ */
+ static function createCDataSection($data)
+ {
+ return sprintf("<![CDATA[%s]]>", $data);
+ }
+
+ /**
+ * split qualified name and return namespace and local part
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // split qualified tag
+ * $parts = XML_Util::splitQualifiedName("xslt:stylesheet");
+ * </code>
+ * the returned array will contain two elements:
+ * <pre>
+ * array(
+ * "namespace" => "xslt",
+ * "localPart" => "stylesheet"
+ * );
+ * </pre>
+ *
+ * @access public
+ * @static
+ * @param string $qname qualified tag name
+ * @param string $defaultNs default namespace (optional)
+ * @return array $parts array containing namespace and local part
+ */
+ static function splitQualifiedName($qname, $defaultNs = null)
+ {
+ if (strstr($qname, ':')) {
+ $tmp = explode(":", $qname);
+ return array(
+ "namespace" => $tmp[0],
+ "localPart" => $tmp[1]
+ );
+ }
+ return array(
+ "namespace" => $defaultNs,
+ "localPart" => $qname
+ );
+ }
+
+ /**
+ * check, whether string is valid XML name
+ *
+ * <p>XML names are used for tagname, attribute names and various
+ * other, lesser known entities.</p>
+ * <p>An XML name may only consist of alphanumeric characters,
+ * dashes, undescores and periods, and has to start with a letter
+ * or an underscore.
+ * </p>
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // verify tag name
+ * $result = XML_Util::isValidName("invalidTag?");
+ * if (XML_Util::isError($result)) {
+ * print "Invalid XML name: " . $result->getMessage();
+ * }
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $string string that should be checked
+ * @return mixed $valid true, if string is a valid XML name, PEAR error otherwise
+ * @todo support for other charsets
+ */
+ static function isValidName($string)
+ {
+ // check for invalid chars
+ if (!preg_match('/^[[:alpha:]_]$/', $string{0})) {
+ return XML_Util::raiseError('XML names may only start with letter or underscore', XML_UTIL_ERROR_INVALID_START);
+ }
+
+ // check for invalid chars
+ if (!preg_match('/^([[:alpha:]_]([[:alnum:]\-\.]*)?:)?[[:alpha:]_]([[:alnum:]\_\-\.]+)?$/', $string)) {
+ return XML_Util::raiseError('XML names may only contain alphanumeric chars, period, hyphen, colon and underscores', XML_UTIL_ERROR_INVALID_CHARS);
+ }
+ // XML name is valid
+ return true;
+ }
+
+ /**
+ * replacement for XML_Util::raiseError
+ *
+ * Avoids the necessity to always require
+ * PEAR.php
+ *
+ * @access public
+ * @param string error message
+ * @param integer error code
+ * @return object PEAR_Error
+ */
+ static function raiseError($msg, $code)
+ {
+ require_once 'PEAR.php';
+ return PEAR::raiseError($msg, $code);
+ }
+}
+?>
\ No newline at end of file |