~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to www/php/phpBB3/includes/search/fulltext_mysql.php

Merge setup-stuff.

phpBB is gone, configuration, setup and jail building are completely redone.

Please read doc/setup/install_proc.txt, or you'll not get far.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
<?php
2
 
/**
3
 
*
4
 
* @package search
5
 
* @version $Id: fulltext_mysql.php,v 1.51 2007/11/29 18:26:20 davidmj Exp $
6
 
* @copyright (c) 2005 phpBB Group
7
 
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
8
 
*
9
 
*/
10
 
 
11
 
/**
12
 
* @ignore
13
 
*/
14
 
if (!defined('IN_PHPBB'))
15
 
{
16
 
        exit;
17
 
}
18
 
 
19
 
/**
20
 
* @ignore
21
 
*/
22
 
include_once($phpbb_root_path . 'includes/search/search.' . $phpEx);
23
 
 
24
 
/**
25
 
* fulltext_mysql
26
 
* Fulltext search for MySQL
27
 
* @package search
28
 
*/
29
 
class fulltext_mysql extends search_backend
30
 
{
31
 
        var $stats = array();
32
 
        var $word_length = array();
33
 
        var $split_words = array();
34
 
        var $search_query;
35
 
        var $common_words = array();
36
 
        var $pcre_properties = false;
37
 
        var $mbstring_regex = false;
38
 
 
39
 
        function fulltext_mysql(&$error)
40
 
        {
41
 
                global $config;
42
 
 
43
 
                $this->word_length = array('min' => $config['fulltext_mysql_min_word_len'], 'max' => $config['fulltext_mysql_max_word_len']);
44
 
 
45
 
                if (version_compare(PHP_VERSION, '5.1.0', '>=') || (version_compare(PHP_VERSION, '5.0.0-dev', '<=') && version_compare(PHP_VERSION, '4.4.0', '>=')))
46
 
                {
47
 
                        // While this is the proper range of PHP versions, PHP may not be linked with the bundled PCRE lib and instead with an older version
48
 
                        if (@preg_match('/\p{L}/u', 'a') !== false)
49
 
                        {
50
 
                                $this->pcre_properties = true;
51
 
                        }
52
 
                }
53
 
 
54
 
                if (function_exists('mb_ereg'))
55
 
                {
56
 
                        $this->mbstring_regex = true;
57
 
                        mb_regex_encoding('UTF-8');
58
 
                }
59
 
 
60
 
                $error = false;
61
 
        }
62
 
 
63
 
        /**
64
 
        * Checks for correct MySQL version and stores min/max word length in the config
65
 
        */
66
 
        function init()
67
 
        {
68
 
                global $db, $user;
69
 
 
70
 
                if ($db->sql_layer != 'mysql4' && $db->sql_layer != 'mysqli')
71
 
                {
72
 
                        return $user->lang['FULLTEXT_MYSQL_INCOMPATIBLE_VERSION'];
73
 
                }
74
 
 
75
 
                $result = $db->sql_query('SHOW TABLE STATUS LIKE \'' . POSTS_TABLE . '\'');
76
 
                $info = $db->sql_fetchrow($result);
77
 
                $db->sql_freeresult($result);
78
 
 
79
 
                $engine = '';
80
 
                if (isset($info['Engine']))
81
 
                {
82
 
                        $engine = $info['Engine'];
83
 
                }
84
 
                else if (isset($info['Type']))
85
 
                {
86
 
                        $engine = $info['Type'];
87
 
                }
88
 
 
89
 
                if ($engine != 'MyISAM')
90
 
                {
91
 
                        return $user->lang['FULLTEXT_MYSQL_NOT_MYISAM'];
92
 
                }
93
 
 
94
 
                $sql = 'SHOW VARIABLES
95
 
                        LIKE \'ft\_%\'';
96
 
                $result = $db->sql_query($sql);
97
 
 
98
 
                $mysql_info = array();
99
 
                while ($row = $db->sql_fetchrow($result))
100
 
                {
101
 
                        $mysql_info[$row['Variable_name']] = $row['Value'];
102
 
                }
103
 
                $db->sql_freeresult($result);
104
 
 
105
 
                set_config('fulltext_mysql_max_word_len', $mysql_info['ft_max_word_len']);
106
 
                set_config('fulltext_mysql_min_word_len', $mysql_info['ft_min_word_len']);
107
 
 
108
 
                return false;
109
 
        }
110
 
 
111
 
        /**
112
 
        * Splits keywords entered by a user into an array of words stored in $this->split_words
113
 
        * Stores the tidied search query in $this->search_query
114
 
        *
115
 
        * @param string &$keywords Contains the keyword as entered by the user
116
 
        * @param string $terms is either 'all' or 'any'
117
 
        * @return bool false if no valid keywords were found and otherwise true
118
 
        */
119
 
        function split_keywords(&$keywords, $terms)
120
 
        {
121
 
                global $config;
122
 
 
123
 
                if ($terms == 'all')
124
 
                {
125
 
                        $match          = array('#\sand\s#iu', '#\sor\s#iu', '#\snot\s#iu', '#\+#', '#-#', '#\|#');
126
 
                        $replace        = array(' +', ' |', ' -', ' +', ' -', ' |');
127
 
 
128
 
                        $keywords = preg_replace($match, $replace, $keywords);
129
 
                }
130
 
 
131
 
                // Filter out as above
132
 
                $split_keywords = preg_replace("#[\n\r\t]+#", ' ', trim(htmlspecialchars_decode($keywords)));
133
 
 
134
 
                // Split words
135
 
                if ($this->pcre_properties)
136
 
                {
137
 
                        $split_keywords = preg_replace('#([^\p{L}\p{N}\'*"()])#u', '$1$1', str_replace('\'\'', '\' \'', trim($split_keywords)));
138
 
                }
139
 
                else if ($this->mbstring_regex)
140
 
                {
141
 
                        $split_keywords = mb_ereg_replace('([^\w\'*"()])', '\\1\\1', str_replace('\'\'', '\' \'', trim($split_keywords)));
142
 
                }
143
 
                else
144
 
                {
145
 
                        $split_keywords = preg_replace('#([^\w\'*"()])#u', '$1$1', str_replace('\'\'', '\' \'', trim($split_keywords)));
146
 
                }
147
 
 
148
 
                if ($this->pcre_properties)
149
 
                {
150
 
                        $matches = array();
151
 
                        preg_match_all('#(?:[^\p{L}\p{N}*"()]|^)([+\-|]?(?:[\p{L}\p{N}*"()]+\'?)*[\p{L}\p{N}*"()])(?:[^\p{L}\p{N}*"()]|$)#u', $split_keywords, $matches);
152
 
                        $this->split_words = $matches[1];
153
 
                }
154
 
                else if ($this->mbstring_regex)
155
 
                {
156
 
                        mb_ereg_search_init($split_keywords, '(?:[^\w*"()]|^)([+\-|]?(?:[\w*"()]+\'?)*[\w*"()])(?:[^\w*"()]|$)');
157
 
 
158
 
                        while (($word = mb_ereg_search_regs()))
159
 
                        {
160
 
                                $this->split_words[] = $word[1];
161
 
                        }
162
 
                }
163
 
                else
164
 
                {
165
 
                        $matches = array();
166
 
                        preg_match_all('#(?:[^\w*"()]|^)([+\-|]?(?:[\w*"()]+\'?)*[\w*"()])(?:[^\w*"()]|$)#u', $split_keywords, $matches);
167
 
                        $this->split_words = $matches[1];
168
 
                }
169
 
 
170
 
                // to allow phrase search, we need to concatenate quoted words
171
 
                $tmp_split_words = array();
172
 
                $phrase = '';
173
 
                foreach ($this->split_words as $word)
174
 
                {
175
 
                        if ($phrase)
176
 
                        {
177
 
                                $phrase .= ' ' . $word;
178
 
                                if (strpos($word, '"') !== false && substr_count($word, '"') % 2 == 1)
179
 
                                {
180
 
                                        $tmp_split_words[] = $phrase;
181
 
                                        $phrase = '';
182
 
                                }
183
 
                        }
184
 
                        else if (strpos($word, '"') !== false && substr_count($word, '"') % 2 == 1)
185
 
                        {
186
 
                                $phrase = $word;
187
 
                        }
188
 
                        else
189
 
                        {
190
 
                                $tmp_split_words[] = $word . ' ';
191
 
                        }
192
 
                }
193
 
                if ($phrase)
194
 
                {
195
 
                        $tmp_split_words[] = $phrase;
196
 
                }
197
 
 
198
 
                $this->split_words = $tmp_split_words;
199
 
 
200
 
                unset($tmp_split_words);
201
 
                unset($phrase);
202
 
 
203
 
                foreach ($this->split_words as $i => $word)
204
 
                {
205
 
                        $clean_word = preg_replace('#^[+\-|"]#', '', $word);
206
 
 
207
 
                        // check word length
208
 
                        $clean_len = utf8_strlen(str_replace('*', '', $clean_word));
209
 
                        if (($clean_len < $config['fulltext_mysql_min_word_len']) || ($clean_len > $config['fulltext_mysql_max_word_len']))
210
 
                        {
211
 
                                $this->common_words[] = $word;
212
 
                                unset($this->split_words[$i]);
213
 
                        }
214
 
                }
215
 
 
216
 
                if ($terms == 'any')
217
 
                {
218
 
                        $this->search_query = '';
219
 
                        foreach ($this->split_words as $word)
220
 
                        {
221
 
                                if ((strpos($word, '+') === 0) || (strpos($word, '-') === 0) || (strpos($word, '|') === 0))
222
 
                                {
223
 
                                        $word = substr($word, 1);
224
 
                                }
225
 
                                $this->search_query .= $word . ' ';
226
 
                        }
227
 
                }
228
 
                else
229
 
                {
230
 
                        $this->search_query = '';
231
 
                        foreach ($this->split_words as $word)
232
 
                        {
233
 
                                if ((strpos($word, '+') === 0) || (strpos($word, '-') === 0))
234
 
                                {
235
 
                                        $this->search_query .= $word . ' ';
236
 
                                }
237
 
                                else if (strpos($word, '|') === 0)
238
 
                                {
239
 
                                        $this->search_query .= substr($word, 1) . ' ';
240
 
                                }
241
 
                                else
242
 
                                {
243
 
                                        $this->search_query .= '+' . $word . ' ';
244
 
                                }
245
 
                        }
246
 
                }
247
 
 
248
 
                $this->search_query = utf8_htmlspecialchars($this->search_query);
249
 
 
250
 
                if ($this->search_query)
251
 
                {
252
 
                        $this->split_words = array_values($this->split_words);
253
 
                        sort($this->split_words);
254
 
                        return true;
255
 
                }
256
 
                return false;
257
 
        }
258
 
 
259
 
        /**
260
 
        * Turns text into an array of words
261
 
        */
262
 
        function split_message($text)
263
 
        {
264
 
                global $config;
265
 
 
266
 
                // Split words
267
 
                if ($this->pcre_properties)
268
 
                {
269
 
                        $text = preg_replace('#([^\p{L}\p{N}\'*])#u', '$1$1', str_replace('\'\'', '\' \'', trim($text)));
270
 
                }
271
 
                else if ($this->mbstring_regex)
272
 
                {
273
 
                        $text = mb_ereg_replace('([^\w\'*])', '\\1\\1', str_replace('\'\'', '\' \'', trim($text)));
274
 
                }
275
 
                else
276
 
                {
277
 
                        $text = preg_replace('#([^\w\'*])#u', '$1$1', str_replace('\'\'', '\' \'', trim($text)));
278
 
                }
279
 
 
280
 
                if ($this->pcre_properties)
281
 
                {
282
 
                        $matches = array();
283
 
                        preg_match_all('#(?:[^\p{L}\p{N}*]|^)([+\-|]?(?:[\p{L}\p{N}*]+\'?)*[\p{L}\p{N}*])(?:[^\p{L}\p{N}*]|$)#u', $text, $matches);
284
 
                        $text = $matches[1];
285
 
                }
286
 
                else if ($this->mbstring_regex)
287
 
                {
288
 
                        mb_ereg_search_init($text, '(?:[^\w*]|^)([+\-|]?(?:[\w*]+\'?)*[\w*])(?:[^\w*]|$)');
289
 
 
290
 
                        $text = array();
291
 
                        while (($word = mb_ereg_search_regs()))
292
 
                        {
293
 
                                $text[] = $word[1];
294
 
                        }
295
 
                }
296
 
                else
297
 
                {
298
 
                        $matches = array();
299
 
                        preg_match_all('#(?:[^\w*]|^)([+\-|]?(?:[\w*]+\'?)*[\w*])(?:[^\w*]|$)#u', $text, $matches);
300
 
                        $text = $matches[1];
301
 
                }
302
 
 
303
 
                // remove too short or too long words
304
 
                $text = array_values($text);
305
 
                for ($i = 0, $n = sizeof($text); $i < $n; $i++)
306
 
                {
307
 
                        $text[$i] = trim($text[$i]);
308
 
                        if (utf8_strlen($text[$i]) < $config['fulltext_mysql_min_word_len'] || utf8_strlen($text[$i]) > $config['fulltext_mysql_max_word_len'])
309
 
                        {
310
 
                                unset($text[$i]);
311
 
                        }
312
 
                }
313
 
 
314
 
                return array_values($text);
315
 
        }
316
 
 
317
 
        /**
318
 
        * Performs a search on keywords depending on display specific params. You have to run split_keywords() first.
319
 
        *
320
 
        * @param        string          $type                           contains either posts or topics depending on what should be searched for
321
 
        * @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)
322
 
        * @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)
323
 
        * @param        array           &$sort_by_sql           contains SQL code for the ORDER BY part of a query
324
 
        * @param        string          &$sort_key                      is the key of $sort_by_sql for the selected sorting
325
 
        * @param        string          &$sort_dir                      is either a or d representing ASC and DESC
326
 
        * @param        string          &$sort_days                     specifies the maximum amount of days a post may be old
327
 
        * @param        array           &$ex_fid_ary            specifies an array of forum ids which should not be searched
328
 
        * @param        array           &$m_approve_fid_ary     specifies an array of forum ids in which the searcher is allowed to view unapproved posts
329
 
        * @param        int                     &$topic_id                      is set to 0 or a topic id, if it is not 0 then only posts in this topic should be searched
330
 
        * @param        array           &$author_ary            an array of author ids if the author should be ignored during the search the array is empty
331
 
        * @param        array           &$id_ary                        passed by reference, to be filled with ids for the page specified by $start and $per_page, should be ordered
332
 
        * @param        int                     $start                          indicates the first index of the page
333
 
        * @param        int                     $per_page                       number of ids each page is supposed to contain
334
 
        * @return       boolean|int                                             total number of results
335
 
        *
336
 
        * @access       public
337
 
        */
338
 
        function keyword_search($type, &$fields, &$terms, &$sort_by_sql, &$sort_key, &$sort_dir, &$sort_days, &$ex_fid_ary, &$m_approve_fid_ary, &$topic_id, &$author_ary, &$id_ary, $start, $per_page)
339
 
        {
340
 
                global $config, $db;
341
 
 
342
 
                // No keywords? No posts.
343
 
                if (!$this->search_query)
344
 
                {
345
 
                        return false;
346
 
                }
347
 
 
348
 
                // generate a search_key from all the options to identify the results
349
 
                $search_key = md5(implode('#', array(
350
 
                        implode(', ', $this->split_words),
351
 
                        $type,
352
 
                        $fields,
353
 
                        $terms,
354
 
                        $sort_days,
355
 
                        $sort_key,
356
 
                        $topic_id,
357
 
                        implode(',', $ex_fid_ary),
358
 
                        implode(',', $m_approve_fid_ary),
359
 
                        implode(',', $author_ary)
360
 
                )));
361
 
 
362
 
                // try reading the results from cache
363
 
                $result_count = 0;
364
 
                if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE)
365
 
                {
366
 
                        return $result_count;
367
 
                }
368
 
 
369
 
                $id_ary = array();
370
 
 
371
 
                $join_topic = ($type == 'posts') ? false : true;
372
 
 
373
 
                // Build sql strings for sorting
374
 
                $sql_sort = $sort_by_sql[$sort_key] . (($sort_dir == 'a') ? ' ASC' : ' DESC');
375
 
                $sql_sort_table = $sql_sort_join = '';
376
 
 
377
 
                switch ($sql_sort[0])
378
 
                {
379
 
                        case 'u':
380
 
                                $sql_sort_table = USERS_TABLE . ' u, ';
381
 
                                $sql_sort_join  = ($type == 'posts') ? ' AND u.user_id = p.poster_id ' : ' AND u.user_id = t.topic_poster ';
382
 
                        break;
383
 
 
384
 
                        case 't':
385
 
                                $join_topic = true;
386
 
                        break;
387
 
 
388
 
                        case 'f':
389
 
                                $sql_sort_table = FORUMS_TABLE . ' f, ';
390
 
                                $sql_sort_join  = ' AND f.forum_id = p.forum_id ';
391
 
                        break;
392
 
                }
393
 
 
394
 
                // Build some display specific sql strings
395
 
                switch ($fields)
396
 
                {
397
 
                        case 'titleonly':
398
 
                                $sql_match = 'p.post_subject';
399
 
                                $sql_match_where = ' AND p.post_id = t.topic_first_post_id';
400
 
                                $join_topic = true;
401
 
                        break;
402
 
 
403
 
                        case 'msgonly':
404
 
                                $sql_match = 'p.post_text';
405
 
                                $sql_match_where = '';
406
 
                        break;
407
 
 
408
 
                        case 'firstpost':
409
 
                                $sql_match = 'p.post_subject, p.post_text';
410
 
                                $sql_match_where = ' AND p.post_id = t.topic_first_post_id';
411
 
                                $join_topic = true;
412
 
                        break;
413
 
 
414
 
                        default:
415
 
                                $sql_match = 'p.post_subject, p.post_text';
416
 
                                $sql_match_where = '';
417
 
                        break;
418
 
                }
419
 
 
420
 
                if (!sizeof($m_approve_fid_ary))
421
 
                {
422
 
                        $m_approve_fid_sql = ' AND p.post_approved = 1';
423
 
                }
424
 
                else if ($m_approve_fid_ary === array(-1))
425
 
                {
426
 
                        $m_approve_fid_sql = '';
427
 
                }
428
 
                else
429
 
                {
430
 
                        $m_approve_fid_sql = ' AND (p.post_approved = 1 OR ' . $db->sql_in_set('p.forum_id', $m_approve_fid_ary, true) . ')';
431
 
                }
432
 
 
433
 
                $sql_select                     = (!$result_count) ? 'SQL_CALC_FOUND_ROWS ' : '';
434
 
                $sql_select                     = ($type == 'posts') ? $sql_select . 'p.post_id' : 'DISTINCT ' . $sql_select . 't.topic_id';
435
 
                $sql_from                       = ($join_topic) ? TOPICS_TABLE . ' t, ' : '';
436
 
                $field                          = ($type == 'posts') ? 'post_id' : 'topic_id';
437
 
                $sql_author                     = (sizeof($author_ary) == 1) ? ' = ' . $author_ary[0] : 'IN (' . implode(', ', $author_ary) . ')';
438
 
 
439
 
                $sql_where_options = $sql_sort_join;
440
 
                $sql_where_options .= ($topic_id) ? ' AND p.topic_id = ' . $topic_id : '';
441
 
                $sql_where_options .= ($join_topic) ? ' AND t.topic_id = p.topic_id' : '';
442
 
                $sql_where_options .= (sizeof($ex_fid_ary)) ? ' AND ' . $db->sql_in_set('p.forum_id', $ex_fid_ary, true) : '';
443
 
                $sql_where_options .= $m_approve_fid_sql;
444
 
                $sql_where_options .= (sizeof($author_ary)) ? ' AND p.poster_id ' . $sql_author : '';
445
 
                $sql_where_options .= ($sort_days) ? ' AND p.post_time >= ' . (time() - ($sort_days * 86400)) : '';
446
 
                $sql_where_options .= $sql_match_where;
447
 
 
448
 
                $sql = "SELECT $sql_select
449
 
                        FROM $sql_from$sql_sort_table" . POSTS_TABLE . " p
450
 
                        WHERE MATCH ($sql_match) AGAINST ('" . $db->sql_escape(htmlspecialchars_decode($this->search_query)) . "' IN BOOLEAN MODE)
451
 
                                $sql_where_options
452
 
                        ORDER BY $sql_sort";
453
 
                $result = $db->sql_query_limit($sql, $config['search_block_size'], $start);
454
 
 
455
 
                while ($row = $db->sql_fetchrow($result))
456
 
                {
457
 
                        $id_ary[] = $row[$field];
458
 
                }
459
 
                $db->sql_freeresult($result);
460
 
 
461
 
                $id_ary = array_unique($id_ary);
462
 
 
463
 
                if (!sizeof($id_ary))
464
 
                {
465
 
                        return false;
466
 
                }
467
 
 
468
 
                // if the total result count is not cached yet, retrieve it from the db
469
 
                if (!$result_count)
470
 
                {
471
 
                        $sql = 'SELECT FOUND_ROWS() as result_count';
472
 
                        $result = $db->sql_query($sql);
473
 
                        $result_count = (int) $db->sql_fetchfield('result_count');
474
 
                        $db->sql_freeresult($result);
475
 
 
476
 
                        if (!$result_count)
477
 
                        {
478
 
                                return false;
479
 
                        }
480
 
                }
481
 
 
482
 
                // store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page
483
 
                $this->save_ids($search_key, implode(' ', $this->split_words), $author_ary, $result_count, $id_ary, $start, $sort_dir);
484
 
                $id_ary = array_slice($id_ary, 0, (int) $per_page);
485
 
 
486
 
                return $result_count;
487
 
        }
488
 
 
489
 
        /**
490
 
        * Performs a search on an author's posts without caring about message contents. Depends on display specific params
491
 
        *
492
 
        * @param array &$id_ary passed by reference, to be filled with ids for the page specified by $start and $per_page, should be ordered
493
 
        * @param int $start indicates the first index of the page
494
 
        * @param int $per_page number of ids each page is supposed to contain
495
 
        * @return total number of results
496
 
        */
497
 
        function author_search($type, $firstpost_only, &$sort_by_sql, &$sort_key, &$sort_dir, &$sort_days, &$ex_fid_ary, &$m_approve_fid_ary, &$topic_id, &$author_ary, &$id_ary, $start, $per_page)
498
 
        {
499
 
                global $config, $db;
500
 
 
501
 
                // No author? No posts.
502
 
                if (!sizeof($author_ary))
503
 
                {
504
 
                        return 0;
505
 
                }
506
 
 
507
 
                // generate a search_key from all the options to identify the results
508
 
                $search_key = md5(implode('#', array(
509
 
                        '',
510
 
                        $type,
511
 
                        ($firstpost_only) ? 'firstpost' : '',
512
 
                        '',
513
 
                        '',
514
 
                        $sort_days,
515
 
                        $sort_key,
516
 
                        $topic_id,
517
 
                        implode(',', $ex_fid_ary),
518
 
                        implode(',', $m_approve_fid_ary),
519
 
                        implode(',', $author_ary)
520
 
                )));
521
 
 
522
 
                // try reading the results from cache
523
 
                $result_count = 0;
524
 
                if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE)
525
 
                {
526
 
                        return $result_count;
527
 
                }
528
 
 
529
 
                $id_ary = array();
530
 
 
531
 
                // Create some display specific sql strings
532
 
                $sql_author             = $db->sql_in_set('p.poster_id', $author_ary);
533
 
                $sql_fora               = (sizeof($ex_fid_ary)) ? ' AND ' . $db->sql_in_set('p.forum_id', $ex_fid_ary, true) : '';
534
 
                $sql_topic_id   = ($topic_id) ? ' AND p.topic_id = ' . (int) $topic_id : '';
535
 
                $sql_time               = ($sort_days) ? ' AND p.post_time >= ' . (time() - ($sort_days * 86400)) : '';
536
 
                $sql_firstpost = ($firstpost_only) ? ' AND p.post_id = t.topic_first_post_id' : '';
537
 
 
538
 
                // Build sql strings for sorting
539
 
                $sql_sort = $sort_by_sql[$sort_key] . (($sort_dir == 'a') ? ' ASC' : ' DESC');
540
 
                $sql_sort_table = $sql_sort_join = '';
541
 
                switch ($sql_sort[0])
542
 
                {
543
 
                        case 'u':
544
 
                                $sql_sort_table = USERS_TABLE . ' u, ';
545
 
                                $sql_sort_join  = ($type == 'posts') ? ' AND u.user_id = p.poster_id ' : ' AND u.user_id = t.topic_poster ';
546
 
                        break;
547
 
 
548
 
                        case 't':
549
 
                                $sql_sort_table = ($type == 'posts') ? TOPICS_TABLE . ' t, ' : '';
550
 
                                $sql_sort_join  = ($type == 'posts') ? ' AND t.topic_id = p.topic_id ' : '';
551
 
                        break;
552
 
 
553
 
                        case 'f':
554
 
                                $sql_sort_table = FORUMS_TABLE . ' f, ';
555
 
                                $sql_sort_join  = ' AND f.forum_id = p.forum_id ';
556
 
                        break;
557
 
                }
558
 
 
559
 
                if (!sizeof($m_approve_fid_ary))
560
 
                {
561
 
                        $m_approve_fid_sql = ' AND p.post_approved = 1';
562
 
                }
563
 
                else if ($m_approve_fid_ary == array(-1))
564
 
                {
565
 
                        $m_approve_fid_sql = '';
566
 
                }
567
 
                else
568
 
                {
569
 
                        $m_approve_fid_sql = ' AND (p.post_approved = 1 OR ' . $db->sql_in_set('p.forum_id', $m_approve_fid_ary, true) . ')';
570
 
                }
571
 
 
572
 
                // If the cache was completely empty count the results
573
 
                $calc_results = ($result_count) ? '' : 'SQL_CALC_FOUND_ROWS ';
574
 
 
575
 
                // Build the query for really selecting the post_ids
576
 
                if ($type == 'posts')
577
 
                {
578
 
                        $sql = "SELECT {$calc_results}p.post_id
579
 
                                FROM " . $sql_sort_table . POSTS_TABLE . ' p' . (($firstpost_only) ? ', ' . TOPICS_TABLE . ' t ' : ' ') . "
580
 
                                WHERE $sql_author
581
 
                                        $sql_topic_id
582
 
                                        $sql_firstpost
583
 
                                        $m_approve_fid_sql
584
 
                                        $sql_fora
585
 
                                        $sql_sort_join
586
 
                                        $sql_time
587
 
                                ORDER BY $sql_sort";
588
 
                        $field = 'post_id';
589
 
                }
590
 
                else
591
 
                {
592
 
                        $sql = "SELECT {$calc_results}t.topic_id
593
 
                                FROM " . $sql_sort_table . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
594
 
                                WHERE $sql_author
595
 
                                        $sql_topic_id
596
 
                                        $sql_firstpost
597
 
                                        $m_approve_fid_sql
598
 
                                        $sql_fora
599
 
                                        AND t.topic_id = p.topic_id
600
 
                                        $sql_sort_join
601
 
                                        $sql_time
602
 
                                GROUP BY t.topic_id
603
 
                                ORDER BY $sql_sort";
604
 
                        $field = 'topic_id';
605
 
                }
606
 
 
607
 
                // Only read one block of posts from the db and then cache it
608
 
                $result = $db->sql_query_limit($sql, $config['search_block_size'], $start);
609
 
 
610
 
                while ($row = $db->sql_fetchrow($result))
611
 
                {
612
 
                        $id_ary[] = $row[$field];
613
 
                }
614
 
                $db->sql_freeresult($result);
615
 
 
616
 
                // retrieve the total result count if needed
617
 
                if (!$result_count)
618
 
                {
619
 
                        $sql = 'SELECT FOUND_ROWS() as result_count';
620
 
                        $result = $db->sql_query($sql);
621
 
                        $result_count = (int) $db->sql_fetchfield('result_count');
622
 
                        $db->sql_freeresult($result);
623
 
 
624
 
                        if (!$result_count)
625
 
                        {
626
 
                                return false;
627
 
                        }
628
 
                }
629
 
 
630
 
                if (sizeof($id_ary))
631
 
                {
632
 
                        $this->save_ids($search_key, '', $author_ary, $result_count, $id_ary, $start, $sort_dir);
633
 
                        $id_ary = array_slice($id_ary, 0, $per_page);
634
 
 
635
 
                        return $result_count;
636
 
                }
637
 
                return false;
638
 
        }
639
 
 
640
 
        /**
641
 
        * Destroys cached search results, that contained one of the new words in a post so the results won't be outdated.
642
 
        *
643
 
        * @param string $mode contains the post mode: edit, post, reply, quote ...
644
 
        */
645
 
        function index($mode, $post_id, &$message, &$subject, $poster_id, $forum_id)
646
 
        {
647
 
                global $db;
648
 
 
649
 
                // Split old and new post/subject to obtain array of words
650
 
                $split_text = $this->split_message($message);
651
 
                $split_title = ($subject) ? $this->split_message($subject) : array();
652
 
 
653
 
                $words = array_unique(array_merge($split_text, $split_title));
654
 
 
655
 
                unset($split_text);
656
 
                unset($split_title);
657
 
 
658
 
                // destroy cached search results containing any of the words removed or added
659
 
                $this->destroy_cache($words, array($poster_id));
660
 
 
661
 
                unset($words);
662
 
        }
663
 
 
664
 
        /**
665
 
        * Destroy cached results, that might be outdated after deleting a post
666
 
        */
667
 
        function index_remove($post_ids, $author_ids, $forum_ids)
668
 
        {
669
 
                $this->destroy_cache(array(), $author_ids);
670
 
        }
671
 
 
672
 
        /**
673
 
        * Destroy old cache entries
674
 
        */
675
 
        function tidy()
676
 
        {
677
 
                global $db, $config;
678
 
 
679
 
                // destroy too old cached search results
680
 
                $this->destroy_cache(array());
681
 
 
682
 
                set_config('search_last_gc', time(), true);
683
 
        }
684
 
 
685
 
        /**
686
 
        * Create fulltext index
687
 
        */
688
 
        function create_index($acp_module, $u_action)
689
 
        {
690
 
                global $db;
691
 
 
692
 
                // Make sure we can actually use MySQL with fulltext indexes
693
 
                if ($error = $this->init())
694
 
                {
695
 
                        return $error;
696
 
                }
697
 
 
698
 
                if (empty($this->stats))
699
 
                {
700
 
                        $this->get_stats();
701
 
                }
702
 
 
703
 
                $alter = array();
704
 
 
705
 
                if (!isset($this->stats['post_subject']))
706
 
                {
707
 
                        if ($db->sql_layer == 'mysqli' || version_compare($db->mysql_version, '4.1.3', '>='))
708
 
                        {
709
 
                                //$alter[] = 'MODIFY post_subject varchar(100) COLLATE utf8_unicode_ci DEFAULT \'\' NOT NULL';
710
 
                        }
711
 
                        else
712
 
                        {
713
 
                                $alter[] = 'MODIFY post_subject text NOT NULL';
714
 
                        }
715
 
                        $alter[] = 'ADD FULLTEXT (post_subject)';
716
 
                }
717
 
 
718
 
                if (!isset($this->stats['post_text']))
719
 
                {
720
 
                        if ($db->sql_layer == 'mysqli' || version_compare($db->mysql_version, '4.1.3', '>='))
721
 
                        {
722
 
                                $alter[] = 'MODIFY post_text mediumtext COLLATE utf8_unicode_ci NOT NULL';
723
 
                        }
724
 
                        else
725
 
                        {
726
 
                                $alter[] = 'MODIFY post_text mediumtext NOT NULL';
727
 
                        }
728
 
                        $alter[] = 'ADD FULLTEXT (post_text)';
729
 
                }
730
 
 
731
 
                if (!isset($this->stats['post_content']))
732
 
                {
733
 
                        $alter[] = 'ADD FULLTEXT post_content (post_subject, post_text)';
734
 
                }
735
 
 
736
 
                if (sizeof($alter))
737
 
                {
738
 
                        $db->sql_query('ALTER TABLE ' . POSTS_TABLE . ' ' . implode(', ', $alter));
739
 
                }
740
 
 
741
 
                $db->sql_query('TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE);
742
 
 
743
 
                return false;
744
 
        }
745
 
 
746
 
        /**
747
 
        * Drop fulltext index
748
 
        */
749
 
        function delete_index($acp_module, $u_action)
750
 
        {
751
 
                global $db;
752
 
 
753
 
                // Make sure we can actually use MySQL with fulltext indexes
754
 
                if ($error = $this->init())
755
 
                {
756
 
                        return $error;
757
 
                }
758
 
 
759
 
                if (empty($this->stats))
760
 
                {
761
 
                        $this->get_stats();
762
 
                }
763
 
 
764
 
                $alter = array();
765
 
 
766
 
                if (isset($this->stats['post_subject']))
767
 
                {
768
 
                        $alter[] = 'DROP INDEX post_subject';
769
 
                }
770
 
 
771
 
                if (isset($this->stats['post_text']))
772
 
                {
773
 
                        $alter[] = 'DROP INDEX post_text';
774
 
                }
775
 
 
776
 
                if (isset($this->stats['post_content']))
777
 
                {
778
 
                        $alter[] = 'DROP INDEX post_content';
779
 
                }
780
 
 
781
 
                if (sizeof($alter))
782
 
                {
783
 
                        $db->sql_query('ALTER TABLE ' . POSTS_TABLE . ' ' . implode(', ', $alter));
784
 
                }
785
 
 
786
 
                $db->sql_query('TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE);
787
 
 
788
 
                return false;
789
 
        }
790
 
 
791
 
        /**
792
 
        * Returns true if both FULLTEXT indexes exist
793
 
        */
794
 
        function index_created()
795
 
        {
796
 
                if (empty($this->stats))
797
 
                {
798
 
                        $this->get_stats();
799
 
                }
800
 
 
801
 
                return (isset($this->stats['post_text']) && isset($this->stats['post_subject']) && isset($this->stats['post_content'])) ? true : false;
802
 
        }
803
 
 
804
 
        /**
805
 
        * Returns an associative array containing information about the indexes
806
 
        */
807
 
        function index_stats()
808
 
        {
809
 
                global $user;
810
 
 
811
 
                if (empty($this->stats))
812
 
                {
813
 
                        $this->get_stats();
814
 
                }
815
 
 
816
 
                return array(
817
 
                        $user->lang['FULLTEXT_MYSQL_TOTAL_POSTS']                       => ($this->index_created()) ? $this->stats['total_posts'] : 0,
818
 
                );
819
 
        }
820
 
 
821
 
        function get_stats()
822
 
        {
823
 
                global $db;
824
 
 
825
 
                if (strpos($db->sql_layer, 'mysql') === false)
826
 
                {
827
 
                        $this->stats = array();
828
 
                        return;
829
 
                }
830
 
 
831
 
                $sql = 'SHOW INDEX
832
 
                        FROM ' . POSTS_TABLE;
833
 
                $result = $db->sql_query($sql);
834
 
 
835
 
                while ($row = $db->sql_fetchrow($result))
836
 
                {
837
 
                        // deal with older MySQL versions which didn't use Index_type
838
 
                        $index_type = (isset($row['Index_type'])) ? $row['Index_type'] : $row['Comment'];
839
 
 
840
 
                        if ($index_type == 'FULLTEXT')
841
 
                        {
842
 
                                if ($row['Key_name'] == 'post_text')
843
 
                                {
844
 
                                        $this->stats['post_text'] = $row;
845
 
                                }
846
 
                                else if ($row['Key_name'] == 'post_subject')
847
 
                                {
848
 
                                        $this->stats['post_subject'] = $row;
849
 
                                }
850
 
                                else if ($row['Key_name'] == 'post_content')
851
 
                                {
852
 
                                        $this->stats['post_content'] = $row;
853
 
                                }
854
 
                        }
855
 
                }
856
 
                $db->sql_freeresult($result);
857
 
 
858
 
                $sql = 'SELECT COUNT(post_id) as total_posts
859
 
                        FROM ' . POSTS_TABLE;
860
 
                $result = $db->sql_query($sql);
861
 
                $this->stats['total_posts'] = (int) $db->sql_fetchfield('total_posts');
862
 
                $db->sql_freeresult($result);
863
 
        }
864
 
 
865
 
        /**
866
 
        * Display a note, that UTF-8 support is not available with certain versions of PHP
867
 
        */
868
 
        function acp()
869
 
        {
870
 
                global $user, $config;
871
 
 
872
 
                $tpl = '
873
 
                <dl>
874
 
                        <dt><label>' . $user->lang['FULLTEXT_MYSQL_PCRE'] . '</label><br /><span>' . $user->lang['FULLTEXT_MYSQL_PCRE_EXPLAIN'] . '</span></dt>
875
 
                        <dd>' . (($this->pcre_properties) ? $user->lang['YES'] : $user->lang['NO']) . ' (PHP ' . PHP_VERSION . ')</dd>
876
 
                </dl>
877
 
                <dl>
878
 
                        <dt><label>' . $user->lang['FULLTEXT_MYSQL_MBSTRING'] . '</label><br /><span>' . $user->lang['FULLTEXT_MYSQL_MBSTRING_EXPLAIN'] . '</span></dt>
879
 
                        <dd>' . (($this->mbstring_regex) ? $user->lang['YES'] : $user->lang['NO']). '</dd>
880
 
                </dl>
881
 
                ';
882
 
 
883
 
                // These are fields required in the config table
884
 
                return array(
885
 
                        'tpl'           => $tpl,
886
 
                        'config'        => array()
887
 
                );
888
 
        }
889
 
}
890
 
 
891
 
?>
 
 
b'\\ No newline at end of file'