path = $path; $this->properties = $properties; $this->depth = $depth; $this->requestType = $requestType; if ($requestType === self::ALLPROPS) { $this->properties = [ '{DAV:}getlastmodified', '{DAV:}getcontentlength', '{DAV:}resourcetype', '{DAV:}quota-used-bytes', '{DAV:}quota-available-bytes', '{DAV:}getetag', '{DAV:}getcontenttype', ]; } foreach ($this->properties as $propertyName) { // Seeding properties with 404's. $this->result[$propertyName] = [404, null]; } $this->itemsLeft = count($this->result); } /** * Handles a specific property. * * This method checks wether the specified property was requested in this * PROPFIND request, and if so, it will call the callback and use the * return value for it's value. * * Example: * * $propFind->handle('{DAV:}displayname', function() { * return 'hello'; * }); * * Note that handle will only work the first time. If null is returned, the * value is ignored. * * It's also possible to not pass a callback, but immediately pass a value * * @param string $propertyName * @param mixed $valueOrCallBack * @return void */ function handle($propertyName, $valueOrCallBack) { if ($this->itemsLeft && isset($this->result[$propertyName]) && $this->result[$propertyName][0] === 404) { if (is_callable($valueOrCallBack)) { $value = $valueOrCallBack(); } else { $value = $valueOrCallBack; } if (!is_null($value)) { $this->itemsLeft--; $this->result[$propertyName] = [200, $value]; } } } /** * Sets the value of the property * * If status is not supplied, the status will default to 200 for non-null * properties, and 404 for null properties. * * @param string $propertyName * @param mixed $value * @param int $status * @return void */ function set($propertyName, $value, $status = null) { if (is_null($status)) { $status = is_null($value) ? 404 : 200; } // If this is an ALLPROPS request and the property is // unknown, add it to the result; else ignore it: if (!isset($this->result[$propertyName])) { if ($this->requestType === self::ALLPROPS) { $this->result[$propertyName] = [$status, $value]; } return; } if ($status !== 404 && $this->result[$propertyName][0] === 404) { $this->itemsLeft--; } elseif ($status === 404 && $this->result[$propertyName][0] !== 404) { $this->itemsLeft++; } $this->result[$propertyName] = [$status, $value]; } /** * Returns the current value for a property. * * @param string $propertyName * @return mixed */ function get($propertyName) { return isset($this->result[$propertyName]) ? $this->result[$propertyName][1] : null; } /** * Returns the current status code for a property name. * * If the property does not appear in the list of requested properties, * null will be returned. * * @param string $propertyName * @return int|null */ function getStatus($propertyName) { return isset($this->result[$propertyName]) ? $this->result[$propertyName][0] : null; } /** * Updates the path for this PROPFIND. * * @param string $path * @return void */ function setPath($path) { $this->path = $path; } /** * Returns the path this PROPFIND request is for. * * @return string */ function getPath() { return $this->path; } /** * Returns the depth of this propfind request. * * @return int */ function getDepth() { return $this->depth; } /** * Updates the depth of this propfind request. * * @param int $depth * @return void */ function setDepth($depth) { $this->depth = $depth; } /** * Returns all propertynames that have a 404 status, and thus don't have a * value yet. * * @return array */ function get404Properties() { if ($this->itemsLeft === 0) { return []; } $result = []; foreach ($this->result as $propertyName => $stuff) { if ($stuff[0] === 404) { $result[] = $propertyName; } } return $result; } /** * Returns the full list of requested properties. * * This returns just their names, not a status or value. * * @return array */ function getRequestedProperties() { return $this->properties; } /** * Returns true if this was an '{DAV:}allprops' request. * * @return bool */ function isAllProps() { return $this->requestType === self::ALLPROPS; } /** * Returns a result array that's often used in multistatus responses. * * The array uses status codes as keys, and property names and value pairs * as the value of the top array.. such as : * * [ * 200 => [ '{DAV:}displayname' => 'foo' ], * ] * * @return array */ function getResultForMultiStatus() { $r = [ 200 => [], 404 => [], ]; foreach ($this->result as $propertyName => $info) { if (!isset($r[$info[0]])) { $r[$info[0]] = [$propertyName => $info[1]]; } else { $r[$info[0]][$propertyName] = $info[1]; } } // Removing the 404's for multi-status requests. if ($this->requestType === self::ALLPROPS) unset($r[404]); return $r; } /** * The path that we're fetching properties for. * * @var string */ protected $path; /** * The Depth of the request. * * 0 means only the current item. 1 means the current item + its children. * It can also be DEPTH_INFINITY if this is enabled in the server. * * @var int */ protected $depth = 0; /** * The type of request. See the TYPE constants */ protected $requestType; /** * A list of requested properties * * @var array */ protected $properties = []; /** * The result of the operation. * * The keys in this array are property names. * The values are an array with two elements: the http status code and then * optionally a value. * * Example: * * [ * "{DAV:}owner" : [404], * "{DAV:}displayname" : [200, "Admin"] * ] * * @var array */ protected $result = []; /** * This is used as an internal counter for the number of properties that do * not yet have a value. * * @var int */ protected $itemsLeft; }