Passed
Push — master ( ce4559...4d7880 )
by
unknown
14:44 queued 13s
created

IndexSqlite::bind_insert()   C

Complexity

Conditions 9
Paths 256

Size

Total Lines 73
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 41
c 1
b 0
f 0
nc 256
nop 14
dl 0
loc 73
rs 6.4817

How to fix   Long Method    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
define('PRIVATE_FID_ROOT', 0x1);
4
5
define('PR_FOLDER_ID', 0x67480014);
6
define('PR_MID', 0x674A0014);
7
define('PR_CHANGE_NUMBER', 0x67A40014);
8
9
class IndexSqlite extends SQLite3 {
10
	private $username;
11
	private $count;
12
	private $store;
13
	private $session;
14
15
	private static function get_gc_value($eid) {
16
		$r0 = ($eid >> 56) & 0xFF;
17
		$r1 = ($eid >> 48) & 0xFF;
18
		$r2 = ($eid >> 40) & 0xFF;
19
		$r3 = ($eid >> 32) & 0xFF;
20
		$r4 = ($eid >> 24) & 0xFF;
21
		$r5 = ($eid >> 16) & 0xFF;
22
		$value = $r0 | ($r1 << 8) | ($r2 << 16) | ($r3 << 24) | ($r4 << 32) | ($r5 << 40);
23
24
		return $value;
25
	}
26
27
	public function __construct($username = null, $session = null, $store = null) {
28
		$this->username = $username ?? $GLOBALS["mapisession"]->getSMTPAddress();
29
		$this->session = $session ?? $GLOBALS["mapisession"]->getSession();
30
		$this->store = $store ?? $GLOBALS["mapisession"]->getDefaultMessageStore();
31
		$this->open(SQLITE_INDEX_PATH . '/' . $this->username . '/index.sqlite3');
32
	}
33
34
	private function try_insert_content(
35
		$search_entryid,
36
		$row,
37
		$message_classes,
38
		$date_start,
39
		$date_end,
40
		$unread,
41
		$has_attachments
42
	) {
43
		// if match condition contains '@', $row['entryid'] will disappear. it seems a bug for php-sqlite
44
		if (strlen($row['entryid']) == 0) {
45
			$results = $this->query("SELECT entryid FROM messages WHERE message_id=" . $row['message_id']);
46
			$row1 = $results->fetchArray(SQLITE3_NUM);
47
			if ($row1) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $row1 of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
48
				$row['entryid'] = $row1[0];
49
			}
50
		}
51
		if (isset($message_classes)) {
52
			$found = false;
53
			foreach ($message_classes as $message_class) {
54
				if (strncasecmp($row['message_class'], $message_class, strlen($message_class)) == 0) {
55
					$found = true;
56
					break;
57
				}
58
			}
59
			if (!$found) {
60
				return;
61
			}
62
		}
63
		if (isset($date_start) && $row['date'] < $date_start) {
64
			return;
65
		}
66
		if (isset($date_end) && $row['date'] > $date_end) {
67
			return;
68
		}
69
		if (isset($unread) && $row['readflag']) {
70
			return;
71
		}
72
		if (isset($has_attachments) && !$row['attach_indexed']) {
73
			return;
74
		}
75
76
		try {
77
			mapi_linkmessage($this->session, $search_entryid, $row['entryid']);
78
		}
79
		catch (Exception $e) {
80
			return;
81
		}
82
		++$this->count;
83
	}
84
85
	private function result_full() {
86
		return $this->count >= MAX_FTS_RESULT_ITEMS;
87
	}
88
89
	public function search($search_entryid, $search_patterns, $folder_entryid, $recursive) {
90
		$whereFolderids = '';
91
		if (isset($folder_entryid)) {
92
			try {
93
				$folder = mapi_msgstore_openentry($this->store, $folder_entryid);
94
				if (!$folder) {
95
					return false;
96
				}
97
				$tmp_props = mapi_getprops($folder, [PR_FOLDER_ID]);
98
				if (empty($tmp_props[PR_FOLDER_ID])) {
99
					return false;
100
				}
101
				$folder_id = IndexSqlite::get_gc_value((int) $tmp_props[PR_FOLDER_ID]);
102
				$whereFolderids .= "c.folder_id in (" . $folder_id . ", ";
103
				if ($recursive) {
104
					$this->getWhereFolderids($folder, $whereFolderids);
105
				}
106
				$whereFolderids = substr($whereFolderids, 0, -2) . ") AND ";
107
			}
108
			catch (Exception $e) {
109
				error_log(sprintf("Index: error getting folder information %s - %s", $this->username, $e));
110
111
				return false;
112
			}
113
		}
114
		$sql_string = "SELECT c.message_id, c.entryid, c.folder_id, " .
115
			"c.message_class, c.date, c.readflag, c.attach_indexed " .
116
			"FROM msg_content c " .
117
			"JOIN messages m ON c.message_id = m.rowid " .
118
			"WHERE ";
119
		if (!empty($whereFolderids)) {
120
			$sql_string .= $whereFolderids;
121
		}
122
		$sql_string .= "messages MATCH '";
123
		$this->count = 0;
124
		// Extract search_patterns into separate variables
125
		[
126
			'sender' => $sender,
127
			'sending' => $sending,
128
			'recipients' => $recipients,
129
			'subject' => $subject,
130
			'content' => $content,
131
			'attachments' => $attachments,
132
			'others' => $others,
133
			'message_classes' => $message_classes,
134
			'date_start' => $date_start,
135
			'date_end' => $date_end,
136
			'unread' => $unread,
137
			'has_attachments' => $has_attachments
138
		] = $search_patterns;
139
		if (isset($sender) && $sender == $sending && $sending == $recipients && $recipients == $subject &&
140
			$subject == $content && $content == $attachments && $attachments == $others) {
141
			$sql_string .= SQLite3::escapeString($this->quote_words($sender)) . "'";
142
		}
143
		else {
144
			$first = true;
145
			foreach ($search_patterns as $key => $search_pattern) {
146
				switch($key) {
147
					case 'message_classes':
148
					case 'date_start':
149
					case 'date_end':
150
					case 'unread':
151
					case 'has_attachments':
152
						break;
153
					default:
154
						if (!is_null($search_pattern)) {
155
							if ($first === true) {
156
								$first = false;
157
							}
158
							else {
159
								$sql_string .= " OR ";
160
							}
161
							$sql_string .= $key . ':' . SQLite3::escapeString($this->quote_words($search_pattern));
162
						}
163
				}
164
			}
165
			if ($first) {
166
				return false;
167
			}
168
			$sql_string .= "'";
169
		}
170
		$sql_string .= " ORDER BY c.date DESC LIMIT " . MAX_FTS_RESULT_ITEMS;
171
		$results = $this->query($sql_string);
172
		while (($row = $results->fetchArray(SQLITE3_ASSOC)) && !$this->result_full()) {
173
			$this->try_insert_content(
174
				$search_entryid,
175
				$row,
176
				$message_classes,
177
				$date_start,
178
				$date_end,
179
				$unread,
180
				$has_attachments
181
			);
182
		}
183
184
		return true;
185
	}
186
187
	private function quote_words($search_string) {
188
		return '"' . preg_replace("/(\\s+)/", '*" "', trim($search_string)) . '"*';
189
	}
190
191
	/**
192
	 * Returns the restriction to filter hidden folders.
193
	 *
194
	 * @return array
195
	 */
196
	private function getHiddenRestriction() {
197
		return
198
			[RES_OR, [
199
				[RES_PROPERTY,
200
					[
201
						RELOP => RELOP_EQ,
202
						ULPROPTAG => PR_ATTR_HIDDEN,
203
						VALUE => [PR_ATTR_HIDDEN => false],
204
					],
205
				],
206
				[RES_NOT,
207
					[
208
						[RES_EXIST,
209
							[
210
								ULPROPTAG => PR_ATTR_HIDDEN,
211
							],
212
						],
213
					],
214
				],
215
			]];
216
	}
217
218
	/**
219
	 * Returns the comma joined folderids for the WHERE clause in the SQL
220
	 * statement.
221
	 *
222
	 * @param mixed  $folder
223
	 * @param string $whereFolderids
224
	 */
225
	private function getWhereFolderids($folder, &$whereFolderids) {
226
		/**
227
		 * remove hidden folders, folders with PR_ATTR_HIDDEN property set
228
		 * should not be shown to the client.
229
		 */
230
		$restriction = $this->getHiddenRestriction();
231
		$hierarchy = mapi_folder_gethierarchytable($folder, CONVENIENT_DEPTH | MAPI_DEFERRED_ERRORS);
232
		mapi_table_restrict($hierarchy, $restriction, TBL_BATCH);
233
		$rows = mapi_table_queryallrows($hierarchy, [PR_FOLDER_ID]);
234
		foreach ($rows as $row) {
235
			if (isset($row[PR_FOLDER_ID])) {
236
				$whereFolderids .= IndexSqlite::get_gc_value((int) $row[PR_FOLDER_ID]) . ", ";
237
			}
238
		}
239
	}
240
}
241