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

52
					$spa->/** @scrutinizer ignore-call */ 
53
           SetFolderId($fid);
Loading history...
53
					$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

53
					$spa->/** @scrutinizer ignore-call */ 
54
           SetBackendFolderId(self::$deviceManager->GetBackendIdForFolderId($fid));
Loading history...
54
55
					if (!self::$decoder->getElementEndTag()) {
56
						return false;
57
					}
58
				}
59
60
				// conversation mode requested
61
				elseif (self::$decoder->getElementStartTag(SYNC_CONVERSATIONMODE)) {
62
					$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

62
					$spa->/** @scrutinizer ignore-call */ 
63
           SetConversationMode(true);
Loading history...
63
					if (($conversationmode = self::$decoder->getElementContent()) !== false) {
64
						$spa->SetConversationMode((bool) $conversationmode);
65
						if (!self::$decoder->getElementEndTag()) {
66
							return false;
67
						}
68
					}
69
				}
70
71
				// get items estimate does not necessarily send the folder type
72
				elseif (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERTYPE)) {
73
					$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

73
					$spa->/** @scrutinizer ignore-call */ 
74
           SetContentClass(self::$decoder->getElementContent());
Loading history...
74
75
					if (!self::$decoder->getElementEndTag()) {
76
						return false;
77
					}
78
				}
79
80
				// TODO AS 2.5 and filtertype not set
81
				elseif (self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) {
82
					$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

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

124
							$spa->/** @scrutinizer ignore-call */ 
125
             SetWindowSize($maxitems = self::$decoder->getElementContent());
Loading history...
125
							if (!self::$decoder->getElementEndTag()) {
126
								return false;
127
							}
128
						}
129
130
						$e = self::$decoder->peek();
131
						if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
132
							self::$decoder->getElementEndTag();
133
134
							break;
135
						}
136
					}
137
				}
138
139
				$e = self::$decoder->peek();
140
				if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
141
					self::$decoder->getElementEndTag(); // SYNC_GETITEMESTIMATE_FOLDER
142
143
					break;
144
				}
145
			}
146
			// Process folder data
147
148
			// In AS 14 request only collectionid is sent, without class
149
			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

149
			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

149
			if (!$spa->HasContentClass() && $spa->/** @scrutinizer ignore-call */ HasFolderId()) {
Loading history...
150
				try {
151
					$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

151
					$spa->SetContentClass(self::$deviceManager->GetFolderClassFromCacheByID($spa->/** @scrutinizer ignore-call */ GetFolderId()));
Loading history...
152
				}
153
				catch (NoHierarchyCacheAvailableException $nhca) {
154
					$spastatus = SYNC_GETITEMESTSTATUS_COLLECTIONINVALID;
155
				}
156
			}
157
158
			// compatibility mode AS 1.0 - get folderid which was sent during GetHierarchy()
159
			if (!$spa->HasFolderId() && $spa->HasContentClass()) {
160
				$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

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