1*a1a3b679SAndreas Boehler<?php 2*a1a3b679SAndreas Boehler 3*a1a3b679SAndreas Boehlernamespace Sabre\DAV; 4*a1a3b679SAndreas Boehler 5*a1a3b679SAndreas Boehler/** 6*a1a3b679SAndreas Boehler * This class holds all the information about a PROPFIND request. 7*a1a3b679SAndreas Boehler * 8*a1a3b679SAndreas Boehler * It contains the type of PROPFIND request, which properties were requested 9*a1a3b679SAndreas Boehler * and also the returned items. 10*a1a3b679SAndreas Boehler */ 11*a1a3b679SAndreas Boehlerclass PropFind { 12*a1a3b679SAndreas Boehler 13*a1a3b679SAndreas Boehler /** 14*a1a3b679SAndreas Boehler * A normal propfind 15*a1a3b679SAndreas Boehler */ 16*a1a3b679SAndreas Boehler const NORMAL = 0; 17*a1a3b679SAndreas Boehler 18*a1a3b679SAndreas Boehler /** 19*a1a3b679SAndreas Boehler * An allprops request. 20*a1a3b679SAndreas Boehler * 21*a1a3b679SAndreas Boehler * While this was originally intended for instructing the server to really 22*a1a3b679SAndreas Boehler * fetch every property, because it was used so often and it's so heavy 23*a1a3b679SAndreas Boehler * this turned into a small list of default properties after a while. 24*a1a3b679SAndreas Boehler * 25*a1a3b679SAndreas Boehler * So 'all properties' now means a hardcoded list. 26*a1a3b679SAndreas Boehler */ 27*a1a3b679SAndreas Boehler const ALLPROPS = 1; 28*a1a3b679SAndreas Boehler 29*a1a3b679SAndreas Boehler /** 30*a1a3b679SAndreas Boehler * A propname request. This just returns a list of properties that are 31*a1a3b679SAndreas Boehler * defined on a node, without their values. 32*a1a3b679SAndreas Boehler */ 33*a1a3b679SAndreas Boehler const PROPNAME = 2; 34*a1a3b679SAndreas Boehler 35*a1a3b679SAndreas Boehler /** 36*a1a3b679SAndreas Boehler * Creates the PROPFIND object 37*a1a3b679SAndreas Boehler * 38*a1a3b679SAndreas Boehler * @param string $path 39*a1a3b679SAndreas Boehler * @param array $properties 40*a1a3b679SAndreas Boehler * @param int $depth 41*a1a3b679SAndreas Boehler * @param int $requestType 42*a1a3b679SAndreas Boehler */ 43*a1a3b679SAndreas Boehler function __construct($path, array $properties, $depth = 0, $requestType = self::NORMAL) { 44*a1a3b679SAndreas Boehler 45*a1a3b679SAndreas Boehler $this->path = $path; 46*a1a3b679SAndreas Boehler $this->properties = $properties; 47*a1a3b679SAndreas Boehler $this->depth = $depth; 48*a1a3b679SAndreas Boehler $this->requestType = $requestType; 49*a1a3b679SAndreas Boehler 50*a1a3b679SAndreas Boehler if ($requestType === self::ALLPROPS) { 51*a1a3b679SAndreas Boehler $this->properties = [ 52*a1a3b679SAndreas Boehler '{DAV:}getlastmodified', 53*a1a3b679SAndreas Boehler '{DAV:}getcontentlength', 54*a1a3b679SAndreas Boehler '{DAV:}resourcetype', 55*a1a3b679SAndreas Boehler '{DAV:}quota-used-bytes', 56*a1a3b679SAndreas Boehler '{DAV:}quota-available-bytes', 57*a1a3b679SAndreas Boehler '{DAV:}getetag', 58*a1a3b679SAndreas Boehler '{DAV:}getcontenttype', 59*a1a3b679SAndreas Boehler ]; 60*a1a3b679SAndreas Boehler } 61*a1a3b679SAndreas Boehler 62*a1a3b679SAndreas Boehler foreach ($this->properties as $propertyName) { 63*a1a3b679SAndreas Boehler 64*a1a3b679SAndreas Boehler // Seeding properties with 404's. 65*a1a3b679SAndreas Boehler $this->result[$propertyName] = [404, null]; 66*a1a3b679SAndreas Boehler 67*a1a3b679SAndreas Boehler } 68*a1a3b679SAndreas Boehler $this->itemsLeft = count($this->result); 69*a1a3b679SAndreas Boehler 70*a1a3b679SAndreas Boehler } 71*a1a3b679SAndreas Boehler 72*a1a3b679SAndreas Boehler /** 73*a1a3b679SAndreas Boehler * Handles a specific property. 74*a1a3b679SAndreas Boehler * 75*a1a3b679SAndreas Boehler * This method checks wether the specified property was requested in this 76*a1a3b679SAndreas Boehler * PROPFIND request, and if so, it will call the callback and use the 77*a1a3b679SAndreas Boehler * return value for it's value. 78*a1a3b679SAndreas Boehler * 79*a1a3b679SAndreas Boehler * Example: 80*a1a3b679SAndreas Boehler * 81*a1a3b679SAndreas Boehler * $propFind->handle('{DAV:}displayname', function() { 82*a1a3b679SAndreas Boehler * return 'hello'; 83*a1a3b679SAndreas Boehler * }); 84*a1a3b679SAndreas Boehler * 85*a1a3b679SAndreas Boehler * Note that handle will only work the first time. If null is returned, the 86*a1a3b679SAndreas Boehler * value is ignored. 87*a1a3b679SAndreas Boehler * 88*a1a3b679SAndreas Boehler * It's also possible to not pass a callback, but immediately pass a value 89*a1a3b679SAndreas Boehler * 90*a1a3b679SAndreas Boehler * @param string $propertyName 91*a1a3b679SAndreas Boehler * @param mixed $valueOrCallBack 92*a1a3b679SAndreas Boehler * @return void 93*a1a3b679SAndreas Boehler */ 94*a1a3b679SAndreas Boehler function handle($propertyName, $valueOrCallBack) { 95*a1a3b679SAndreas Boehler 96*a1a3b679SAndreas Boehler if ($this->itemsLeft && isset($this->result[$propertyName]) && $this->result[$propertyName][0] === 404) { 97*a1a3b679SAndreas Boehler if (is_callable($valueOrCallBack)) { 98*a1a3b679SAndreas Boehler $value = $valueOrCallBack(); 99*a1a3b679SAndreas Boehler } else { 100*a1a3b679SAndreas Boehler $value = $valueOrCallBack; 101*a1a3b679SAndreas Boehler } 102*a1a3b679SAndreas Boehler if (!is_null($value)) { 103*a1a3b679SAndreas Boehler $this->itemsLeft--; 104*a1a3b679SAndreas Boehler $this->result[$propertyName] = [200, $value]; 105*a1a3b679SAndreas Boehler } 106*a1a3b679SAndreas Boehler } 107*a1a3b679SAndreas Boehler 108*a1a3b679SAndreas Boehler } 109*a1a3b679SAndreas Boehler 110*a1a3b679SAndreas Boehler /** 111*a1a3b679SAndreas Boehler * Sets the value of the property 112*a1a3b679SAndreas Boehler * 113*a1a3b679SAndreas Boehler * If status is not supplied, the status will default to 200 for non-null 114*a1a3b679SAndreas Boehler * properties, and 404 for null properties. 115*a1a3b679SAndreas Boehler * 116*a1a3b679SAndreas Boehler * @param string $propertyName 117*a1a3b679SAndreas Boehler * @param mixed $value 118*a1a3b679SAndreas Boehler * @param int $status 119*a1a3b679SAndreas Boehler * @return void 120*a1a3b679SAndreas Boehler */ 121*a1a3b679SAndreas Boehler function set($propertyName, $value, $status = null) { 122*a1a3b679SAndreas Boehler 123*a1a3b679SAndreas Boehler if (is_null($status)) { 124*a1a3b679SAndreas Boehler $status = is_null($value) ? 404 : 200; 125*a1a3b679SAndreas Boehler } 126*a1a3b679SAndreas Boehler // If this is an ALLPROPS request and the property is 127*a1a3b679SAndreas Boehler // unknown, add it to the result; else ignore it: 128*a1a3b679SAndreas Boehler if (!isset($this->result[$propertyName])) { 129*a1a3b679SAndreas Boehler if ($this->requestType === self::ALLPROPS) { 130*a1a3b679SAndreas Boehler $this->result[$propertyName] = [$status, $value]; 131*a1a3b679SAndreas Boehler } 132*a1a3b679SAndreas Boehler return; 133*a1a3b679SAndreas Boehler } 134*a1a3b679SAndreas Boehler if ($status !== 404 && $this->result[$propertyName][0] === 404) { 135*a1a3b679SAndreas Boehler $this->itemsLeft--; 136*a1a3b679SAndreas Boehler } elseif ($status === 404 && $this->result[$propertyName][0] !== 404) { 137*a1a3b679SAndreas Boehler $this->itemsLeft++; 138*a1a3b679SAndreas Boehler } 139*a1a3b679SAndreas Boehler $this->result[$propertyName] = [$status, $value]; 140*a1a3b679SAndreas Boehler 141*a1a3b679SAndreas Boehler } 142*a1a3b679SAndreas Boehler 143*a1a3b679SAndreas Boehler /** 144*a1a3b679SAndreas Boehler * Returns the current value for a property. 145*a1a3b679SAndreas Boehler * 146*a1a3b679SAndreas Boehler * @param string $propertyName 147*a1a3b679SAndreas Boehler * @return mixed 148*a1a3b679SAndreas Boehler */ 149*a1a3b679SAndreas Boehler function get($propertyName) { 150*a1a3b679SAndreas Boehler 151*a1a3b679SAndreas Boehler return isset($this->result[$propertyName]) ? $this->result[$propertyName][1] : null; 152*a1a3b679SAndreas Boehler 153*a1a3b679SAndreas Boehler } 154*a1a3b679SAndreas Boehler 155*a1a3b679SAndreas Boehler /** 156*a1a3b679SAndreas Boehler * Returns the current status code for a property name. 157*a1a3b679SAndreas Boehler * 158*a1a3b679SAndreas Boehler * If the property does not appear in the list of requested properties, 159*a1a3b679SAndreas Boehler * null will be returned. 160*a1a3b679SAndreas Boehler * 161*a1a3b679SAndreas Boehler * @param string $propertyName 162*a1a3b679SAndreas Boehler * @return int|null 163*a1a3b679SAndreas Boehler */ 164*a1a3b679SAndreas Boehler function getStatus($propertyName) { 165*a1a3b679SAndreas Boehler 166*a1a3b679SAndreas Boehler return isset($this->result[$propertyName]) ? $this->result[$propertyName][0] : null; 167*a1a3b679SAndreas Boehler 168*a1a3b679SAndreas Boehler } 169*a1a3b679SAndreas Boehler 170*a1a3b679SAndreas Boehler /** 171*a1a3b679SAndreas Boehler * Updates the path for this PROPFIND. 172*a1a3b679SAndreas Boehler * 173*a1a3b679SAndreas Boehler * @param string $path 174*a1a3b679SAndreas Boehler * @return void 175*a1a3b679SAndreas Boehler */ 176*a1a3b679SAndreas Boehler function setPath($path) { 177*a1a3b679SAndreas Boehler 178*a1a3b679SAndreas Boehler $this->path = $path; 179*a1a3b679SAndreas Boehler 180*a1a3b679SAndreas Boehler } 181*a1a3b679SAndreas Boehler 182*a1a3b679SAndreas Boehler /** 183*a1a3b679SAndreas Boehler * Returns the path this PROPFIND request is for. 184*a1a3b679SAndreas Boehler * 185*a1a3b679SAndreas Boehler * @return string 186*a1a3b679SAndreas Boehler */ 187*a1a3b679SAndreas Boehler function getPath() { 188*a1a3b679SAndreas Boehler 189*a1a3b679SAndreas Boehler return $this->path; 190*a1a3b679SAndreas Boehler 191*a1a3b679SAndreas Boehler } 192*a1a3b679SAndreas Boehler 193*a1a3b679SAndreas Boehler /** 194*a1a3b679SAndreas Boehler * Returns the depth of this propfind request. 195*a1a3b679SAndreas Boehler * 196*a1a3b679SAndreas Boehler * @return int 197*a1a3b679SAndreas Boehler */ 198*a1a3b679SAndreas Boehler function getDepth() { 199*a1a3b679SAndreas Boehler 200*a1a3b679SAndreas Boehler return $this->depth; 201*a1a3b679SAndreas Boehler 202*a1a3b679SAndreas Boehler } 203*a1a3b679SAndreas Boehler 204*a1a3b679SAndreas Boehler /** 205*a1a3b679SAndreas Boehler * Updates the depth of this propfind request. 206*a1a3b679SAndreas Boehler * 207*a1a3b679SAndreas Boehler * @param int $depth 208*a1a3b679SAndreas Boehler * @return void 209*a1a3b679SAndreas Boehler */ 210*a1a3b679SAndreas Boehler function setDepth($depth) { 211*a1a3b679SAndreas Boehler 212*a1a3b679SAndreas Boehler $this->depth = $depth; 213*a1a3b679SAndreas Boehler 214*a1a3b679SAndreas Boehler } 215*a1a3b679SAndreas Boehler 216*a1a3b679SAndreas Boehler /** 217*a1a3b679SAndreas Boehler * Returns all propertynames that have a 404 status, and thus don't have a 218*a1a3b679SAndreas Boehler * value yet. 219*a1a3b679SAndreas Boehler * 220*a1a3b679SAndreas Boehler * @return array 221*a1a3b679SAndreas Boehler */ 222*a1a3b679SAndreas Boehler function get404Properties() { 223*a1a3b679SAndreas Boehler 224*a1a3b679SAndreas Boehler if ($this->itemsLeft === 0) { 225*a1a3b679SAndreas Boehler return []; 226*a1a3b679SAndreas Boehler } 227*a1a3b679SAndreas Boehler $result = []; 228*a1a3b679SAndreas Boehler foreach ($this->result as $propertyName => $stuff) { 229*a1a3b679SAndreas Boehler if ($stuff[0] === 404) { 230*a1a3b679SAndreas Boehler $result[] = $propertyName; 231*a1a3b679SAndreas Boehler } 232*a1a3b679SAndreas Boehler } 233*a1a3b679SAndreas Boehler return $result; 234*a1a3b679SAndreas Boehler 235*a1a3b679SAndreas Boehler } 236*a1a3b679SAndreas Boehler 237*a1a3b679SAndreas Boehler /** 238*a1a3b679SAndreas Boehler * Returns the full list of requested properties. 239*a1a3b679SAndreas Boehler * 240*a1a3b679SAndreas Boehler * This returns just their names, not a status or value. 241*a1a3b679SAndreas Boehler * 242*a1a3b679SAndreas Boehler * @return array 243*a1a3b679SAndreas Boehler */ 244*a1a3b679SAndreas Boehler function getRequestedProperties() { 245*a1a3b679SAndreas Boehler 246*a1a3b679SAndreas Boehler return $this->properties; 247*a1a3b679SAndreas Boehler 248*a1a3b679SAndreas Boehler } 249*a1a3b679SAndreas Boehler 250*a1a3b679SAndreas Boehler /** 251*a1a3b679SAndreas Boehler * Returns true if this was an '{DAV:}allprops' request. 252*a1a3b679SAndreas Boehler * 253*a1a3b679SAndreas Boehler * @return bool 254*a1a3b679SAndreas Boehler */ 255*a1a3b679SAndreas Boehler function isAllProps() { 256*a1a3b679SAndreas Boehler 257*a1a3b679SAndreas Boehler return $this->requestType === self::ALLPROPS; 258*a1a3b679SAndreas Boehler 259*a1a3b679SAndreas Boehler } 260*a1a3b679SAndreas Boehler 261*a1a3b679SAndreas Boehler /** 262*a1a3b679SAndreas Boehler * Returns a result array that's often used in multistatus responses. 263*a1a3b679SAndreas Boehler * 264*a1a3b679SAndreas Boehler * The array uses status codes as keys, and property names and value pairs 265*a1a3b679SAndreas Boehler * as the value of the top array.. such as : 266*a1a3b679SAndreas Boehler * 267*a1a3b679SAndreas Boehler * [ 268*a1a3b679SAndreas Boehler * 200 => [ '{DAV:}displayname' => 'foo' ], 269*a1a3b679SAndreas Boehler * ] 270*a1a3b679SAndreas Boehler * 271*a1a3b679SAndreas Boehler * @return array 272*a1a3b679SAndreas Boehler */ 273*a1a3b679SAndreas Boehler function getResultForMultiStatus() { 274*a1a3b679SAndreas Boehler 275*a1a3b679SAndreas Boehler $r = [ 276*a1a3b679SAndreas Boehler 200 => [], 277*a1a3b679SAndreas Boehler 404 => [], 278*a1a3b679SAndreas Boehler ]; 279*a1a3b679SAndreas Boehler foreach ($this->result as $propertyName => $info) { 280*a1a3b679SAndreas Boehler if (!isset($r[$info[0]])) { 281*a1a3b679SAndreas Boehler $r[$info[0]] = [$propertyName => $info[1]]; 282*a1a3b679SAndreas Boehler } else { 283*a1a3b679SAndreas Boehler $r[$info[0]][$propertyName] = $info[1]; 284*a1a3b679SAndreas Boehler } 285*a1a3b679SAndreas Boehler } 286*a1a3b679SAndreas Boehler // Removing the 404's for multi-status requests. 287*a1a3b679SAndreas Boehler if ($this->requestType === self::ALLPROPS) unset($r[404]); 288*a1a3b679SAndreas Boehler return $r; 289*a1a3b679SAndreas Boehler 290*a1a3b679SAndreas Boehler } 291*a1a3b679SAndreas Boehler 292*a1a3b679SAndreas Boehler /** 293*a1a3b679SAndreas Boehler * The path that we're fetching properties for. 294*a1a3b679SAndreas Boehler * 295*a1a3b679SAndreas Boehler * @var string 296*a1a3b679SAndreas Boehler */ 297*a1a3b679SAndreas Boehler protected $path; 298*a1a3b679SAndreas Boehler 299*a1a3b679SAndreas Boehler /** 300*a1a3b679SAndreas Boehler * The Depth of the request. 301*a1a3b679SAndreas Boehler * 302*a1a3b679SAndreas Boehler * 0 means only the current item. 1 means the current item + its children. 303*a1a3b679SAndreas Boehler * It can also be DEPTH_INFINITY if this is enabled in the server. 304*a1a3b679SAndreas Boehler * 305*a1a3b679SAndreas Boehler * @var int 306*a1a3b679SAndreas Boehler */ 307*a1a3b679SAndreas Boehler protected $depth = 0; 308*a1a3b679SAndreas Boehler 309*a1a3b679SAndreas Boehler /** 310*a1a3b679SAndreas Boehler * The type of request. See the TYPE constants 311*a1a3b679SAndreas Boehler */ 312*a1a3b679SAndreas Boehler protected $requestType; 313*a1a3b679SAndreas Boehler 314*a1a3b679SAndreas Boehler /** 315*a1a3b679SAndreas Boehler * A list of requested properties 316*a1a3b679SAndreas Boehler * 317*a1a3b679SAndreas Boehler * @var array 318*a1a3b679SAndreas Boehler */ 319*a1a3b679SAndreas Boehler protected $properties = []; 320*a1a3b679SAndreas Boehler 321*a1a3b679SAndreas Boehler /** 322*a1a3b679SAndreas Boehler * The result of the operation. 323*a1a3b679SAndreas Boehler * 324*a1a3b679SAndreas Boehler * The keys in this array are property names. 325*a1a3b679SAndreas Boehler * The values are an array with two elements: the http status code and then 326*a1a3b679SAndreas Boehler * optionally a value. 327*a1a3b679SAndreas Boehler * 328*a1a3b679SAndreas Boehler * Example: 329*a1a3b679SAndreas Boehler * 330*a1a3b679SAndreas Boehler * [ 331*a1a3b679SAndreas Boehler * "{DAV:}owner" : [404], 332*a1a3b679SAndreas Boehler * "{DAV:}displayname" : [200, "Admin"] 333*a1a3b679SAndreas Boehler * ] 334*a1a3b679SAndreas Boehler * 335*a1a3b679SAndreas Boehler * @var array 336*a1a3b679SAndreas Boehler */ 337*a1a3b679SAndreas Boehler protected $result = []; 338*a1a3b679SAndreas Boehler 339*a1a3b679SAndreas Boehler /** 340*a1a3b679SAndreas Boehler * This is used as an internal counter for the number of properties that do 341*a1a3b679SAndreas Boehler * not yet have a value. 342*a1a3b679SAndreas Boehler * 343*a1a3b679SAndreas Boehler * @var int 344*a1a3b679SAndreas Boehler */ 345*a1a3b679SAndreas Boehler protected $itemsLeft; 346*a1a3b679SAndreas Boehler 347*a1a3b679SAndreas Boehler} 348