4 * XML utilities for WebDAV
8 * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
9 * @author Evert Pot (http://www.rooftopsolutions.nl/)
10 * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
12 class Sabre_DAV_XMLUtil {
15 * Returns the 'clark notation' for an element.
17 * For example, and element encoded as:
18 * <b:myelem xmlns:b="http://www.example.org/" />
19 * will be returned as:
20 * {http://www.example.org}myelem
22 * This format is used throughout the SabreDAV sourcecode.
24 * This function will return null if a nodetype other than an Element is passed.
29 static function toClarkNotation(DOMNode $dom) {
31 if ($dom->nodeType !== XML_ELEMENT_NODE) return null;
33 $ns = $dom->namespaceURI;
35 // Mapping to clark notation
36 return '{' . $ns . '}' . $dom->localName;
41 * Parses a clark-notation string, and returns the namespace and element
44 * If the string was invalid, it will throw an InvalidArgumentException.
47 * @throws InvalidArgumentException
50 static function parseClarkNotation($str) {
52 if (!preg_match('/^{([^}]*)}(.*)$/',$str,$matches)) {
53 throw new InvalidArgumentException('\'' . $str . '\' is not a valid clark-notation formatted string');
64 * This method provides a generic way to load a DOMDocument for WebDAV use.
66 * This method throws a Sabre_DAV_Exception_BadRequest exception for any xml errors.
67 * It does not preserve whitespace.
70 * @throws Sabre_DAV_Exception_BadRequest
73 static function loadDOMDocument($xml) {
76 throw new Sabre_DAV_Exception_BadRequest('Empty XML document sent');
78 // The BitKinex client sends xml documents as UTF-16. PHP 5.3.1 (and presumably lower)
79 // does not support this, so we must intercept this and convert to UTF-8.
80 if (substr($xml,0,12) === "\x3c\x00\x3f\x00\x78\x00\x6d\x00\x6c\x00\x20\x00") {
82 // Note: the preceeding byte sequence is "<?xml" encoded as UTF_16, without the BOM.
83 $xml = iconv('UTF-16LE','UTF-8',$xml);
85 // Because the xml header might specify the encoding, we must also change this.
86 // This regex looks for the string encoding="UTF-16" and replaces it with
88 $xml = preg_replace('|<\?xml([^>]*)encoding="UTF-16"([^>]*)>|u','<?xml\1encoding="UTF-8"\2>',$xml);
92 // Retaining old error setting
93 $oldErrorSetting = libxml_use_internal_errors(true);
95 // Clearing any previous errors
96 libxml_clear_errors();
98 $dom = new DOMDocument();
100 // We don't generally care about any whitespace
101 $dom->preserveWhiteSpace = false;
103 $dom->loadXML($xml,LIBXML_NOWARNING | LIBXML_NOERROR);
105 if ($error = libxml_get_last_error()) {
106 libxml_clear_errors();
107 throw new Sabre_DAV_Exception_BadRequest('The request body had an invalid XML body. (message: ' . $error->message . ', errorcode: ' . $error->code . ', line: ' . $error->line . ')');
110 // Restoring old mechanism for error handling
111 if ($oldErrorSetting===false) libxml_use_internal_errors(false);
118 * Parses all WebDAV properties out of a DOM Element
120 * Generally WebDAV properties are enclosed in {DAV:}prop elements. This
121 * method helps by going through all these and pulling out the actual
122 * propertynames, making them array keys and making the property values,
123 * well.. the array values.
125 * If no value was given (self-closing element) null will be used as the
126 * value. This is used in for example PROPFIND requests.
128 * Complex values are supported through the propertyMap argument. The
129 * propertyMap should have the clark-notation properties as it's keys, and
130 * classnames as values.
132 * When any of these properties are found, the unserialize() method will be
133 * (statically) called. The result of this method is used as the value.
135 * @param DOMElement $parentNode
136 * @param array $propertyMap
139 static function parseProperties(DOMElement $parentNode, array $propertyMap = array()) {
142 foreach($parentNode->childNodes as $propNode) {
144 if (Sabre_DAV_XMLUtil::toClarkNotation($propNode)!=='{DAV:}prop') continue;
146 foreach($propNode->childNodes as $propNodeData) {
148 /* If there are no elements in here, we actually get 1 text node, this special case is dedicated to netdrive */
149 if ($propNodeData->nodeType != XML_ELEMENT_NODE) continue;
151 $propertyName = Sabre_DAV_XMLUtil::toClarkNotation($propNodeData);
152 if (isset($propertyMap[$propertyName])) {
153 $propList[$propertyName] = call_user_func(array($propertyMap[$propertyName],'unserialize'),$propNodeData);
155 $propList[$propertyName] = $propNodeData->textContent;