xref: /plugin/davcal/vendor/sabre/dav/lib/DAV/PropFind.php (revision a1a3b6794e0e143a4a8b51d3185ce2d339be61ab)
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