GetItemEstimate::Handle()   F
last analyzed

Complexity

Conditions 49
Paths > 20000

Size

Total Lines 252
Code Lines 136

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 49
eloc 136
nc 1956698
nop 1
dl 0
loc 252
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * SPDX-License-Identifier: AGPL-3.0-only
5
 * SPDX-FileCopyrightText: Copyright 2007-2016 Zarafa Deutschland GmbH
6
 * SPDX-FileCopyrightText: Copyright 2020-2022 grommunio GmbH
7
 *
8
 * Provides the GETITEMESTIMATE command
9
 */
10
11
class GetItemEstimate extends RequestProcessor {
12
	/**
13
	 * Handles the GetItemEstimate command
14
	 * Returns an estimation of how many items will be synchronized at the next sync
15
	 * This is mostly used to show something in the progress bar.
16
	 *
17
	 * @param int $commandCode
18
	 *
19
	 * @return bool
20
	 */
21
	public function Handle($commandCode) {
22
		$sc = new SyncCollections();
23
24
		if (!self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_GETITEMESTIMATE)) {
25
			return false;
26
		}
27
28
		if (!self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERS)) {
29
			return false;
30
		}
31
32
		while (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDER)) {
33
			$spa = new SyncParameters();
34
			$spastatus = false;
35
36
			// read the folder properties
37
			WBXMLDecoder::ResetInWhile("getItemEstimateFolders");
38
			while (WBXMLDecoder::InWhile("getItemEstimateFolders")) {
39
				if (self::$decoder->getElementStartTag(SYNC_SYNCKEY)) {
40
					try {
41
						$spa->SetSyncKey(self::$decoder->getElementContent());
42
					}
43
					catch (StateInvalidException) {
44
						$spastatus = SYNC_GETITEMESTSTATUS_SYNCSTATENOTPRIMED;
45
					}
46
47
					if (!self::$decoder->getElementEndTag()) {
48
						return false;
49
					}
50
				}
51
				elseif (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERID)) {
52
					$fid = self::$decoder->getElementContent();
53
					$spa->SetFolderId($fid);
0 ignored issues
show
Bug introduced by
The method SetFolderId() does not exist on SyncParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

53
					$spa->/** @scrutinizer ignore-call */ 
54
           SetFolderId($fid);
Loading history...
54
					$spa->SetBackendFolderId(self::$deviceManager->GetBackendIdForFolderId($fid));
0 ignored issues
show
Bug introduced by
The method SetBackendFolderId() does not exist on SyncParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

54
					$spa->/** @scrutinizer ignore-call */ 
55
           SetBackendFolderId(self::$deviceManager->GetBackendIdForFolderId($fid));
Loading history...
55
56
					if (!self::$decoder->getElementEndTag()) {
57
						return false;
58
					}
59
				}
60
61
				// conversation mode requested
62
				elseif (self::$decoder->getElementStartTag(SYNC_CONVERSATIONMODE)) {
63
					$spa->SetConversationMode(true);
0 ignored issues
show
Bug introduced by
The method SetConversationMode() does not exist on SyncParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

63
					$spa->/** @scrutinizer ignore-call */ 
64
           SetConversationMode(true);
Loading history...
64
					if (($conversationmode = self::$decoder->getElementContent()) !== false) {
65
						$spa->SetConversationMode((bool) $conversationmode);
66
						if (!self::$decoder->getElementEndTag()) {
67
							return false;
68
						}
69
					}
70
				}
71
72
				// get items estimate does not necessarily send the folder type
73
				elseif (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERTYPE)) {
74
					$spa->SetContentClass(self::$decoder->getElementContent());
0 ignored issues
show
Bug introduced by
The method SetContentClass() does not exist on SyncParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

74
					$spa->/** @scrutinizer ignore-call */ 
75
           SetContentClass(self::$decoder->getElementContent());
Loading history...
75
76
					if (!self::$decoder->getElementEndTag()) {
77
						return false;
78
					}
79
				}
80
81
				// TODO AS 2.5 and filtertype not set
82
				elseif (self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) {
83
					$spa->SetFilterType(self::$decoder->getElementContent());
0 ignored issues
show
Bug introduced by
The method SetFilterType() does not exist on SyncParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

83
					$spa->/** @scrutinizer ignore-call */ 
84
           SetFilterType(self::$decoder->getElementContent());
Loading history...
84
85
					if (!self::$decoder->getElementEndTag()) {
86
						return false;
87
					}
88
				}
89
90
				while (self::$decoder->getElementStartTag(SYNC_OPTIONS)) {
91
					WBXMLDecoder::ResetInWhile("getItemEstimateOptions");
92
					while (WBXMLDecoder::InWhile("getItemEstimateOptions")) {
93
						$firstOption = true;
94
						// foldertype definition
95
						if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
96
							$foldertype = self::$decoder->getElementContent();
97
							SLog::Write(LOGLEVEL_DEBUG, sprintf("HandleGetItemEstimate(): specified options block with foldertype '%s'", $foldertype));
98
99
							// switch the foldertype for the next options
100
							$spa->UseCPO($foldertype);
101
102
							// set to synchronize all changes. The mobile could overwrite this value
103
							$spa->SetFilterType(SYNC_FILTERTYPE_ALL);
104
105
							if (!self::$decoder->getElementEndTag()) {
106
								return false;
107
							}
108
						}
109
						// if no foldertype is defined, use default cpo
110
						elseif ($firstOption) {
111
							$spa->UseCPO();
112
							// set to synchronize all changes. The mobile could overwrite this value
113
							$spa->SetFilterType(SYNC_FILTERTYPE_ALL);
114
						}
115
						$firstOption = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $firstOption is dead and can be removed.
Loading history...
116
117
						if (self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) {
118
							$spa->SetFilterType(self::$decoder->getElementContent());
119
							if (!self::$decoder->getElementEndTag()) {
120
								return false;
121
							}
122
						}
123
124
						if (self::$decoder->getElementStartTag(SYNC_MAXITEMS)) {
125
							$spa->SetWindowSize($maxitems = self::$decoder->getElementContent());
0 ignored issues
show
Bug introduced by
The method SetWindowSize() does not exist on SyncParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

125
							$spa->/** @scrutinizer ignore-call */ 
126
             SetWindowSize($maxitems = self::$decoder->getElementContent());
Loading history...
126
							if (!self::$decoder->getElementEndTag()) {
127
								return false;
128
							}
129
						}
130
131
						$e = self::$decoder->peek();
132
						if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
133
							self::$decoder->getElementEndTag();
134
135
							break;
136
						}
137
					}
138
				}
139
140
				$e = self::$decoder->peek();
141
				if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
142
					self::$decoder->getElementEndTag(); // SYNC_GETITEMESTIMATE_FOLDER
143
144
					break;
145
				}
146
			}
147
			// Process folder data
148
149
			// In AS 14 request only collectionid is sent, without class
150
			if (!$spa->HasContentClass() && $spa->HasFolderId()) {
0 ignored issues
show
Bug introduced by
The method HasContentClass() does not exist on SyncParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

150
			if (!$spa->/** @scrutinizer ignore-call */ HasContentClass() && $spa->HasFolderId()) {
Loading history...
Bug introduced by
The method HasFolderId() does not exist on SyncParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

150
			if (!$spa->HasContentClass() && $spa->/** @scrutinizer ignore-call */ HasFolderId()) {
Loading history...
151
				try {
152
					$spa->SetContentClass(self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId()));
0 ignored issues
show
Bug introduced by
The method GetFolderId() does not exist on SyncParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

152
					$spa->SetContentClass(self::$deviceManager->GetFolderClassFromCacheByID($spa->/** @scrutinizer ignore-call */ GetFolderId()));
Loading history...
153
				}
154
				catch (NoHierarchyCacheAvailableException) {
155
					$spastatus = SYNC_GETITEMESTSTATUS_COLLECTIONINVALID;
156
				}
157
			}
158
159
			// compatibility mode AS 1.0 - get folderid which was sent during GetHierarchy()
160
			if (!$spa->HasFolderId() && $spa->HasContentClass()) {
161
				$spa->SetFolderId(self::$deviceManager->GetFolderIdFromCacheByClass($spa->GetContentClass()));
0 ignored issues
show
Bug introduced by
The method GetContentClass() does not exist on SyncParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

161
				$spa->SetFolderId(self::$deviceManager->GetFolderIdFromCacheByClass($spa->/** @scrutinizer ignore-call */ GetContentClass()));
Loading history...
162
			}
163
164
			// Add collection to SC and load state
165
			$sc->AddCollection($spa);
166
			if ($spastatus) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $spastatus of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
167
				// the CPO has a folder id now, so we can set the status
168
				$sc->AddParameter($spa, "status", $spastatus);
169
			}
170
			else {
171
				try {
172
					$sc->AddParameter($spa, "state", self::$deviceManager->GetStateManager()->GetSyncState($spa->GetSyncKey()));
173
174
					// if this is an additional folder the backend has to be setup correctly
175
					if (!self::$backend->Setup(GSync::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()))) {
176
						throw new StatusException(sprintf("HandleGetItemEstimate() could not Setup() the backend for folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_GETITEMESTSTATUS_COLLECTIONINVALID);
177
					}
178
				}
179
				catch (StateNotFoundException) {
180
					// ok, the key is invalid. Question is, if the hierarchycache is still ok
181
					// if not, we have to issue SYNC_GETITEMESTSTATUS_COLLECTIONINVALID which triggers a FolderSync
182
					try {
183
						self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId());
184
						// we got here, so the HierarchyCache is ok
185
						$sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_SYNCKKEYINVALID);
186
					}
187
					catch (NoHierarchyCacheAvailableException) {
188
						$sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_COLLECTIONINVALID);
189
					}
190
191
					self::$topCollector->AnnounceInformation("StateNotFoundException " . $sc->GetParameter($spa, "status"), true);
192
				}
193
				catch (StatusException $stex) {
194
					if ($stex->getCode() == SYNC_GETITEMESTSTATUS_COLLECTIONINVALID) {
195
						$sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_COLLECTIONINVALID);
196
					}
197
					else {
198
						$sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_SYNCSTATENOTPRIMED);
199
					}
200
					self::$topCollector->AnnounceInformation("StatusException " . $sc->GetParameter($spa, "status"), true);
201
				}
202
			}
203
		}
204
		if (!self::$decoder->getElementEndTag()) {
205
			return false;
206
		} // SYNC_GETITEMESTIMATE_FOLDERS
207
208
		if (!self::$decoder->getElementEndTag()) {
209
			return false;
210
		} // SYNC_GETITEMESTIMATE_GETITEMESTIMATE
211
212
		self::$encoder->startWBXML();
213
		self::$encoder->startTag(SYNC_GETITEMESTIMATE_GETITEMESTIMATE);
214
215
		$status = SYNC_GETITEMESTSTATUS_SUCCESS;
216
		// look for changes in all collections
217
218
		try {
219
			$sc->CountChanges();
220
		}
221
		catch (StatusException) {
222
			$status = SYNC_GETITEMESTSTATUS_COLLECTIONINVALID;
223
		}
224
		$changes = $sc->GetChangedFolderIds();
225
226
		foreach ($sc as $folderid => $spa) {
227
			self::$encoder->startTag(SYNC_GETITEMESTIMATE_RESPONSE);
228
229
			if ($sc->GetParameter($spa, "status")) {
230
				$status = $sc->GetParameter($spa, "status");
231
			}
232
233
			self::$encoder->startTag(SYNC_GETITEMESTIMATE_STATUS);
234
			self::$encoder->content($status);
235
			self::$encoder->endTag();
236
237
			self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDER);
238
239
			self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDERTYPE);
240
			self::$encoder->content($spa->GetContentClass());
241
			self::$encoder->endTag();
242
243
			self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDERID);
244
			self::$encoder->content($spa->GetFolderId());
245
			self::$encoder->endTag();
246
247
			if (isset($changes[$folderid]) && $changes[$folderid] !== false) {
248
				self::$encoder->startTag(SYNC_GETITEMESTIMATE_ESTIMATE);
249
				self::$encoder->content($changes[$folderid]);
250
				self::$encoder->endTag();
251
252
				if ($changes[$folderid] > 0) {
253
					self::$topCollector->AnnounceInformation(sprintf("%s %d changes", $spa->GetContentClass(), $changes[$folderid]), true);
254
				}
255
256
				// update the device data to mark folders as complete when syncing with WM
257
				if ($changes[$folderid] == 0) {
258
					self::$deviceManager->SetFolderSyncStatus($folderid, DeviceManager::FLD_SYNC_COMPLETED);
259
				}
260
			}
261
262
			self::$encoder->endTag();
263
264
			self::$encoder->endTag();
265
		}
266
		if (array_sum($changes) == 0) {
267
			self::$topCollector->AnnounceInformation("No changes found", true);
268
		}
269
270
		self::$encoder->endTag();
271
272
		return true;
273
	}
274
}
275