1<?php 2 3namespace Sabre\DAV\Xml\Element; 4 5use Sabre\Xml\Element; 6use Sabre\Xml\Reader; 7use Sabre\Xml\Writer; 8 9/** 10 * WebDAV {DAV:}response parser 11 * 12 * This class parses the {DAV:}response element, as defined in: 13 * 14 * https://tools.ietf.org/html/rfc4918#section-14.24 15 * 16 * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/). 17 * @author Evert Pot (http://www.rooftopsolutions.nl/) 18 * @license http://sabre.io/license/ Modified BSD License 19 */ 20class Response implements Element { 21 22 /** 23 * Url for the response 24 * 25 * @var string 26 */ 27 protected $href; 28 29 /** 30 * Propertylist, ordered by HTTP status code 31 * 32 * @var array 33 */ 34 protected $responseProperties; 35 36 /** 37 * The HTTP status for an entire response. 38 * 39 * This is currently only used in WebDAV-Sync 40 * 41 * @var string 42 */ 43 protected $httpStatus; 44 45 /** 46 * The href argument is a url relative to the root of the server. This 47 * class will calculate the full path. 48 * 49 * The responseProperties argument is a list of properties 50 * within an array with keys representing HTTP status codes 51 * 52 * Besides specific properties, the entire {DAV:}response element may also 53 * have a http status code. 54 * In most cases you don't need it. 55 * 56 * This is currently used by the Sync extension to indicate that a node is 57 * deleted. 58 * 59 * @param string $href 60 * @param array $responseProperties 61 * @param string $httpStatus 62 */ 63 function __construct($href, array $responseProperties, $httpStatus = null) { 64 65 $this->href = $href; 66 $this->responseProperties = $responseProperties; 67 $this->httpStatus = $httpStatus; 68 69 } 70 71 /** 72 * Returns the url 73 * 74 * @return string 75 */ 76 function getHref() { 77 78 return $this->href; 79 80 } 81 82 /** 83 * Returns the httpStatus value 84 * 85 * @return string 86 */ 87 function getHttpStatus() { 88 89 return $this->httpStatus; 90 91 } 92 93 /** 94 * Returns the property list 95 * 96 * @return array 97 */ 98 function getResponseProperties() { 99 100 return $this->responseProperties; 101 102 } 103 104 105 /** 106 * The serialize method is called during xml writing. 107 * 108 * It should use the $writer argument to encode this object into XML. 109 * 110 * Important note: it is not needed to create the parent element. The 111 * parent element is already created, and we only have to worry about 112 * attributes, child elements and text (if any). 113 * 114 * Important note 2: If you are writing any new elements, you are also 115 * responsible for closing them. 116 * 117 * @param Writer $writer 118 * @return void 119 */ 120 function xmlSerialize(Writer $writer) { 121 122 if ($status = $this->getHTTPStatus()) { 123 $writer->writeElement('{DAV:}status', 'HTTP/1.1 ' . $status . ' ' . \Sabre\HTTP\Response::$statusCodes[$status]); 124 } 125 $writer->writeElement('{DAV:}href', $writer->contextUri . $this->getHref()); 126 foreach ($this->getResponseProperties() as $status => $properties) { 127 128 // Skipping empty lists 129 if (!$properties || (!ctype_digit($status) && !is_int($status))) { 130 continue; 131 } 132 $writer->startElement('{DAV:}propstat'); 133 $writer->writeElement('{DAV:}prop', $properties); 134 $writer->writeElement('{DAV:}status', 'HTTP/1.1 ' . $status . ' ' . \Sabre\HTTP\Response::$statusCodes[$status]); 135 $writer->endElement(); // {DAV:}propstat 136 137 } 138 139 } 140 141 /** 142 * The deserialize method is called during xml parsing. 143 * 144 * This method is called statictly, this is because in theory this method 145 * may be used as a type of constructor, or factory method. 146 * 147 * Often you want to return an instance of the current class, but you are 148 * free to return other data as well. 149 * 150 * You are responsible for advancing the reader to the next element. Not 151 * doing anything will result in a never-ending loop. 152 * 153 * If you just want to skip parsing for this element altogether, you can 154 * just call $reader->next(); 155 * 156 * $reader->parseInnerTree() will parse the entire sub-tree, and advance to 157 * the next element. 158 * 159 * @param Reader $reader 160 * @return mixed 161 */ 162 static function xmlDeserialize(Reader $reader) { 163 164 $reader->pushContext(); 165 166 $reader->elementMap['{DAV:}propstat'] = 'Sabre\\Xml\\Element\\KeyValue'; 167 168 // We are overriding the parser for {DAV:}prop. This deserializer is 169 // almost identical to the one for Sabre\Xml\Element\KeyValue. 170 // 171 // The difference is that if there are any child-elements inside of 172 // {DAV:}prop, that have no value, normally any deserializers are 173 // called. But we don't want this, because a singlular element without 174 // child-elements implies 'no value' in {DAV:}prop, so we want to skip 175 // deserializers and just set null for those. 176 $reader->elementMap['{DAV:}prop'] = function(Reader $reader) { 177 178 if ($reader->isEmptyElement) { 179 $reader->next(); 180 return []; 181 } 182 $values = []; 183 $reader->read(); 184 do { 185 if ($reader->nodeType === Reader::ELEMENT) { 186 $clark = $reader->getClark(); 187 188 if ($reader->isEmptyElement) { 189 $values[$clark] = null; 190 $reader->next(); 191 } else { 192 $values[$clark] = $reader->parseCurrentElement()['value']; 193 } 194 } else { 195 $reader->read(); 196 } 197 } while ($reader->nodeType !== Reader::END_ELEMENT); 198 $reader->read(); 199 return $values; 200 201 }; 202 $elems = $reader->parseInnerTree(); 203 $reader->popContext(); 204 205 $href = null; 206 $propertyLists = []; 207 $statusCode = null; 208 209 foreach ($elems as $elem) { 210 211 switch ($elem['name']) { 212 213 case '{DAV:}href' : 214 $href = $elem['value']; 215 break; 216 case '{DAV:}propstat' : 217 $status = $elem['value']['{DAV:}status']; 218 list(, $status, ) = explode(' ', $status, 3); 219 $properties = isset($elem['value']['{DAV:}prop']) ? $elem['value']['{DAV:}prop'] : []; 220 $propertyLists[$status] = $properties; 221 break; 222 case '{DAV:}status' : 223 list(, $statusCode, ) = explode(' ', $elem['value'], 3); 224 break; 225 226 } 227 228 } 229 230 return new self($href, $propertyLists, $statusCode); 231 232 } 233 234} 235