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

« back to all changes in this revision

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

  • Committer: dcoles
  • Date: 2008-02-13 04:10:55 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:443
Added Forum application along with unmodifed version of phpBB3 "Olympus" 3.0.0

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'