1<?php
2
3namespace Sabre\DAV\Sync;
4
5use Sabre\DAV;
6
7/**
8 * This mocks a ISyncCollection, for unittesting.
9 *
10 * This object behaves the same as SimpleCollection. Call addChange to update
11 * the 'changelog' that this class uses for the collection.
12 *
13 * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/).
14 * @author Evert Pot (http://evertpot.com/)
15 * @license http://sabre.io/license/ Modified BSD License
16 */
17class MockSyncCollection extends DAV\SimpleCollection implements ISyncCollection {
18
19    public $changeLog = [];
20
21    public $token = null;
22
23    /**
24     * This method returns the current sync-token for this collection.
25     * This can be any string.
26     *
27     * If null is returned from this function, the plugin assumes there's no
28     * sync information available.
29     *
30     * @return string|null
31     */
32    public function getSyncToken() {
33
34        // Will be 'null' in the first round, and will increment ever after.
35        return $this->token;
36
37    }
38
39    public function addChange(array $added, array $modified, array $deleted) {
40
41        $this->token++;
42        $this->changeLog[$this->token] = [
43            'added'     => $added,
44            'modified'  => $modified,
45            'deleted'   => $deleted,
46        ];
47
48    }
49
50    /**
51     * The getChanges method returns all the changes that have happened, since
52     * the specified syncToken and the current collection.
53     *
54     * This function should return an array, such as the following:
55     *
56     * array(
57     *   'syncToken' => 'The current synctoken',
58     *   'modified'   => array(
59     *      'new.txt',
60     *   ),
61     *   'deleted' => array(
62     *      'foo.php.bak',
63     *      'old.txt'
64     *   )
65     * );
66     *
67     * The syncToken property should reflect the *current* syncToken of the
68     * collection, as reported getSyncToken(). This is needed here too, to
69     * ensure the operation is atomic.
70     *
71     * If the syncToken is specified as null, this is an initial sync, and all
72     * members should be reported.
73     *
74     * The modified property is an array of nodenames that have changed since
75     * the last token.
76     *
77     * The deleted property is an array with nodenames, that have been deleted
78     * from collection.
79     *
80     * The second argument is basically the 'depth' of the report. If it's 1,
81     * you only have to report changes that happened only directly in immediate
82     * descendants. If it's 2, it should also include changes from the nodes
83     * below the child collections. (grandchildren)
84     *
85     * The third (optional) argument allows a client to specify how many
86     * results should be returned at most. If the limit is not specified, it
87     * should be treated as infinite.
88     *
89     * If the limit (infinite or not) is higher than you're willing to return,
90     * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
91     *
92     * If the syncToken is expired (due to data cleanup) or unknown, you must
93     * return null.
94     *
95     * The limit is 'suggestive'. You are free to ignore it.
96     *
97     * @param string $syncToken
98     * @param int $syncLevel
99     * @param int $limit
100     * @return array
101     */
102    public function getChanges($syncToken, $syncLevel, $limit = null) {
103
104        // This is an initial sync
105        if (is_null($syncToken)) {
106            return [
107               'added' => array_map(
108                    function($item) {
109                        return $item->getName();
110                    }, $this->getChildren()
111                ),
112                'modified' => [],
113                'deleted' => [],
114                'syncToken' => $this->getSyncToken(),
115            ];
116        }
117
118        if (!is_int($syncToken) && !ctype_digit($syncToken)) {
119
120            return null;
121
122        }
123        if (is_null($this->token)) return null;
124
125        $added    = [];
126        $modified = [];
127        $deleted  = [];
128
129        foreach($this->changeLog as $token=>$change) {
130
131            if ($token > $syncToken) {
132
133                $added = array_merge($added, $change['added']);
134                $modified = array_merge($modified, $change['modified']);
135                $deleted = array_merge($deleted, $change['deleted']);
136
137                if ($limit) {
138                    // If there's a limit, we may need to cut things off.
139                    // This alghorithm is weird and stupid, but it works.
140                    $left = $limit - (count($modified) + count($deleted));
141                    if ($left>0) continue;
142                    if ($left===0) break;
143                    if ($left<0) {
144                        $modified = array_slice($modified, 0, $left);
145                    }
146                    $left = $limit - (count($modified) + count($deleted));
147                    if ($left===0) break;
148                    if ($left<0) {
149                        $deleted = array_slice($deleted, 0, $left);
150                    }
151                    break;
152
153                }
154
155            }
156
157        }
158
159        return array(
160            'syncToken' => $this->token,
161            'added'     => $added,
162            'modified'  => $modified,
163            'deleted'   => $deleted,
164        );
165
166    }
167
168
169}
170