pm_search_fulltext_native::index_remove()   B
last analyzed

Complexity

Conditions 6
Paths 5

Size

Total Lines 52
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 27
c 1
b 0
f 0
nc 5
nop 3
dl 0
loc 52
ccs 0
cts 26
cp 0
crap 42
rs 8.8657

How to fix   Long Method   

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
* This file is part of the phpBB Forum Software package.
5
*
6
* @copyright (c) phpBB Limited <https://www.phpbb.com>
7
* @license GNU General Public License, version 2 (GPL-2.0)
8
*
9
* For full copyright and license information, please see
10
* the docs/CREDITS.txt file.
11
*
12
*/
13
14
namespace anavaro\pmsearch\search;
15
/**
16
* phpBB's own db driven fulltext search, version 2
17
*/
18
class pm_search_fulltext_native extends \phpbb\search\fulltext_native
0 ignored issues
show
Bug introduced by
The type phpbb\search\fulltext_native was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
{
20
	protected $target;
21
22
	/**
23
	* Returns the name of this search backend to be displayed to administrators
24
	*
25
	* @return string Name
26
	*/
27
	public function get_name($type = 'normal')
28
	{
29
		switch ($type)
30
		{
31
			case ('normal'):
32
				return 'phpBB Native Fulltext PM Normal';
33
			break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
34
		}
35
	}
36
37
	public function get_common_words()
38
	{
39
		return $this->common_words;
40
	}
41
42
	/**
43
	 * Returns the word_length array
44
	 *
45
	 * @return array min and max word length for searching
46
	 */
47
	public function get_word_length()
48
	{
49
		return $this->word_length;
50
	}
51
52
	/**
53
	* This function fills $this->search_query with the cleaned user search query
54
	*
55
	* If $terms is 'any' then the words will be extracted from the search query
56
	* and combined with | inside brackets. They will afterwards be treated like
57
	* an standard search query.
58
	*
59
	* Then it analyses the query and fills the internal arrays $must_not_contain_ids,
60
	* $must_contain_ids and $must_exclude_one_ids which are later used by keyword_search()
61
	*
62
	* @param	string	$keywords	contains the search query string as entered by the user
63
	* @param	string	$terms		is either 'all' (use search query as entered, default words to 'must be contained in post')
64
	* 	or 'any' (find all posts containing at least one of the given words)
65
	* @return	boolean				false if no valid keywords were found and otherwise true
66
	*/
67
	public function split_keywords($keywords, $terms)
68
	{
69
		$swl_table = PRIVMSGS_TABLE . '_swl';
0 ignored issues
show
Bug introduced by
The constant anavaro\pmsearch\search\PRIVMSGS_TABLE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
70
		$swm_table = PRIVMSGS_TABLE . '_swm';
0 ignored issues
show
Unused Code introduced by
The assignment to $swm_table is dead and can be removed.
Loading history...
71
72
		$tokens = '+-|()*';
73
74
		$keywords = trim($this->cleanup($keywords, $tokens));
75
76
		// allow word|word|word without brackets
77
		if ((strpos($keywords, ' ') === false) && (strpos($keywords, '|') !== false) && (strpos($keywords, '(') === false))
78
		{
79
			$keywords = '(' . $keywords . ')';
80
		}
81
82
		$open_bracket = $space = false;
83
		for ($i = 0, $n = strlen($keywords); $i < $n; $i++)
84
		{
85
			if ($open_bracket !== false)
86
			{
87
				switch ($keywords[$i])
88
				{
89
					case ')':
90
						if ($open_bracket + 1 == $i)
91
						{
92
							$keywords[$i - 1] = '|';
93
							$keywords[$i] = '|';
94
						}
95
						$open_bracket = false;
96
					break;
97
					case '(':
98
						$keywords[$i] = '|';
99
					break;
100
					case '+':
101
					case '-':
102
					case ' ':
103
						$keywords[$i] = '|';
104
					break;
105
					case '*':
106
						if ($i === 0 || ($keywords[$i - 1] !== '*' && strcspn($keywords[$i - 1], $tokens) === 0))
107
						{
108
							if ($i === $n - 1 || ($keywords[$i + 1] !== '*' && strcspn($keywords[$i + 1], $tokens) === 0))
109
							{
110
								$keywords = substr($keywords, 0, $i) . substr($keywords, $i + 1);
111
							}
112
						}
113
					break;
114
				}
115
			}
116
			else
117
			{
118
				switch ($keywords[$i])
119
				{
120
					case ')':
121
						$keywords[$i] = ' ';
122
					break;
123
					case '(':
124
						$open_bracket = $i;
125
						$space = false;
126
					break;
127
					case '|':
128
						$keywords[$i] = ' ';
129
					break;
130
					case '-':
131
					case '+':
132
						$space = $keywords[$i];
133
					break;
134
					case ' ':
135
						if ($space !== false)
136
						{
137
							$keywords[$i] = $space;
138
						}
139
					break;
140
					default:
141
						$space = false;
142
				}
143
			}
144
		}
145
146
		if ($open_bracket)
0 ignored issues
show
introduced by
The condition $open_bracket is always false.
Loading history...
147
		{
148
			$keywords .= ')';
149
		}
150
151
		$match = array(
152
			'#  +#',
153
			'#\|\|+#',
154
			'#(\+|\-)(?:\+|\-)+#',
155
			'#\(\|#',
156
			'#\|\)#',
157
		);
158
		$replace = array(
159
			' ',
160
			'|',
161
			'$1',
162
			'(',
163
			')',
164
		);
165
166
		$keywords = preg_replace($match, $replace, $keywords);
167
		$num_keywords = sizeof(explode(' ', $keywords));
168
169
		// We limit the number of allowed keywords to minimize load on the database
170
		if ($this->config['max_num_search_keywords'] && $num_keywords > $this->config['max_num_search_keywords'])
171
		{
172
			trigger_error($this->user->lang('MAX_NUM_SEARCH_KEYWORDS_REFINE', (int) $this->config['max_num_search_keywords'], $num_keywords));
173
		}
174
175
		// $keywords input format: each word separated by a space, words in a bracket are not separated
176
177
		// the user wants to search for any word, convert the search query
178
		if ($terms == 'any')
179
		{
180
			$words = array();
181
182
			preg_match_all('#([^\\s+\\-|()]+)(?:$|[\\s+\\-|()])#u', $keywords, $words);
183
			if (sizeof($words[1]))
184
			{
185
				$keywords = '(' . implode('|', $words[1]) . ')';
186
			}
187
		}
188
189
		// set the search_query which is shown to the user
190
		$this->search_query = $keywords;
0 ignored issues
show
Bug Best Practice introduced by
The property search_query does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
191
192
		$exact_words = array();
193
		preg_match_all('#([^\\s+\\-|()]+)(?:$|[\\s+\\-|()])#u', $keywords, $exact_words);
194
		$exact_words = $exact_words[1];
195
196
		$common_ids = $words = array();
197
198
		if (sizeof($exact_words))
199
		{
200
			$sql = 'SELECT word_id, word_text, word_common
201
				FROM ' . $swl_table . '
202
				WHERE ' . $this->db->sql_in_set('word_text', $exact_words) . '
203
				ORDER BY word_count ASC';
204
			$result = $this->db->sql_query($sql);
205
206
			// store an array of words and ids, remove common words
207
			while ($row = $this->db->sql_fetchrow($result))
208
			{
209
				if ($row['word_common'])
210
				{
211
					$this->common_words[] = $row['word_text'];
0 ignored issues
show
Bug Best Practice introduced by
The property common_words does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
212
					$common_ids[$row['word_text']] = (int) $row['word_id'];
213
					continue;
214
				}
215
216
				$words[$row['word_text']] = (int) $row['word_id'];
217
			}
218
			$this->db->sql_freeresult($result);
219
		}
220
221
		// Handle +, - without preceeding whitespace character
222
		$match		= array('#(\S)\+#', '#(\S)-#');
223
		$replace	= array('$1 +', '$1 +');
224
225
		$keywords = preg_replace($match, $replace, $keywords);
226
227
		// now analyse the search query, first split it using the spaces
228
		$query = explode(' ', $keywords);
229
230
		$this->must_contain_ids = array();
0 ignored issues
show
Bug Best Practice introduced by
The property must_contain_ids does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
231
		$this->must_not_contain_ids = array();
0 ignored issues
show
Bug Best Practice introduced by
The property must_not_contain_ids does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
232
		$this->must_exclude_one_ids = array();
0 ignored issues
show
Bug Best Practice introduced by
The property must_exclude_one_ids does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
233
234
		foreach ($query as $word)
235
		{
236
			if (empty($word))
237
			{
238
				continue;
239
			}
240
241
			// words which should not be included
242
			if ($word[0] == '-')
243
			{
244
				$word = substr($word, 1);
245
246
				// a group of which at least one may not be in the resulting posts
247
				if ($word[0] == '(')
248
				{
249
					$word = array_unique(explode('|', substr($word, 1, -1)));
250
					$mode = 'must_exclude_one';
251
				}
252
				// one word which should not be in the resulting posts
253
				else
254
				{
255
					$mode = 'must_not_contain';
256
				}
257
				$ignore_no_id = true;
258
			}
259
			// words which have to be included
260
			else
261
			{
262
				// no prefix is the same as a +prefix
263
				if ($word[0] == '+')
264
				{
265
					$word = substr($word, 1);
266
				}
267
268
				// a group of words of which at least one word should be in every resulting post
269
				if ($word[0] == '(')
270
				{
271
					$word = array_unique(explode('|', substr($word, 1, -1)));
272
				}
273
				$ignore_no_id = false;
274
				$mode = 'must_contain';
275
			}
276
277
			if (empty($word))
278
			{
279
				continue;
280
			}
281
282
			// if this is an array of words then retrieve an id for each
283
			if (is_array($word))
284
			{
285
				$non_common_words = array();
286
				$id_words = array();
287
				foreach ($word as $i => $word_part)
288
				{
289
					if (strpos($word_part, '*') !== false)
290
					{
291
						$id_words[] = '\'' . $this->db->sql_escape(str_replace('*', '%', $word_part)) . '\'';
292
						$non_common_words[] = $word_part;
293
					}
294
					else if (isset($words[$word_part]))
295
					{
296
						$id_words[] = $words[$word_part];
297
						$non_common_words[] = $word_part;
298
					}
299
					else
300
					{
301
						$len = utf8_strlen($word_part);
0 ignored issues
show
Bug introduced by
The function utf8_strlen was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

301
						$len = /** @scrutinizer ignore-call */ utf8_strlen($word_part);
Loading history...
302
						if ($len < $this->word_length['min'] || $len > $this->word_length['max'])
303
						{
304
							$this->common_words[] = $word_part;
305
						}
306
					}
307
				}
308
				if (sizeof($id_words))
309
				{
310
					sort($id_words);
311
					if (sizeof($id_words) > 1)
312
					{
313
						$this->{$mode . '_ids'}[] = $id_words;
314
					}
315
					else
316
					{
317
						$mode = ($mode == 'must_exclude_one') ? 'must_not_contain' : $mode;
318
						$this->{$mode . '_ids'}[] = $id_words[0];
319
					}
320
				}
321
				// throw an error if we shall not ignore unexistant words
322
				else if (!$ignore_no_id && sizeof($non_common_words))
323
				{
324
					trigger_error(sprintf($this->user->lang['WORDS_IN_NO_POST'], implode($this->user->lang['COMMA_SEPARATOR'], $non_common_words)));
325
				}
326
				unset($non_common_words);
327
			}
328
			// else we only need one id
329
			else if (($wildcard = strpos($word, '*') !== false) || isset($words[$word]))
330
			{
331
				if ($wildcard)
332
				{
333
					$len = utf8_strlen(str_replace('*', '', $word));
334
					if ($len >= $this->word_length['min'] && $len <= $this->word_length['max'])
335
					{
336
						$this->{$mode . '_ids'}[] = '\'' . $this->db->sql_escape(str_replace('*', '%', $word)) . '\'';
337
					}
338
					else
339
					{
340
						$this->common_words[] = $word;
341
					}
342
				}
343
				else
344
				{
345
					$this->{$mode . '_ids'}[] = $words[$word];
346
				}
347
			}
348
			else
349
			{
350
				if (!isset($common_ids[$word]))
351
				{
352
					$len = utf8_strlen($word);
353
					if ($len < $this->word_length['min'] || $len > $this->word_length['max'])
354
					{
355
						$this->common_words[] = $word;
356
					}
357
				}
358
			}
359
		}
360
361
		// Return true if all words are not common words
362
		if (sizeof($exact_words) - sizeof($this->common_words) > 0)
363
		{
364
			return true;
365
		}
366
		return false;
367
	}
368
369
	/**
370
	* Performs a search on keywords depending on display specific params. You have to run split_keywords() first
371
	*
372
	* @param	string		$type				wchich type of table to be searched defaults to norma
373
	* @param	string		$fields				contains either titleonly (topic titles should be searched), msgonly (only message bodies should be searched), firstpost (only subject and body of the first post should be searched) or all (all post bodies and subjects should be searched)
374
	* @param	string		$terms				is either 'all' (use query as entered, words without prefix should default to "have to be in field") or 'any' (ignore search query parts and just return all posts that contain any of the specified words)
375
	* @param	array		$sort_by_sql		contains SQL code for the ORDER BY part of a query
376
	* @param	string		$sort_key			is the key of $sort_by_sql for the selected sorting
377
	* @param	string		$sort_dir			is either a or d representing ASC and DESC
378
	* @param	string		$sort_days			specifies the maximum amount of days a post may be old
379
	* @param	array		$author_ary			an array of author ids if the author should be ignored during the search the array is empty
380
	* @param	string		$author_name		specifies the author match, when ANONYMOUS is also a search-match
381
	* @param	array		&$id_ary			passed by reference, to be filled with ids for the page specified by $start and $per_page, should be ordered
382
	* @param	int			$start				indicates the first index of the page
383
	* @param	int			$per_page			number of ids each page is supposed to contain
384
	* @return	boolean|int						total number of results
385
	*/
386
	public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $post_visibility, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
387
	{
388
		// No keywords? No posts.
389
		if (empty($this->search_query))
390
		{
391
			return false;
392
		}
393
394
		// we can't search for negatives only
395
		if (empty($this->must_contain_ids))
396
		{
397
			return false;
398
		}
399
		$swl_table = PRIVMSGS_TABLE . '_swl';
0 ignored issues
show
Bug introduced by
The constant anavaro\pmsearch\search\PRIVMSGS_TABLE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
400
		$swm_table = PRIVMSGS_TABLE . '_swm';
401
		$message_table = PRIVMSGS_TABLE;
0 ignored issues
show
Unused Code introduced by
The assignment to $message_table is dead and can be removed.
Loading history...
402
		$message_to_table = PRIVMSGS_TO_TABLE;
0 ignored issues
show
Bug introduced by
The constant anavaro\pmsearch\search\PRIVMSGS_TO_TABLE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
403
404
		$must_contain_ids = $this->must_contain_ids;
405
		$must_not_contain_ids = $this->must_not_contain_ids;
406
		$must_exclude_one_ids = $this->must_exclude_one_ids;
407
408
		sort($must_contain_ids);
409
		sort($must_not_contain_ids);
410
		sort($must_exclude_one_ids);
411
412
		// generate a search_key from all the options to identify the results
413
		$search_key_array = array(
414
			serialize($must_contain_ids),
415
			serialize($must_not_contain_ids),
416
			serialize($must_exclude_one_ids),
417
			$type,
418
			$fields,
419
			$terms,
420
			$sort_days,
421
			$sort_key,
422
			$topic_id,
423
			implode(',', $ex_fid_ary),
424
			$post_visibility,
425
			implode(',', $author_ary),
426
			$author_name,
427
		);
428
429
		$search_key = md5(implode('#', $search_key_array));
430
431
		// try reading the results from cache
432
		$total_results = 0;
433
		if ($this->obtain_ids($search_key, $total_results, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE)
0 ignored issues
show
Bug introduced by
The constant anavaro\pmsearch\search\SEARCH_RESULT_IN_CACHE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
434
		{
435
			return $total_results;
436
		}
437
438
		$id_ary = array();
439
440
		$sql_where = array();
441
		$m_num = 0;
442
		$w_num = 0;
443
444
		$sql_array = array(
445
			'SELECT'	=> 'msg.msg_id',
446
			'FROM'		=> array(
447
				$swm_table	=> array(),
448
				$swl_table	=> array(),
449
			),
450
			'LEFT_JOIN' => array(array(
451
									 'FROM'	=> array(
452
										 $message_to_table	=> 'msg',
453
									 ),
454
									 'WHERE'	=> '',
455
									 'ON'	=> 'm0.post_id = msg.msg_id',
456
								 )),
457
		);
458
459
		$title_match = '';
460
		$left_join_topics = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $left_join_topics is dead and can be removed.
Loading history...
461
		$group_by = true;
462
		// Build some display specific sql strings
463
		switch ($fields)
464
		{
465
			case 'titleonly':
466
				$title_match = 'title_match = 1';
467
				$group_by = false;
468
			break;
469
470
			case 'msgonly':
471
				$title_match = 'title_match = 0';
472
				$group_by = false;
473
			break;
474
		}
475
476
		/**
477
		 * @todo Add a query optimizer (handle stuff like "+(4|3) +4")
478
		 */
479
480
		foreach ($this->must_contain_ids as $subquery)
481
		{
482
			if (is_array($subquery))
483
			{
484
				$group_by = true;
485
486
				$word_id_sql = array();
487
				$word_ids = array();
488
				foreach ($subquery as $id)
489
				{
490
					if (is_string($id))
491
					{
492
						$sql_array['LEFT_JOIN'][] = array(
493
							'FROM'	=> array($swl_table => 'w' . $w_num),
494
							'ON'	=> "w$w_num.word_text LIKE $id"
495
						);
496
						$word_ids[] = "w$w_num.word_id";
497
498
						$w_num++;
499
					}
500
					else
501
					{
502
						$word_ids[] = $id;
503
					}
504
				}
505
506
				$sql_where[] = $this->db->sql_in_set("m$m_num.word_id", $word_ids);
507
508
				unset($word_id_sql);
509
				unset($word_ids);
510
			}
511
			else if (is_string($subquery))
512
			{
513
				$sql_array['FROM'][$swl_table][] = 'w' . $w_num;
514
515
				$sql_where[] = "w$w_num.word_text LIKE $subquery";
516
				$sql_where[] = "m$m_num.word_id = w$w_num.word_id";
517
518
				$group_by = true;
519
				$w_num++;
520
			}
521
			else
522
			{
523
				$sql_where[] = "m$m_num.word_id = $subquery";
524
			}
525
526
			$sql_array['FROM'][$swm_table][] = 'm' . $m_num;
527
528
			if ($title_match)
529
			{
530
				$sql_where[] = "m$m_num.$title_match";
531
			}
532
533
			if ($m_num != 0)
534
			{
535
				$sql_where[] = "m$m_num.post_id = m0.post_id";
536
			}
537
			$m_num++;
538
		}
539
540
		foreach ($this->must_not_contain_ids as $key => $subquery)
541
		{
542
			if (is_string($subquery))
543
			{
544
				$sql_array['LEFT_JOIN'][] = array(
545
					'FROM'	=> array($swl_table => 'w' . $w_num),
546
					'ON'	=> "w$w_num.word_text LIKE $subquery"
547
				);
548
549
				$this->must_not_contain_ids[$key] = "w$w_num.word_id";
0 ignored issues
show
Bug Best Practice introduced by
The property must_not_contain_ids does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
550
551
				$group_by = true;
552
				$w_num++;
553
			}
554
		}
555
556
		if (sizeof($this->must_not_contain_ids))
557
		{
558
			$sql_array['LEFT_JOIN'][] = array(
559
				'FROM'	=> array($swm_table => 'm' . $m_num),
560
				'ON'	=> $this->db->sql_in_set("m$m_num.word_id", $this->must_not_contain_ids) . (($title_match) ? " AND m$m_num.$title_match" : '') . " AND m$m_num.post_id = m0.post_id"
561
			);
562
563
			$sql_where[] = "m$m_num.word_id IS NULL";
564
			$m_num++;
565
		}
566
567
		foreach ($this->must_exclude_one_ids as $ids)
568
		{
569
			$is_null_joins = array();
570
			foreach ($ids as $id)
571
			{
572
				if (is_string($id))
573
				{
574
					$sql_array['LEFT_JOIN'][] = array(
575
						'FROM'	=> array($swl_table => 'w' . $w_num),
576
						'ON'	=> "w$w_num.word_text LIKE $id"
577
					);
578
					$id = "w$w_num.word_id";
579
580
					$group_by = true;
581
					$w_num++;
582
				}
583
584
				$sql_array['LEFT_JOIN'][] = array(
585
					'FROM'	=> array($swm_table => 'm' . $m_num),
586
					'ON'	=> "m$m_num.word_id = $id AND m$m_num.post_id = m0.post_id" . (($title_match) ? " AND m$m_num.$title_match" : '')
587
				);
588
				$is_null_joins[] = "m$m_num.word_id IS NULL";
589
590
				$m_num++;
591
			}
592
			$sql_where[] = '(' . implode(' OR ', $is_null_joins) . ')';
593
		}
594
		//$sql_where[] = $post_visibility;
595
596
		$search_query = $this->search_query;
0 ignored issues
show
Unused Code introduced by
The assignment to $search_query is dead and can be removed.
Loading history...
597
		$must_exclude_one_ids = $this->must_exclude_one_ids;
0 ignored issues
show
Unused Code introduced by
The assignment to $must_exclude_one_ids is dead and can be removed.
Loading history...
598
		$must_not_contain_ids = $this->must_not_contain_ids;
0 ignored issues
show
Unused Code introduced by
The assignment to $must_not_contain_ids is dead and can be removed.
Loading history...
599
		$must_contain_ids = $this->must_contain_ids;
0 ignored issues
show
Unused Code introduced by
The assignment to $must_contain_ids is dead and can be removed.
Loading history...
600
601
		if (sizeof($author_ary))
602
		{
603
			$folders = array(-2, -1);
0 ignored issues
show
Unused Code introduced by
The assignment to $folders is dead and can be removed.
Loading history...
604
			//$sql_author = '((' . $this->db->sql_in_set('msg.author_id', $author_ary) . ' or ' . $this->db->sql_in_set('msg.user_id', $author_ary) . ') and (msg.user_id <> msg.author_id or (msg.user_id = msg.author_id and ' . $this->db->sql_in_set('msg.folder_id', $folders, true) . ')))';
605
			$sql_author = '(' . $this->db->sql_in_set('msg.author_id', $author_ary) . ' or ' . $this->db->sql_in_set('msg.user_id', $author_ary) . ')';
606
			$sql_where[] = $sql_author;
607
608
		}
609
610
		if ($sort_days)
611
		{
612
			$sql_where[] = 'msg.message_time >= ' . (time() - ($sort_days * 86400));
613
		}
614
615
		$sql_array['WHERE'] = implode(' AND ', $sql_where);
616
617
		$is_mysql = false;
618
		// if the total result count is not cached yet, retrieve it from the db
619
620
		if (!$total_results)
621
		{
622
			$sql = '';
623
			$sql_array_count = $sql_array;
624
			switch ($this->db->get_sql_layer())
625
			{
626
				case 'mysql4':
627
				//case 'mysqli':
628
629
					// 3.x does not support SQL_CALC_FOUND_ROWS
630
					// $sql_array['SELECT'] = 'SQL_CALC_FOUND_ROWS ' . $sql_array['SELECT'];
631
					$is_mysql = true;
632
633
				break;
634
635
				case 'sqlite3':
636
					$sql_array_count['SELECT'] = 'DISTINCT msg.msg_id';
637
					$sql = 'SELECT COUNT(msg_id) as total_results
638
							FROM (' . $this->db->sql_build_query('SELECT_DISTINCT', $sql_array_count) . ')';
639
640
				// no break
641
642
				default:
643
					$sql_array_count['SELECT'] = 'COUNT(DISTINCT msg.msg_id) AS total_results';
644
					$sql = (!$sql) ? $this->db->sql_build_query('SELECT', $sql_array_count) : $sql;
645
646
					$result = $this->db->sql_query($sql);
647
					$total_results = (int) $this->db->sql_fetchfield('total_results');
648
					$this->db->sql_freeresult($result);
649
650
					if (!$total_results)
651
					{
652
						return false;
653
					}
654
				break;
655
			}
656
			unset($sql_array_count, $sql);
657
		}
658
659
		// Build sql strings for sorting
660
		$sql_sort = $sort_by_sql[$sort_key] . (($sort_dir == 'a') ? ' ASC' : ' DESC');
661
662
		// if using mysql and the total result count is not calculated yet, get it from the db
663
		if (!$total_results && $is_mysql)
664
		{
665
			// Also count rows for the query as if there was not LIMIT. Add SQL_CALC_FOUND_ROWS to SQL
666
			$sql_array['SELECT'] = 'SQL_CALC_FOUND_ROWS ' . $sql_array['SELECT'];
667
		}
668
669
		$sql_array['WHERE'] = implode(' AND ', $sql_where);
670
		//$sql_array['GROUP_BY'] = ($group_by) ? (($type == 'posts') ? 'p.post_id' : 'p.topic_id') . ', ' . $sort_by_sql[$sort_key] : '';
671
		$sql_array['ORDER_BY'] = 'msg.msg_id DESC';
672
673
		unset($sql_where, $sql_sort, $group_by);
674
675
		$sql = $this->db->sql_build_query('SELECT_DISTINCT', $sql_array);
676
677
		$result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start);
678
679
		while ($row = $this->db->sql_fetchrow($result))
680
		{
681
			$id_ary[] = (int) $row['msg_id'];
682
		}
683
684
		$this->db->sql_freeresult($result);
685
686
		/**
687
		 * There seems to be issue with SELECT FOUND_ROWS() so we will need to think of something else.
688
		 */
689
		if (!$total_results && $is_mysql)
690
		{
691
			// Get the number of results as calculated by MySQL
692
			$sql_count = 'SELECT FOUND_ROWS() as total_results';
693
			$result = $this->db->sql_query($sql_count);
694
			$total_results = (int) $this->db->sql_fetchfield('total_results');
695
			$this->db->sql_freeresult($result);
696
			if (!$total_results)
697
			{
698
				return false;
699
			}
700
		}
701
702
		if ($start >= $total_results)
703
		{
704
			$start = floor(($total_results - 1) / $per_page) * $per_page;
705
706
			$result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start);
707
708
			while ($row = $this->db->sql_fetchrow($result))
709
			{
710
				$id_ary[] = (int) $row['msg_id'];
711
			}
712
			$this->db->sql_freeresult($result);
713
714
		}
715
716
		// store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page
717
		$this->save_ids($search_key, $this->search_query, $author_ary, $total_results, $id_ary, $start, $sort_dir);
718
		$id_ary = array_slice($id_ary, 0, (int) $per_page);
719
720
		return $total_results;
721
	}
722
723
	/**
724
	* Updates wordlist and wordmatch tables when a message is posted or changed
725
	*
726
	* @param	string	$mode		Contains the post mode: edit, post, reply, quote
727
	* @param	int		$post_id	The id of the post which is modified/created
728
	* @param	string	&$message	New or updated post content
729
	* @param	string	&$subject	New or updated post subject
730
	* @param	int		$poster_id	Post author's user id
731
	* @param	int		$forum_id	The id of the forum in which the post is located
732
	*/
733
	public function index($mode, $post_id, &$message, &$subject, $poster_id, $forum_id = '')
734
	{
735
		$wordlist = PRIVMSGS_TABLE . '_swl';
736
		$wordmatch = PRIVMSGS_TABLE . '_swm';
737
738
		if (!$this->config['fulltext_native_load_upd'])
739
		{
740
			/**
741
			 * The search indexer is disabled, return
742
			 */
743
			return;
744
		}
745
746
		// Split old and new post/subject to obtain array of 'words'
747
		$split_text = $this->split_message($message);
748
		$split_title = $this->split_message($subject);
749
750
		$cur_words = array('post' => array(), 'title' => array());
751
752
		$words = array();
753
		if ($mode == 'edit')
754
		{
755
			$words['add']['post'] = array();
756
			$words['add']['title'] = array();
757
			$words['del']['post'] = array();
758
			$words['del']['title'] = array();
759
760
			$sql = 'SELECT w.word_id, w.word_text, m.title_match
761
				FROM ' . $wordlist . ' w, ' . $wordmatch . " m
762
				WHERE m.post_id = $post_id
763
					AND w.word_id = m.word_id";
764
			$result = $this->db->sql_query($sql);
765
766
			while ($row = $this->db->sql_fetchrow($result))
767
			{
768
				$which = ($row['title_match']) ? 'title' : 'post';
769
				$cur_words[$which][$row['word_text']] = $row['word_id'];
770
			}
771
			$this->db->sql_freeresult($result);
772
773
			$words['add']['post'] = array_diff($split_text, array_keys($cur_words['post']));
774
			$words['add']['title'] = array_diff($split_title, array_keys($cur_words['title']));
775
			$words['del']['post'] = array_diff(array_keys($cur_words['post']), $split_text);
776
			$words['del']['title'] = array_diff(array_keys($cur_words['title']), $split_title);
777
		}
778
		else
779
		{
780
			$words['add']['post'] = $split_text;
781
			$words['add']['title'] = $split_title;
782
			$words['del']['post'] = array();
783
			$words['del']['title'] = array();
784
		}
785
		unset($split_text);
786
		unset($split_title);
787
788
		// Get unique words from the above arrays
789
		$unique_add_words = array_unique(array_merge($words['add']['post'], $words['add']['title']));
790
791
		// We now have unique arrays of all words to be added and removed and
792
		// individual arrays of added and removed words for text and title. What
793
		// we need to do now is add the new words (if they don't already exist)
794
		// and then add (or remove) matches between the words and this post
795
		if (sizeof($unique_add_words))
796
		{
797
			$sql = 'SELECT word_id, word_text
798
				FROM ' . $wordlist . '
799
				WHERE ' . $this->db->sql_in_set('word_text', $unique_add_words);
800
			$result = $this->db->sql_query($sql);
801
802
			$word_ids = array();
803
			while ($row = $this->db->sql_fetchrow($result))
804
			{
805
				$word_ids[$row['word_text']] = $row['word_id'];
806
			}
807
			$this->db->sql_freeresult($result);
808
			$new_words = array_diff($unique_add_words, array_keys($word_ids));
809
810
			$this->db->sql_transaction('begin');
811
			if (sizeof($new_words))
812
			{
813
				$sql_ary = array();
814
815
				foreach ($new_words as $word)
816
				{
817
					$sql_ary[] = array('word_text' => (string) $word, 'word_count' => 0);
818
				}
819
				$this->db->sql_return_on_error(true);
820
				$this->db->sql_multi_insert($wordlist, $sql_ary);
821
				$this->db->sql_return_on_error(false);
822
			}
823
			unset($new_words, $sql_ary);
824
		}
825
		else
826
		{
827
			$this->db->sql_transaction('begin');
828
		}
829
830
		// now update the search match table, remove links to removed words and add links to new words
831
		foreach ($words['del'] as $word_in => $word_ary)
832
		{
833
			$title_match = ($word_in == 'title') ? 1 : 0;
834
835
			if (sizeof($word_ary))
836
			{
837
				$sql_in = array();
838
				foreach ($word_ary as $word)
839
				{
840
					$sql_in[] = $cur_words[$word_in][$word];
841
				}
842
843
				$sql = 'DELETE FROM ' . $wordmatch . '
844
					WHERE ' . $this->db->sql_in_set('word_id', $sql_in) . '
845
						AND post_id = ' . intval($post_id) . "
846
						AND title_match = $title_match";
847
				$this->db->sql_query($sql);
848
849
				$sql = 'UPDATE ' . $wordlist . '
850
					SET word_count = word_count - 1
851
					WHERE ' . $this->db->sql_in_set('word_id', $sql_in) . '
852
						AND word_count > 0';
853
				$this->db->sql_query($sql);
854
855
				unset($sql_in);
856
			}
857
		}
858
859
		$this->db->sql_return_on_error(true);
860
		foreach ($words['add'] as $word_in => $word_ary)
861
		{
862
			$title_match = ($word_in == 'title') ? 1 : 0;
863
864
			if (sizeof($word_ary))
865
			{
866
				$sql = 'INSERT INTO ' . $wordmatch . ' (post_id, word_id, title_match)
867
					SELECT ' . (int) $post_id . ', word_id, ' . (int) $title_match . '
868
					FROM ' . $wordlist . '
869
					WHERE ' . $this->db->sql_in_set('word_text', $word_ary);
870
				$this->db->sql_query($sql);
871
872
				$sql = 'UPDATE ' . $wordlist . '
873
					SET word_count = word_count + 1
874
					WHERE ' . $this->db->sql_in_set('word_text', $word_ary);
875
				$this->db->sql_query($sql);
876
			}
877
		}
878
		$this->db->sql_return_on_error(false);
879
880
		$this->db->sql_transaction('commit');
881
882
		// destroy cached search results containing any of the words removed or added
883
		$this->destroy_cache(array_unique(array_merge($words['add']['post'], $words['add']['title'], $words['del']['post'], $words['del']['title'])), array($poster_id));
884
885
		unset($unique_add_words);
886
		unset($words);
887
		unset($cur_words);
888
	}
889
	/**
890
	 * Removes entries from the wordmatch table for the specified post_ids
891
	 */
892
	public function index_remove($post_ids, $author_ids = null, $forum_ids = null)
893
	{
894
		if (sizeof($post_ids))
895
		{
896
			$sql = 'SELECT w.word_id, w.word_text, m.title_match
897
				FROM ' . PRIVMSGS_TABLE . '_swm' . ' m, ' . PRIVMSGS_TABLE . '_swl' . ' w
0 ignored issues
show
Bug introduced by
The constant anavaro\pmsearch\search\PRIVMSGS_TABLE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
898
				WHERE ' . $this->db->sql_in_set('m.post_id', $post_ids) . '
899
					AND w.word_id = m.word_id';
900
			$result = $this->db->sql_query($sql);
901
902
			$message_word_ids = $title_word_ids = $word_texts = array();
903
			while ($row = $this->db->sql_fetchrow($result))
904
			{
905
				if ($row['title_match'])
906
				{
907
					$title_word_ids[] = $row['word_id'];
908
				}
909
				else
910
				{
911
					$message_word_ids[] = $row['word_id'];
912
				}
913
				$word_texts[] = $row['word_text'];
914
			}
915
			$this->db->sql_freeresult($result);
916
917
			if (sizeof($title_word_ids))
918
			{
919
				$sql = 'UPDATE ' .  PRIVMSGS_TABLE . '_swl' . '
920
					SET word_count = word_count - 1
921
					WHERE ' . $this->db->sql_in_set('word_id', $title_word_ids) . '
922
						AND word_count > 0';
923
				$this->db->sql_query($sql);
924
			}
925
926
			if (sizeof($message_word_ids))
927
			{
928
				$sql = 'UPDATE ' . PRIVMSGS_TABLE . '_swl' . '
929
					SET word_count = word_count - 1
930
					WHERE ' . $this->db->sql_in_set('word_id', $message_word_ids) . '
931
						AND word_count > 0';
932
				$this->db->sql_query($sql);
933
			}
934
935
			unset($title_word_ids);
936
			unset($message_word_ids);
937
938
			$sql = 'DELETE FROM ' . PRIVMSGS_TABLE . '_swm' . '
939
				WHERE ' . $this->db->sql_in_set('post_id', $post_ids);
940
			$this->db->sql_query($sql);
941
		}
942
943
		$this->destroy_cache(array_unique($word_texts));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $word_texts does not seem to be defined for all execution paths leading up to this point.
Loading history...
944
	}
945
946
	/**
947
	* Tidy up indexes: Tag 'common words' and remove
948
	* words no longer referenced in the match table
949
	*/
950
	public function tidy()
951
	{
952
		$swl_table = PRIVMSGS_TABLE . '_swl';
0 ignored issues
show
Unused Code introduced by
The assignment to $swl_table is dead and can be removed.
Loading history...
Bug introduced by
The constant anavaro\pmsearch\search\PRIVMSGS_TABLE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
953
		$swm_table = PRIVMSGS_TABLE . '_swm';
0 ignored issues
show
Unused Code introduced by
The assignment to $swm_table is dead and can be removed.
Loading history...
954
		// Is the fulltext indexer disabled? If yes then we need not
955
		// carry on ... it's okay ... I know when I'm not wanted boo hoo
956
		if (!$this->config['fulltext_native_load_upd'])
957
		{
958
			$this->config->set('search_last_gc', time(), false);
959
			return;
960
		}
961
962
		$destroy_cache_words = array();
963
964
		// Remove common words
965
		/*
966
		if ($this->config['num_posts'] >= 100 && $this->config['fulltext_native_common_thres'])
967
		{
968
			$common_threshold = ((double) $this->config['fulltext_native_common_thres']) / 100.0;
969
			// First, get the IDs of common words
970
			$sql = 'SELECT word_id, word_text
971
				FROM ' . SEARCH_WORDLIST_TABLE . '
972
				WHERE word_count > ' . floor($this->config['num_posts'] * $common_threshold) . '
973
					OR word_common = 1';
974
			$result = $this->db->sql_query($sql);
975
976
			$sql_in = array();
977
			while ($row = $this->db->sql_fetchrow($result))
978
			{
979
				$sql_in[] = $row['word_id'];
980
				$destroy_cache_words[] = $row['word_text'];
981
			}
982
			$this->db->sql_freeresult($result);
983
984
			if (sizeof($sql_in))
985
			{
986
				// Flag the words
987
				$sql = 'UPDATE ' . SEARCH_WORDLIST_TABLE . '
988
					SET word_common = 1
989
					WHERE ' . $this->db->sql_in_set('word_id', $sql_in);
990
				$this->db->sql_query($sql);
991
992
				// by setting search_last_gc to the new time here we make sure that if a user reloads because the
993
				// following query takes too long, he won't run into it again
994
				$this->config->set('search_last_gc', time(), false);
995
996
				// Delete the matches
997
				$sql = 'DELETE FROM ' . SEARCH_WORDMATCH_TABLE . '
998
					WHERE ' . $this->db->sql_in_set('word_id', $sql_in);
999
				$this->db->sql_query($sql);
1000
			}
1001
			unset($sql_in);
1002
		}
1003
		*/
1004
		if (sizeof($destroy_cache_words))
1005
		{
1006
			// destroy cached search results containing any of the words that are now common or were removed
1007
			$this->destroy_cache(array_unique($destroy_cache_words));
1008
		}
1009
1010
		$this->config->set('search_last_gc', time(), false);
1011
	}
1012
1013
	/**
1014
	* Deletes all words from the index
1015
	*/
1016
	public function delete_index($acp_module, $u_action, $type = 'normal')
1017
	{
1018
		$swl_table = PRIVMSGS_TABLE . '_swl';
0 ignored issues
show
Bug introduced by
The constant anavaro\pmsearch\search\PRIVMSGS_TABLE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1019
		$swm_table = PRIVMSGS_TABLE . '_swm';
1020
1021
		switch ($this->db->get_sql_layer())
1022
		{
1023
			case 'sqlite':
1024
			case 'sqlite3':
1025
				$this->db->sql_query('DELETE FROM ' . $swl_table);
1026
				$this->db->sql_query('DELETE FROM ' . $swm_table);
1027
				//$this->db->sql_query('DELETE FROM ' . SEARCH_RESULTS_TABLE . '');
1028
			break;
1029
1030
			default:
1031
				$this->db->sql_query('TRUNCATE TABLE ' . $swl_table);
1032
				$this->db->sql_query('TRUNCATE TABLE ' . $swm_table);
1033
				$this->db->sql_query('TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE);
0 ignored issues
show
Bug introduced by
The constant anavaro\pmsearch\search\SEARCH_RESULTS_TABLE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1034
			break;
1035
		}
1036
	}
1037
1038
	/**
1039
	* Returns true if both FULLTEXT indexes exist
1040
	*/
1041
	public function index_created($type = 'normal')
1042
	{
1043
		if (!sizeof($this->stats))
1044
		{
1045
			$this->get_stats();
1046
		}
1047
		switch ($type)
1048
		{
1049
			case 'normal':
1050
				return ($this->stats['total_words'] && $this->stats['total_matches']) ? true : false;
1051
			break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1052
		}
1053
	}
1054
1055
	/**
1056
	* Returns an associative array containing information about the indexes
1057
	*/
1058
	public function index_stats($type = 'normal')
1059
	{
1060
		if (!sizeof($this->stats))
1061
		{
1062
			$this->get_stats();
1063
		}
1064
		switch ($type)
1065
		{
1066
			case 'normal':
1067
				return array(
1068
					$this->user->lang['TOTAL_WORDS']		=> $this->stats['total_words'],
1069
					$this->user->lang['TOTAL_MATCHES']	=> $this->stats['total_matches']);
1070
			break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1071
		}
1072
	}
1073
1074
	protected function get_stats()
1075
	{
1076
		$this->stats['total_words']		= $this->db->get_estimated_row_count(PRIVMSGS_TABLE . '_swl');
0 ignored issues
show
Bug introduced by
The constant anavaro\pmsearch\search\PRIVMSGS_TABLE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug Best Practice introduced by
The property stats does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1077
		$this->stats['total_matches']	= $this->db->get_estimated_row_count(PRIVMSGS_TABLE . '_swm');
1078
	}
1079
1080
	/**
1081
	 * Get corespondence with user ...
1082
	 *
1083
	 * This is a part of the july sprint for F-bg.org
1084
	 *
1085
	 * @param $target_id	Should be the target user's ID
0 ignored issues
show
Bug introduced by
The type anavaro\pmsearch\search\Should was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1086
	 * @param $start		If we have pagination - start page
0 ignored issues
show
Bug introduced by
The type anavaro\pmsearch\search\If was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1087
	 * @param $per_page
1088
	 * @return bool
1089
	 */
1090 1
	public function user_search($target_id, &$id_ary, &$start, $per_page)
1091
	{
1092 1
		$messages_table = PRIVMSGS_TABLE;
0 ignored issues
show
Bug introduced by
The constant anavaro\pmsearch\search\PRIVMSGS_TABLE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1093 1
		$messages_to_table = PRIVMSGS_TO_TABLE;
0 ignored issues
show
Bug introduced by
The constant anavaro\pmsearch\search\PRIVMSGS_TO_TABLE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1094
1095
		//Sanity check -> are we getting user id or are we being scamed.
1096
1097 1
		if (!is_numeric($target_id))
1098
		{
1099 1
			return false;
1100
		}
1101
		// Let's get messages we have between users (and don't filter them, becouse we don't know who deleted what.
1102
		$sql_array = array(
1103 1
			'SELECT'	=> 'msg.msg_id',
1104
			'FROM'		=> array(
1105 1
				$messages_table => 'msg',
1106 1
				$messages_to_table => 'mt',
1107
			),
1108
1109 1
			'WHERE'	=> 'msg.msg_id = mt.msg_id AND (((msg.author_id = ' . $this->user->data['user_id'] . ' AND msg.to_address LIKE \'u_' . $target_id .'\') OR (msg.author_id = ' . $target_id . ' AND msg.to_address LIKE \'u_' . $this->user->data['user_id'] .'\')) AND mt.user_id = ' . $this->user->data['user_id'] . ')',
1110
1111
		);
1112
1113
		// Let's get counts and such
1114 1
		$total_results = 0;
1115 1
		$is_mysql = false;
1116
		// if the total result count is not cached yet, retrieve it from the db
1117 1
		if (!$total_results)
1118
		{
1119 1
			$sql = '';
1120 1
			$sql_array_count = $sql_array;
1121 1
			switch ($this->db->get_sql_layer())
1122
			{
1123 1
				case 'mysql4':
1124 1
				case 'mysqli':
1125
1126
					// 3.x does not support SQL_CALC_FOUND_ROWS
1127
					// $sql_array['SELECT'] = 'SQL_CALC_FOUND_ROWS ' . $sql_array['SELECT'];
1128 1
					$is_mysql = true;
1129
1130 1
				break;
1131
1132
				case 'sqlite3':
1133
					$sql_array_count['SELECT'] = 'msg.msg_id';
1134
					$sql = 'SELECT COUNT(msg_id) as total_results
1135
							FROM (' . $this->db->sql_build_query('SELECT_DISTINCT', $sql_array_count) . ')';
1136
1137
				// no break
1138
1139
				default:
1140
					$sql_array_count['SELECT'] = 'COUNT(DISTINCT msg.msg_id) AS total_results';
1141
					$sql = (!$sql) ? $this->db->sql_build_query('SELECT', $sql_array_count) : $sql;
1142
1143
					$result = $this->db->sql_query($sql);
1144
					$total_results = (int) $this->db->sql_fetchfield('total_results');
1145
					$this->db->sql_freeresult($result);
1146
1147
					if (!$total_results)
1148
					{
1149
						return false;
1150
					}
1151
				break;
1152
			}
1153
1154 1
			unset($sql_array_count, $sql);
1155
		}
1156
		// if using mysql and the total result count is not calculated yet, get it from the db
1157 1
		if (!$total_results && $is_mysql)
1158
		{
1159
			// Also count rows for the query as if there was not LIMIT. Add SQL_CALC_FOUND_ROWS to SQL
1160 1
			$sql_array['SELECT'] = 'SQL_CALC_FOUND_ROWS ' . $sql_array['SELECT'];
1161
		}
1162
1163 1
		$sql_array['ORDER_BY'] = 'msg.msg_id DESC';
1164
1165 1
		$sql = $this->db->sql_build_query('SELECT_DISTINCT', $sql_array);
1166
1167 1
		$result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start);
1168
1169 1
		while ($row = $this->db->sql_fetchrow($result))
1170
		{
1171 1
			$id_ary[] = (int) $row['msg_id'];
1172
		}
1173 1
		$this->db->sql_freeresult($result);
1174
1175 1
		if (!$total_results && $is_mysql)
1176
		{
1177
			// Get the number of results as calculated by MySQL
1178 1
			$sql_count = 'SELECT FOUND_ROWS() as total_results';
1179 1
			$result = $this->db->sql_query($sql_count);
1180 1
			$total_results = (int) $this->db->sql_fetchfield('total_results');
1181 1
			$this->db->sql_freeresult($result);
1182
1183 1
			if (!$total_results)
1184
			{
1185
				return false;
1186
			}
1187
		}
1188
1189 1
		if ($start >= $total_results)
1190
		{
1191
			$start = floor(($total_results - 1) / $per_page) * $per_page;
1192
1193
			$result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start);
1194
1195
			while ($row = $this->db->sql_fetchrow($result))
1196
			{
1197
				$id_ary[] = (int) $row['msg_id'];
1198
			}
1199
			$this->db->sql_freeresult($result);
1200
1201
		}
1202
1203
		// store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page
1204
		$search_key = array(
1205 1
			$this->user->data['user_id'],
1206 1
			$target_id
1207
		);
1208 1
		$search_key = implode(';', $search_key);
1209 1
		$author_ary = array();
1210 1
		$sort_dir = 'a';
1211 1
		$this->save_ids(md5($search_key), $this->search_query, $author_ary, $total_results, $id_ary, $start, $sort_dir);
1212 1
		$id_ary = array_slice($id_ary, 0, (int) $per_page);
1213
1214 1
		return $total_results;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $total_results returns the type integer which is incompatible with the documented return type boolean.
Loading history...
1215
1216
	}
1217
}
1218