2005-01-13 22:30:42 +00:00
< ? php
2007-06-09 11:11:20 +00:00
/**
2005-04-09 12:26:45 +00:00
*
* @ package search
* @ version $Id $
2007-06-09 11:11:20 +00:00
* @ copyright ( c ) 2005 phpBB Group
* @ license http :// opensource . org / licenses / gpl - license . php GNU Public License
2005-04-09 12:26:45 +00:00
*
*/
2005-01-13 22:30:42 +00:00
2006-03-19 14:23:21 +00:00
/**
2007-10-05 14:36:34 +00:00
* @ ignore
2006-03-19 14:23:21 +00:00
*/
if ( ! defined ( 'IN_PHPBB' ))
{
exit ;
}
2006-01-22 19:13:12 +00:00
/**
* @ ignore
*/
2006-03-15 23:20:04 +00:00
include_once ( $phpbb_root_path . 'includes/search/search.' . $phpEx );
2006-01-22 19:13:12 +00:00
2005-04-09 12:26:45 +00:00
/**
* fulltext_mysql
2006-01-22 19:13:12 +00:00
* Fulltext search for MySQL
2006-06-13 21:06:29 +00:00
* @ package search
2005-04-09 12:26:45 +00:00
*/
2006-01-22 19:13:12 +00:00
class fulltext_mysql extends search_backend
2005-01-13 22:30:42 +00:00
{
2006-07-27 19:02:47 +00:00
var $stats = array ();
var $word_length = array ();
var $split_words = array ();
2006-07-30 14:09:52 +00:00
var $search_query ;
2006-07-27 19:02:47 +00:00
var $common_words = array ();
2007-01-05 21:17:28 +00:00
var $pcre_properties = false ;
2007-03-31 14:36:01 +00:00
var $mbstring_regex = false ;
2006-03-15 23:20:04 +00:00
2005-01-13 22:30:42 +00:00
function fulltext_mysql ( & $error )
{
2006-03-18 14:30:47 +00:00
global $config ;
$this -> word_length = array ( 'min' => $config [ 'fulltext_mysql_min_word_len' ], 'max' => $config [ 'fulltext_mysql_max_word_len' ]);
2007-01-05 21:17:28 +00:00
if ( version_compare ( PHP_VERSION , '5.1.0' , '>=' ) || ( version_compare ( PHP_VERSION , '5.0.0-dev' , '<=' ) && version_compare ( PHP_VERSION , '4.4.0' , '>=' )))
{
2007-07-23 04:14:49 +00:00
// 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
if ( @ preg_match ( '/\p{L}/u' , 'a' ) !== false )
{
$this -> pcre_properties = true ;
}
2007-01-05 21:17:28 +00:00
}
2007-03-31 14:36:01 +00:00
if ( function_exists ( 'mb_ereg' ))
{
$this -> mbstring_regex = true ;
}
2007-03-31 14:42:33 +00:00
2006-03-15 23:20:04 +00:00
$error = false ;
}
2005-01-13 22:30:42 +00:00
2006-03-15 23:20:04 +00:00
/**
2006-03-26 01:36:26 +00:00
* Checks for correct MySQL version and stores min / max word length in the config
2006-03-15 23:20:04 +00:00
*/
function init ()
{
global $db , $user ;
2006-01-22 19:13:12 +00:00
2006-12-10 17:44:45 +00:00
if ( $db -> sql_layer != 'mysql4' && $db -> sql_layer != 'mysqli' )
2006-03-15 23:20:04 +00:00
{
return $user -> lang [ 'FULLTEXT_MYSQL_INCOMPATIBLE_VERSION' ];
}
2006-03-16 16:22:32 +00:00
$result = $db -> sql_query ( 'SHOW TABLE STATUS LIKE \'' . POSTS_TABLE . '\'' );
2006-03-26 01:36:26 +00:00
$info = $db -> sql_fetchrow ( $result );
$db -> sql_freeresult ( $result );
$engine = '' ;
if ( isset ( $info [ 'Engine' ]))
2006-03-16 16:22:32 +00:00
{
2006-03-26 01:36:26 +00:00
$engine = $info [ 'Engine' ];
}
else if ( isset ( $info [ 'Type' ]))
{
$engine = $info [ 'Type' ];
2006-03-16 16:22:32 +00:00
}
if ( $engine != 'MyISAM' )
{
return $user -> lang [ 'FULLTEXT_MYSQL_NOT_MYISAM' ];
}
2006-01-22 19:13:12 +00:00
$sql = ' SHOW VARIABLES
LIKE \ 'ft\_%\'' ;
2006-03-15 23:20:04 +00:00
$result = $db -> sql_query ( $sql );
2006-01-22 19:13:12 +00:00
2006-03-15 23:20:04 +00:00
$mysql_info = array ();
2006-01-22 19:13:12 +00:00
while ( $row = $db -> sql_fetchrow ( $result ))
{
2006-03-15 23:20:04 +00:00
$mysql_info [ $row [ 'Variable_name' ]] = $row [ 'Value' ];
2006-01-22 19:13:12 +00:00
}
$db -> sql_freeresult ( $result );
2005-01-13 22:30:42 +00:00
2006-03-15 23:20:04 +00:00
set_config ( 'fulltext_mysql_max_word_len' , $mysql_info [ 'ft_max_word_len' ]);
set_config ( 'fulltext_mysql_min_word_len' , $mysql_info [ 'ft_min_word_len' ]);
return false ;
2005-01-13 22:30:42 +00:00
}
2005-10-02 16:48:17 +00:00
2006-01-22 19:13:12 +00:00
/**
* Splits keywords entered by a user into an array of words stored in $this -> split_words
2006-07-27 19:02:47 +00:00
* Stores the tidied search query in $this -> search_query
2006-01-22 19:13:12 +00:00
*
2006-11-17 19:37:57 +00:00
* @ param string & $keywords Contains the keyword as entered by the user
2006-01-22 19:13:12 +00:00
* @ param string $terms is either 'all' or 'any'
2006-11-17 19:37:57 +00:00
* @ return bool false if no valid keywords were found and otherwise true
2006-01-22 19:13:12 +00:00
*/
function split_keywords ( & $keywords , $terms )
2005-01-13 22:30:42 +00:00
{
2006-01-22 19:13:12 +00:00
global $config ;
if ( $terms == 'all' )
2005-01-13 22:30:42 +00:00
{
2006-11-27 19:32:18 +00:00
$match = array ( '#\sand\s#iu' , '#\sor\s#iu' , '#\snot\s#iu' , '#\+#' , '#-#' , '#\|#' );
2006-01-22 19:13:12 +00:00
$replace = array ( ' +' , ' |' , ' -' , ' +' , ' -' , ' |' );
2005-01-13 22:30:42 +00:00
2006-01-22 19:13:12 +00:00
$keywords = preg_replace ( $match , $replace , $keywords );
}
2005-01-13 22:30:42 +00:00
2006-01-22 19:13:12 +00:00
// Filter out as above
2007-04-22 17:12:31 +00:00
$split_keywords = preg_replace ( " #[ \n \r \t ]+# " , ' ' , trim ( htmlspecialchars_decode ( $keywords )));
2006-01-22 19:13:12 +00:00
// Split words
2007-03-31 14:36:01 +00:00
if ( $this -> pcre_properties )
{
2007-04-22 17:12:31 +00:00
$split_keywords = preg_replace ( '#([^\p{L}\p{N}\'*"()])#u' , '$1$1' , str_replace ( '\'\'' , '\' \'' , trim ( $split_keywords )));
2007-03-31 14:36:01 +00:00
}
else if ( $this -> mbstring_regex )
{
2007-04-22 17:12:31 +00:00
$split_keywords = mb_ereg_replace ( '([^\w\'*"()])' , '\\1\\1' , str_replace ( '\'\'' , '\' \'' , trim ( $split_keywords )));
2007-03-31 14:36:01 +00:00
}
else
{
2007-04-22 17:12:31 +00:00
$split_keywords = preg_replace ( '#([^\w\'*"()])#u' , '$1$1' , str_replace ( '\'\'' , '\' \'' , trim ( $split_keywords )));
2007-03-31 14:36:01 +00:00
}
if ( $this -> pcre_properties )
{
$matches = array ();
2007-04-22 17:12:31 +00:00
preg_match_all ( '#(?:[^\p{L}\p{N}*"()]|^)([+\-|]?(?:[\p{L}\p{N}*"()]+\'?)*[\p{L}\p{N}*"()])(?:[^\p{L}\p{N}*"()]|$)#u' , $split_keywords , $matches );
2007-03-31 14:36:01 +00:00
$this -> split_words = $matches [ 1 ];
}
else if ( $this -> mbstring_regex )
{
mb_regex_encoding ( 'UTF-8' );
2007-04-22 17:12:31 +00:00
mb_ereg_search_init ( $split_keywords , '(?:[^\w*"()]|^)([+\-|]?(?:[\w*"()]+\'?)*[\w*"()])(?:[^\w*"()]|$)' );
2007-03-31 14:36:01 +00:00
while (( $word = mb_ereg_search_regs ()))
{
$this -> split_words [] = $word [ 1 ];
}
}
else
{
$matches = array ();
2007-04-22 17:12:31 +00:00
preg_match_all ( '#(?:[^\w*"()]|^)([+\-|]?(?:[\w*"()]+\'?)*[\w*"()])(?:[^\w*"()]|$)#u' , $split_keywords , $matches );
2007-03-31 14:36:01 +00:00
$this -> split_words = $matches [ 1 ];
}
2007-04-22 17:12:31 +00:00
// to allow phrase search, we need to concatenate quoted words
$tmp_split_words = array ();
$phrase = '' ;
foreach ( $this -> split_words as $word )
{
if ( $phrase )
{
$phrase .= ' ' . $word ;
if ( strpos ( $word , '"' ) !== false && substr_count ( $word , '"' ) % 2 == 1 )
{
$tmp_split_words [] = $phrase ;
$phrase = '' ;
}
}
else if ( strpos ( $word , '"' ) !== false && substr_count ( $word , '"' ) % 2 == 1 )
{
$phrase = $word ;
}
else
{
$tmp_split_words [] = $word . ' ' ;
}
}
if ( $phrase )
{
$tmp_split_words [] = $phrase ;
}
$this -> split_words = $tmp_split_words ;
unset ( $tmp_split_words );
unset ( $phrase );
2006-01-22 19:13:12 +00:00
foreach ( $this -> split_words as $i => $word )
{
2007-04-22 17:12:31 +00:00
$clean_word = preg_replace ( '#^[+\-|"]#' , '' , $word );
2006-01-22 19:13:12 +00:00
// check word length
2006-10-07 12:36:31 +00:00
$clean_len = utf8_strlen ( str_replace ( '*' , '' , $clean_word ));
2006-03-15 23:20:04 +00:00
if (( $clean_len < $config [ 'fulltext_mysql_min_word_len' ]) || ( $clean_len > $config [ 'fulltext_mysql_max_word_len' ]))
2005-01-13 22:30:42 +00:00
{
2006-01-22 19:13:12 +00:00
$this -> common_words [] = $word ;
unset ( $this -> split_words [ $i ]);
2005-01-13 22:30:42 +00:00
}
2006-01-22 19:13:12 +00:00
}
2005-01-13 22:30:42 +00:00
2007-04-22 17:12:31 +00:00
if ( $terms == 'any' )
{
$this -> search_query = '' ;
foreach ( $this -> split_words as $word )
{
if (( strpos ( $word , '+' ) === 0 ) || ( strpos ( $word , '-' ) === 0 ) || ( strpos ( $word , '|' ) === 0 ))
{
$word = substr ( $word , 1 );
}
$this -> search_query .= $word . ' ' ;
}
}
else
{
$this -> search_query = '' ;
foreach ( $this -> split_words as $word )
{
if (( strpos ( $word , '+' ) === 0 ) || ( strpos ( $word , '-' ) === 0 ))
{
$this -> search_query .= $word . ' ' ;
}
else if ( strpos ( $word , '|' ) === 0 )
{
$this -> search_query .= substr ( $word , 1 ) . ' ' ;
}
else
{
$this -> search_query .= '+' . $word . ' ' ;
}
}
}
$this -> search_query = utf8_htmlspecialchars ( $this -> search_query );
2006-07-27 19:02:47 +00:00
2007-04-22 17:12:31 +00:00
if ( $this -> search_query )
2006-01-22 19:13:12 +00:00
{
$this -> split_words = array_values ( $this -> split_words );
2006-05-28 19:06:21 +00:00
sort ( $this -> split_words );
2006-01-22 19:13:12 +00:00
return true ;
}
return false ;
}
2005-01-13 22:30:42 +00:00
2006-01-22 19:13:12 +00:00
/**
2006-03-26 01:36:26 +00:00
* Turns text into an array of words
2006-01-22 19:13:12 +00:00
*/
function split_message ( $text )
{
global $config ;
2005-01-13 22:30:42 +00:00
2006-01-22 19:13:12 +00:00
// Split words
2007-03-31 14:36:01 +00:00
if ( $this -> pcre_properties )
{
$text = preg_replace ( '#([^\p{L}\p{N}\'*])#u' , '$1$1' , str_replace ( '\'\'' , '\' \'' , trim ( $text )));
}
else if ( $this -> mbstring_regex )
{
$text = mb_ereg_replace ( '([^\w\'*])' , '\\1\\1' , str_replace ( '\'\'' , '\' \'' , trim ( $text )));
}
else
{
$text = preg_replace ( '#([^\w\'*])#u' , '$1$1' , str_replace ( '\'\'' , '\' \'' , trim ( $text )));
}
if ( $this -> pcre_properties )
{
$matches = array ();
preg_match_all ( '#(?:[^\p{L}\p{N}*]|^)([+\-|]?(?:[\p{L}\p{N}*]+\'?)*[\p{L}\p{N}*])(?:[^\p{L}\p{N}*]|$)#u' , $text , $matches );
$text = $matches [ 1 ];
}
else if ( $this -> mbstring_regex )
{
mb_regex_encoding ( 'UTF-8' );
mb_ereg_search_init ( $text , '(?:[^\w*]|^)([+\-|]?(?:[\w*]+\'?)*[\w*])(?:[^\w*]|$)' );
2007-03-31 17:33:18 +00:00
$text = array ();
2007-03-31 14:36:01 +00:00
while (( $word = mb_ereg_search_regs ()))
{
$text [] = $word [ 1 ];
}
}
else
{
$matches = array ();
preg_match_all ( '#(?:[^\w*]|^)([+\-|]?(?:[\w*]+\'?)*[\w*])(?:[^\w*]|$)#u' , $text , $matches );
$text = $matches [ 1 ];
}
2005-01-13 22:30:42 +00:00
2006-01-22 19:13:12 +00:00
// remove too short or too long words
$text = array_values ( $text );
for ( $i = 0 , $n = sizeof ( $text ); $i < $n ; $i ++ )
{
$text [ $i ] = trim ( $text [ $i ]);
2006-10-07 12:36:31 +00:00
if ( utf8_strlen ( $text [ $i ]) < $config [ 'fulltext_mysql_min_word_len' ] || utf8_strlen ( $text [ $i ]) > $config [ 'fulltext_mysql_max_word_len' ])
2005-01-13 22:30:42 +00:00
{
2006-01-22 19:13:12 +00:00
unset ( $text [ $i ]);
2005-01-13 22:30:42 +00:00
}
}
2006-01-22 19:13:12 +00:00
return array_values ( $text );
}
/**
2007-04-22 17:12:31 +00:00
* Performs a search on keywords depending on display specific params . You have to run split_keywords () first .
2006-01-22 19:13:12 +00:00
*
2007-04-22 17:12:31 +00:00
* @ param string $type contains either posts or topics depending on what should be searched for
* @ 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 )
* @ 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 )
* @ param array & $sort_by_sql contains SQL code for the ORDER BY part of a query
* @ param string & $sort_key is the key of $sort_by_sql for the selected sorting
* @ param string & $sort_dir is either a or d representing ASC and DESC
* @ param string & $sort_days specifies the maximum amount of days a post may be old
* @ param array & $ex_fid_ary specifies an array of forum ids which should not be searched
* @ param array & $m_approve_fid_ary specifies an array of forum ids in which the searcher is allowed to view unapproved posts
* @ 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
* @ param array & $author_ary an array of author ids if the author should be ignored during the search the array is empty
* @ param array & $id_ary passed by reference , to be filled with ids for the page specified by $start and $per_page , should be ordered
* @ param int $start indicates the first index of the page
* @ param int $per_page number of ids each page is supposed to contain
* @ return boolean | int total number of results
*
* @ access public
2006-01-22 19:13:12 +00:00
*/
2006-03-06 18:21:54 +00:00
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 )
2006-01-22 19:13:12 +00:00
{
global $config , $db ;
// No keywords? No posts.
2007-04-22 17:12:31 +00:00
if ( ! $this -> search_query )
2005-01-13 22:30:42 +00:00
{
2006-01-22 19:13:12 +00:00
return false ;
2005-01-13 22:30:42 +00:00
}
2006-01-22 19:13:12 +00:00
// generate a search_key from all the options to identify the results
$search_key = md5 ( implode ( '#' , array (
2007-04-22 17:12:31 +00:00
implode ( ', ' , $this -> split_words ),
2006-01-22 19:13:12 +00:00
$type ,
$fields ,
$terms ,
$sort_days ,
$sort_key ,
$topic_id ,
implode ( ',' , $ex_fid_ary ),
2006-03-06 18:21:54 +00:00
implode ( ',' , $m_approve_fid_ary ),
2006-01-22 19:13:12 +00:00
implode ( ',' , $author_ary )
)));
// try reading the results from cache
$result_count = 0 ;
if ( $this -> obtain_ids ( $search_key , $result_count , $id_ary , $start , $per_page , $sort_dir ) == SEARCH_RESULT_IN_CACHE )
2005-01-13 22:30:42 +00:00
{
2006-01-22 19:13:12 +00:00
return $result_count ;
}
2005-01-13 22:30:42 +00:00
2006-01-22 19:13:12 +00:00
$id_ary = array ();
2005-01-13 22:30:42 +00:00
2006-01-22 19:13:12 +00:00
$join_topic = ( $type == 'posts' ) ? false : true ;
2006-06-16 16:54:51 +00:00
2006-01-22 19:13:12 +00:00
// Build sql strings for sorting
$sql_sort = $sort_by_sql [ $sort_key ] . (( $sort_dir == 'a' ) ? ' ASC' : ' DESC' );
$sql_sort_table = $sql_sort_join = '' ;
2006-06-16 16:54:51 +00:00
2006-01-22 19:13:12 +00:00
switch ( $sql_sort [ 0 ])
{
case 'u' :
$sql_sort_table = USERS_TABLE . ' u, ' ;
$sql_sort_join = ( $type == 'posts' ) ? ' AND u.user_id = p.poster_id ' : ' AND u.user_id = t.topic_poster ' ;
break ;
2005-10-02 16:48:17 +00:00
2006-01-22 19:13:12 +00:00
case 't' :
$join_topic = true ;
break ;
case 'f' :
$sql_sort_table = FORUMS_TABLE . ' f, ' ;
$sql_sort_join = ' AND f.forum_id = p.forum_id ' ;
break ;
}
// Build some display specific sql strings
switch ( $fields )
{
case 'titleonly' :
$sql_match = 'p.post_subject' ;
$sql_match_where = ' AND p.post_id = t.topic_first_post_id' ;
$join_topic = true ;
break ;
case 'msgonly' :
$sql_match = 'p.post_text' ;
$sql_match_where = '' ;
break ;
case 'firstpost' :
$sql_match = 'p.post_subject, p.post_text' ;
$sql_match_where = ' AND p.post_id = t.topic_first_post_id' ;
$join_topic = true ;
break ;
default :
$sql_match = 'p.post_subject, p.post_text' ;
$sql_match_where = '' ;
2006-06-16 16:54:51 +00:00
break ;
2006-01-22 19:13:12 +00:00
}
2006-03-06 18:21:54 +00:00
if ( ! sizeof ( $m_approve_fid_ary ))
{
$m_approve_fid_sql = ' AND p.post_approved = 1' ;
}
else if ( $m_approve_fid_ary === array ( - 1 ))
{
$m_approve_fid_sql = '' ;
}
else
{
2006-08-12 13:14:39 +00:00
$m_approve_fid_sql = ' AND (p.post_approved = 1 OR ' . $db -> sql_in_set ( 'p.forum_id' , $m_approve_fid_ary , true ) . ')' ;
2006-03-06 18:21:54 +00:00
}
2006-01-22 19:13:12 +00:00
$sql_select = ( ! $result_count ) ? 'SQL_CALC_FOUND_ROWS ' : '' ;
$sql_select = ( $type == 'posts' ) ? $sql_select . 'p.post_id' : 'DISTINCT ' . $sql_select . 't.topic_id' ;
$sql_from = ( $join_topic ) ? TOPICS_TABLE . ' t, ' : '' ;
2006-03-06 18:21:54 +00:00
$field = ( $type == 'posts' ) ? 'post_id' : 'topic_id' ;
2006-08-12 13:14:39 +00:00
$sql_author = ( sizeof ( $author_ary ) == 1 ) ? ' = ' . $author_ary [ 0 ] : 'IN (' . implode ( ', ' , $author_ary ) . ')' ;
2006-01-22 19:13:12 +00:00
$sql_where_options = $sql_sort_join ;
$sql_where_options .= ( $topic_id ) ? ' AND p.topic_id = ' . $topic_id : '' ;
$sql_where_options .= ( $join_topic ) ? ' AND t.topic_id = p.topic_id' : '' ;
2006-08-12 13:14:39 +00:00
$sql_where_options .= ( sizeof ( $ex_fid_ary )) ? ' AND ' . $db -> sql_in_set ( 'p.forum_id' , $ex_fid_ary , true ) : '' ;
2006-03-06 18:21:54 +00:00
$sql_where_options .= $m_approve_fid_sql ;
2006-01-22 19:13:12 +00:00
$sql_where_options .= ( sizeof ( $author_ary )) ? ' AND p.poster_id ' . $sql_author : '' ;
$sql_where_options .= ( $sort_days ) ? ' AND p.post_time >= ' . ( time () - ( $sort_days * 86400 )) : '' ;
$sql_where_options .= $sql_match_where ;
$sql = " SELECT $sql_select
FROM $sql_from $sql_sort_table " . POSTS_TABLE . " p
2007-04-22 17:12:31 +00:00
WHERE MATCH ( $sql_match ) AGAINST ( '" . $db->sql_escape(htmlspecialchars_decode($this->search_query)) . "' IN BOOLEAN MODE )
2006-01-22 19:13:12 +00:00
$sql_where_options
ORDER BY $sql_sort " ;
$result = $db -> sql_query_limit ( $sql , $config [ 'search_block_size' ], $start );
2005-01-13 22:30:42 +00:00
2006-01-22 19:13:12 +00:00
while ( $row = $db -> sql_fetchrow ( $result ))
{
2006-03-06 18:21:54 +00:00
$id_ary [] = $row [ $field ];
2006-01-22 19:13:12 +00:00
}
$db -> sql_freeresult ( $result );
$id_ary = array_unique ( $id_ary );
2005-01-13 22:30:42 +00:00
2006-01-22 19:13:12 +00:00
if ( ! sizeof ( $id_ary ))
{
return false ;
}
// if the total result count is not cached yet, retrieve it from the db
if ( ! $result_count )
{
$sql = 'SELECT FOUND_ROWS() as result_count' ;
$result = $db -> sql_query ( $sql );
2006-03-22 17:30:20 +00:00
$result_count = ( int ) $db -> sql_fetchfield ( 'result_count' );
$db -> sql_freeresult ( $result );
2006-01-22 19:13:12 +00:00
2006-03-22 17:30:20 +00:00
if ( ! $result_count )
2005-01-13 22:30:42 +00:00
{
2006-01-22 19:13:12 +00:00
return false ;
2005-01-13 22:30:42 +00:00
}
2006-01-22 19:13:12 +00:00
}
2005-01-13 22:30:42 +00:00
2006-01-22 19:13:12 +00:00
// store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page
$this -> save_ids ( $search_key , implode ( ' ' , $this -> split_words ), $author_ary , $result_count , $id_ary , $start , $sort_dir );
$id_ary = array_slice ( $id_ary , 0 , ( int ) $per_page );
2005-01-13 22:30:42 +00:00
2006-01-22 19:13:12 +00:00
return $result_count ;
}
/**
* Performs a search on an author ' s posts without caring about message contents . Depends on display specific params
*
2006-11-17 19:37:57 +00:00
* @ param array & $id_ary passed by reference , to be filled with ids for the page specified by $start and $per_page , should be ordered
2006-01-22 19:13:12 +00:00
* @ param int $start indicates the first index of the page
* @ param int $per_page number of ids each page is supposed to contain
* @ return total number of results
*/
2007-07-23 17:03:37 +00:00
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 )
2006-01-22 19:13:12 +00:00
{
global $config , $db ;
// No author? No posts.
if ( ! sizeof ( $author_ary ))
{
return 0 ;
2005-01-13 22:30:42 +00:00
}
2006-01-22 19:13:12 +00:00
// generate a search_key from all the options to identify the results
$search_key = md5 ( implode ( '#' , array (
'' ,
$type ,
2007-07-23 17:03:37 +00:00
( $firstpost_only ) ? 'firstpost' : '' ,
2006-01-22 19:13:12 +00:00
'' ,
'' ,
$sort_days ,
$sort_key ,
$topic_id ,
implode ( ',' , $ex_fid_ary ),
2006-03-06 18:21:54 +00:00
implode ( ',' , $m_approve_fid_ary ),
2006-01-22 19:13:12 +00:00
implode ( ',' , $author_ary )
)));
// try reading the results from cache
$result_count = 0 ;
if ( $this -> obtain_ids ( $search_key , $result_count , $id_ary , $start , $per_page , $sort_dir ) == SEARCH_RESULT_IN_CACHE )
2005-01-13 22:30:42 +00:00
{
2006-01-22 19:13:12 +00:00
return $result_count ;
}
2005-01-13 22:30:42 +00:00
2006-01-22 19:13:12 +00:00
$id_ary = array ();
// Create some display specific sql strings
2006-08-12 13:14:39 +00:00
$sql_author = $db -> sql_in_set ( 'p.poster_id' , $author_ary );
$sql_fora = ( sizeof ( $ex_fid_ary )) ? ' AND ' . $db -> sql_in_set ( 'p.forum_id' , $ex_fid_ary , true ) : '' ;
2006-03-06 18:21:54 +00:00
$sql_topic_id = ( $topic_id ) ? ' AND p.topic_id = ' . ( int ) $topic_id : '' ;
2006-01-22 19:13:12 +00:00
$sql_time = ( $sort_days ) ? ' AND p.post_time >= ' . ( time () - ( $sort_days * 86400 )) : '' ;
2007-07-23 17:03:37 +00:00
$sql_firstpost = ( $firstpost_only ) ? ' AND p.post_id = t.topic_first_post_id' : '' ;
2006-01-22 19:13:12 +00:00
// Build sql strings for sorting
$sql_sort = $sort_by_sql [ $sort_key ] . (( $sort_dir == 'a' ) ? ' ASC' : ' DESC' );
$sql_sort_table = $sql_sort_join = '' ;
switch ( $sql_sort [ 0 ])
{
case 'u' :
$sql_sort_table = USERS_TABLE . ' u, ' ;
$sql_sort_join = ( $type == 'posts' ) ? ' AND u.user_id = p.poster_id ' : ' AND u.user_id = t.topic_poster ' ;
break ;
case 't' :
$sql_sort_table = ( $type == 'posts' ) ? TOPICS_TABLE . ' t, ' : '' ;
$sql_sort_join = ( $type == 'posts' ) ? ' AND t.topic_id = p.topic_id ' : '' ;
break ;
case 'f' :
$sql_sort_table = FORUMS_TABLE . ' f, ' ;
$sql_sort_join = ' AND f.forum_id = p.forum_id ' ;
break ;
}
2006-03-06 18:21:54 +00:00
if ( ! sizeof ( $m_approve_fid_ary ))
{
$m_approve_fid_sql = ' AND p.post_approved = 1' ;
}
else if ( $m_approve_fid_ary == array ( - 1 ))
{
$m_approve_fid_sql = '' ;
}
else
{
2006-08-12 14:58:02 +00:00
$m_approve_fid_sql = ' AND (p.post_approved = 1 OR ' . $db -> sql_in_set ( 'p.forum_id' , $m_approve_fid_ary , true ) . ')' ;
2006-03-06 18:21:54 +00:00
}
2006-01-22 19:13:12 +00:00
// If the cache was completely empty count the results
$calc_results = ( $result_count ) ? '' : 'SQL_CALC_FOUND_ROWS ' ;
// Build the query for really selecting the post_ids
if ( $type == 'posts' )
{
$sql = " SELECT { $calc_results } p.post_id
2007-07-23 17:03:37 +00:00
FROM " . $sql_sort_table . POSTS_TABLE . ' p' . (( $firstpost_only ) ? ', ' . TOPICS_TABLE . ' t ' : ' ') . "
2006-01-22 19:13:12 +00:00
WHERE $sql_author
2006-03-06 18:21:54 +00:00
$sql_topic_id
2007-07-23 17:03:37 +00:00
$sql_firstpost
2006-03-06 18:21:54 +00:00
$m_approve_fid_sql
2006-01-22 19:13:12 +00:00
$sql_fora
$sql_sort_join
$sql_time
ORDER BY $sql_sort " ;
$field = 'post_id' ;
}
else
{
$sql = " SELECT { $calc_results } t.topic_id
FROM " . $sql_sort_table . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
WHERE $sql_author
2006-03-06 18:21:54 +00:00
$sql_topic_id
2007-07-23 17:03:37 +00:00
$sql_firstpost
2006-03-06 18:21:54 +00:00
$m_approve_fid_sql
2006-01-22 19:13:12 +00:00
$sql_fora
AND t . topic_id = p . topic_id
$sql_sort_join
$sql_time
GROUP BY t . topic_id
ORDER BY $sql_sort " ;
$field = 'topic_id' ;
}
// Only read one block of posts from the db and then cache it
$result = $db -> sql_query_limit ( $sql , $config [ 'search_block_size' ], $start );
while ( $row = $db -> sql_fetchrow ( $result ))
{
$id_ary [] = $row [ $field ];
}
$db -> sql_freeresult ( $result );
// retrieve the total result count if needed
if ( ! $result_count )
{
$sql = 'SELECT FOUND_ROWS() as result_count' ;
$result = $db -> sql_query ( $sql );
2006-03-22 17:30:20 +00:00
$result_count = ( int ) $db -> sql_fetchfield ( 'result_count' );
$db -> sql_freeresult ( $result );
2006-01-22 19:13:12 +00:00
2006-03-22 17:30:20 +00:00
if ( ! $result_count )
2005-01-13 22:30:42 +00:00
{
2006-01-22 19:13:12 +00:00
return false ;
2005-01-13 22:30:42 +00:00
}
2006-01-22 19:13:12 +00:00
}
2005-01-13 22:30:42 +00:00
2006-01-22 19:13:12 +00:00
if ( sizeof ( $id_ary ))
{
$this -> save_ids ( $search_key , '' , $author_ary , $result_count , $id_ary , $start , $sort_dir );
$id_ary = array_slice ( $id_ary , 0 , $per_page );
return $result_count ;
}
return false ;
}
/**
* Destroys cached search results , that contained one of the new words in a post so the results won ' t be outdated .
*
* @ param string $mode contains the post mode : edit , post , reply , quote ...
*/
2006-09-17 22:02:28 +00:00
function index ( $mode , $post_id , & $message , & $subject , $poster_id , $forum_id )
2006-01-22 19:13:12 +00:00
{
global $db ;
// Split old and new post/subject to obtain array of words
$split_text = $this -> split_message ( $message );
$split_title = ( $subject ) ? $this -> split_message ( $subject ) : array ();
2007-04-22 17:12:31 +00:00
$words = array_unique ( array_merge ( $split_text , $split_title ));
2006-01-22 19:13:12 +00:00
unset ( $split_text );
unset ( $split_title );
2005-01-13 22:30:42 +00:00
2006-01-22 19:13:12 +00:00
// destroy cached search results containing any of the words removed or added
$this -> destroy_cache ( $words , array ( $poster_id ));
unset ( $words );
}
/**
* Destroy cached results , that might be outdated after deleting a post
*/
2006-07-07 18:43:52 +00:00
function index_remove ( $post_ids , $author_ids , $forum_ids )
2006-01-22 19:13:12 +00:00
{
$this -> destroy_cache ( array (), $author_ids );
}
/**
* Destroy old cache entries
*/
function tidy ()
{
global $db , $config ;
// destroy too old cached search results
$this -> destroy_cache ( array ());
2006-05-18 18:18:32 +00:00
set_config ( 'search_last_gc' , time (), true );
2005-01-13 22:30:42 +00:00
}
2006-03-15 23:20:04 +00:00
/**
* Create fulltext index
*/
function create_index ( $acp_module , $u_action )
{
global $db ;
2006-03-26 01:36:26 +00:00
// Make sure we can actually use MySQL with fulltext indexes
if ( $error = $this -> init ())
2006-03-15 23:20:04 +00:00
{
2006-03-26 01:36:26 +00:00
return $error ;
2006-03-15 23:20:04 +00:00
}
2006-07-27 19:02:47 +00:00
if ( empty ( $this -> stats ))
2006-03-15 23:20:04 +00:00
{
$this -> get_stats ();
}
2006-12-16 20:24:34 +00:00
$alter = array ();
2006-03-15 23:20:04 +00:00
if ( ! isset ( $this -> stats [ 'post_subject' ]))
{
2006-12-23 18:49:38 +00:00
if ( $db -> sql_layer == 'mysqli' || version_compare ( $db -> mysql_version , '4.1.3' , '>=' ))
2006-12-16 20:24:34 +00:00
{
2007-05-25 12:22:04 +00:00
//$alter[] = 'MODIFY post_subject varchar(100) COLLATE utf8_unicode_ci DEFAULT \'\' NOT NULL';
2006-12-16 20:24:34 +00:00
}
2007-04-18 20:21:50 +00:00
else
{
2007-04-18 21:11:17 +00:00
$alter [] = 'MODIFY post_subject text NOT NULL' ;
2007-04-18 20:21:50 +00:00
}
2006-12-16 20:24:34 +00:00
$alter [] = 'ADD FULLTEXT (post_subject)' ;
2006-03-15 23:20:04 +00:00
}
if ( ! isset ( $this -> stats [ 'post_text' ]))
{
2006-12-23 18:49:38 +00:00
if ( $db -> sql_layer == 'mysqli' || version_compare ( $db -> mysql_version , '4.1.3' , '>=' ))
2006-12-16 20:24:34 +00:00
{
$alter [] = 'MODIFY post_text mediumtext COLLATE utf8_unicode_ci NOT NULL' ;
}
2007-04-18 20:21:50 +00:00
else
{
2007-04-18 21:11:17 +00:00
$alter [] = 'MODIFY post_text mediumtext NOT NULL' ;
2007-04-18 20:21:50 +00:00
}
2006-12-16 20:24:34 +00:00
$alter [] = 'ADD FULLTEXT (post_text)' ;
}
2007-04-22 17:12:31 +00:00
if ( ! isset ( $this -> stats [ 'post_content' ]))
{
$alter [] = 'ADD FULLTEXT post_content (post_subject, post_text)' ;
}
2006-12-16 20:24:34 +00:00
if ( sizeof ( $alter ))
{
$db -> sql_query ( 'ALTER TABLE ' . POSTS_TABLE . ' ' . implode ( ', ' , $alter ));
2006-03-15 23:20:04 +00:00
}
2006-06-07 19:32:23 +00:00
$db -> sql_query ( 'TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE );
2006-05-26 15:04:27 +00:00
return false ;
2006-03-15 23:20:04 +00:00
}
/**
* Drop fulltext index
*/
function delete_index ( $acp_module , $u_action )
{
global $db ;
2006-03-26 01:36:26 +00:00
// Make sure we can actually use MySQL with fulltext indexes
if ( $error = $this -> init ())
2006-03-15 23:20:04 +00:00
{
2006-03-26 01:36:26 +00:00
return $error ;
2006-03-15 23:20:04 +00:00
}
2006-07-27 19:02:47 +00:00
if ( empty ( $this -> stats ))
2006-03-15 23:20:04 +00:00
{
$this -> get_stats ();
}
2006-12-16 20:24:34 +00:00
$alter = array ();
2006-03-15 23:20:04 +00:00
if ( isset ( $this -> stats [ 'post_subject' ]))
{
2006-12-16 20:24:34 +00:00
$alter [] = 'DROP INDEX post_subject' ;
2006-03-15 23:20:04 +00:00
}
if ( isset ( $this -> stats [ 'post_text' ]))
{
2006-12-16 20:24:34 +00:00
$alter [] = 'DROP INDEX post_text' ;
}
2007-04-22 17:12:31 +00:00
if ( isset ( $this -> stats [ 'post_content' ]))
{
$alter [] = 'DROP INDEX post_content' ;
}
2006-12-16 20:24:34 +00:00
if ( sizeof ( $alter ))
{
$db -> sql_query ( 'ALTER TABLE ' . POSTS_TABLE . ' ' . implode ( ', ' , $alter ));
2006-03-15 23:20:04 +00:00
}
2006-06-07 19:32:23 +00:00
$db -> sql_query ( 'TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE );
2006-05-26 15:04:27 +00:00
return false ;
2006-03-15 23:20:04 +00:00
}
/**
* Returns true if both FULLTEXT indexes exist
*/
function index_created ()
{
2006-07-27 19:02:47 +00:00
if ( empty ( $this -> stats ))
2006-03-15 23:20:04 +00:00
{
$this -> get_stats ();
}
2007-04-22 17:12:31 +00:00
return ( isset ( $this -> stats [ 'post_text' ]) && isset ( $this -> stats [ 'post_subject' ]) && isset ( $this -> stats [ 'post_content' ])) ? true : false ;
2006-03-15 23:20:04 +00:00
}
/**
* Returns an associative array containing information about the indexes
*/
function index_stats ()
{
global $user ;
2006-07-27 19:02:47 +00:00
if ( empty ( $this -> stats ))
2006-03-15 23:20:04 +00:00
{
$this -> get_stats ();
}
return array (
$user -> lang [ 'FULLTEXT_MYSQL_TOTAL_POSTS' ] => ( $this -> index_created ()) ? $this -> stats [ 'total_posts' ] : 0 ,
2006-06-16 16:54:51 +00:00
);
2006-03-15 23:20:04 +00:00
}
function get_stats ()
{
global $db ;
2006-10-14 14:56:46 +00:00
if ( strpos ( $db -> sql_layer , 'mysql' ) === false )
2006-03-26 01:36:26 +00:00
{
$this -> stats = array ();
return ;
}
2006-03-15 23:20:04 +00:00
$sql = ' SHOW INDEX
FROM ' . POSTS_TABLE ;
$result = $db -> sql_query ( $sql );
while ( $row = $db -> sql_fetchrow ( $result ))
{
2006-04-23 13:10:34 +00:00
// deal with older MySQL versions which didn't use Index_type
$index_type = ( isset ( $row [ 'Index_type' ])) ? $row [ 'Index_type' ] : $row [ 'Comment' ];
if ( $index_type == 'FULLTEXT' )
2006-03-15 23:20:04 +00:00
{
2007-04-28 21:16:32 +00:00
if ( $row [ 'Key_name' ] == 'post_text' )
2006-03-15 23:20:04 +00:00
{
$this -> stats [ 'post_text' ] = $row ;
}
2007-04-28 21:16:32 +00:00
else if ( $row [ 'Key_name' ] == 'post_subject' )
2006-03-15 23:20:04 +00:00
{
$this -> stats [ 'post_subject' ] = $row ;
}
2007-04-28 21:16:32 +00:00
else if ( $row [ 'Key_name' ] == 'post_content' )
2007-04-22 17:12:31 +00:00
{
$this -> stats [ 'post_content' ] = $row ;
}
2006-03-15 23:20:04 +00:00
}
}
$db -> sql_freeresult ( $result );
2006-03-22 17:30:20 +00:00
$sql = ' SELECT COUNT ( post_id ) as total_posts
2006-03-15 23:20:04 +00:00
FROM ' . POSTS_TABLE ;
$result = $db -> sql_query ( $sql );
2006-03-22 17:30:20 +00:00
$this -> stats [ 'total_posts' ] = ( int ) $db -> sql_fetchfield ( 'total_posts' );
2006-03-15 23:20:04 +00:00
$db -> sql_freeresult ( $result );
}
2007-01-27 17:38:45 +00:00
/**
* Display a note , that UTF - 8 support is not available with certain versions of PHP
*/
function acp ()
{
global $user , $config ;
$tpl = '
< dl >
2007-03-31 14:36:01 +00:00
< dt >< label > ' . $user->lang[' FULLTEXT_MYSQL_PCRE '] . ' </ label >< br />< span > ' . $user->lang[' FULLTEXT_MYSQL_PCRE_EXPLAIN '] . ' </ span ></ dt >
2007-01-27 17:38:45 +00:00
< dd > ' . (($this->pcre_properties) ? $user->lang[' YES '] : $user->lang[' NO ']) . ' ( PHP ' . PHP_VERSION . ' ) </ dd >
</ dl >
2007-03-31 14:36:01 +00:00
< dl >
< dt >< label > ' . $user->lang[' FULLTEXT_MYSQL_MBSTRING '] . ' </ label >< br />< span > ' . $user->lang[' FULLTEXT_MYSQL_MBSTRING_EXPLAIN '] . ' </ span ></ dt >
< dd > ' . (($this->mbstring_regex) ? $user->lang[' YES '] : $user->lang[' NO ']). ' </ dd >
</ dl >
2007-01-27 17:38:45 +00:00
' ;
// These are fields required in the config table
return array (
'tpl' => $tpl ,
'config' => array ()
);
}
2005-01-13 22:30:42 +00:00
}
?>