mirror of
https://github.com/phpbb/phpbb.git
synced 2025-06-07 20:08:53 +00:00
Merge pull request #5440 from rubencm/ticket/15540
[ticket/15540] Refactor search backend classes to Symfony services
This commit is contained in:
commit
c6a202bdb3
44 changed files with 2065 additions and 2125 deletions
|
@ -24,6 +24,7 @@ imports:
|
|||
- { resource: services_profilefield.yml }
|
||||
- { resource: services_report.yml }
|
||||
- { resource: services_routing.yml }
|
||||
- { resource: services_search.yml }
|
||||
- { resource: services_storage.yml }
|
||||
- { resource: services_text_formatter.yml }
|
||||
- { resource: services_text_reparser.yml }
|
||||
|
|
72
phpBB/config/default/container/services_search.yml
Normal file
72
phpBB/config/default/container/services_search.yml
Normal file
|
@ -0,0 +1,72 @@
|
|||
services:
|
||||
|
||||
# Search backends
|
||||
search.fulltext.mysql:
|
||||
class: phpbb\search\backend\fulltext_mysql
|
||||
arguments:
|
||||
- '@config'
|
||||
- '@dbal.conn'
|
||||
- '@dispatcher'
|
||||
- '@language'
|
||||
- '@user'
|
||||
- '%core.root_path%'
|
||||
- '%core.php_ext%'
|
||||
tags:
|
||||
- { name: search.backend }
|
||||
|
||||
search.fulltext.native:
|
||||
class: phpbb\search\backend\fulltext_native
|
||||
arguments:
|
||||
- '@config'
|
||||
- '@dbal.conn'
|
||||
- '@dispatcher'
|
||||
- '@language'
|
||||
- '@user'
|
||||
- '%core.root_path%'
|
||||
- '%core.php_ext%'
|
||||
tags:
|
||||
- { name: search.backend }
|
||||
|
||||
search.fulltext.postgres:
|
||||
class: phpbb\search\backend\fulltext_postgres
|
||||
arguments:
|
||||
- '@config'
|
||||
- '@dbal.conn'
|
||||
- '@dispatcher'
|
||||
- '@language'
|
||||
- '@user'
|
||||
- '%core.root_path%'
|
||||
- '%core.php_ext%'
|
||||
tags:
|
||||
- { name: search.backend }
|
||||
|
||||
search.fulltext.sphinx:
|
||||
class: phpbb\search\backend\fulltext_sphinx
|
||||
arguments:
|
||||
- '@auth'
|
||||
- '@config'
|
||||
- '@dbal.conn'
|
||||
- '@dbal.tools'
|
||||
- '@dispatcher'
|
||||
- '@language'
|
||||
- '@log'
|
||||
- '@user'
|
||||
- '%core.root_path%'
|
||||
- '%core.php_ext%'
|
||||
tags:
|
||||
- { name: search.backend }
|
||||
|
||||
# Factory
|
||||
search.backend_factory:
|
||||
class: phpbb\search\search_backend_factory
|
||||
arguments:
|
||||
- '@config'
|
||||
- '@search.backend_collection'
|
||||
|
||||
# Collections
|
||||
search.backend_collection:
|
||||
class: phpbb\di\service_collection
|
||||
arguments:
|
||||
- '@service_container'
|
||||
tags:
|
||||
- { name: service_collection, tag: search.backend, class_name_aware: true }
|
|
@ -1,140 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
if (php_sapi_name() != 'cli')
|
||||
{
|
||||
die("This program must be run from the command line.\n");
|
||||
}
|
||||
|
||||
if ($argc < 2)
|
||||
{
|
||||
echo 'Usage: php ' . basename(__FILE__) . " index_type [batch_size]\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$class_name = basename($argv[1]);
|
||||
|
||||
define('IN_PHPBB', true);
|
||||
$phpbb_root_path = (defined('PHPBB_ROOT_PATH')) ? PHPBB_ROOT_PATH : '../';
|
||||
$phpEx = substr(strrchr(__FILE__, '.'), 1);
|
||||
require($phpbb_root_path . 'common.' . $phpEx);
|
||||
require($phpbb_root_path . 'includes/acp/acp_search.' . $phpEx);
|
||||
|
||||
$user->session_begin();
|
||||
$auth->acl($user->data);
|
||||
$user->setup('acp/search');
|
||||
|
||||
$search_name = ucfirst(strtolower(str_replace('_', ' ', $class_name)));
|
||||
$search_errors = array();
|
||||
$search = new $class_name($search_errors);
|
||||
|
||||
$batch_size = isset($argv[2]) ? $argv[2] : 2000;
|
||||
|
||||
if (method_exists($search, 'create_index'))
|
||||
{
|
||||
if ($error = $search->create_index(null, ''))
|
||||
{
|
||||
var_dump($error);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql = 'SELECT forum_id, enable_indexing
|
||||
FROM ' . FORUMS_TABLE;
|
||||
$result = $db->sql_query($sql);
|
||||
|
||||
while ($row = $db->sql_fetchrow($result))
|
||||
{
|
||||
$forums[$row['forum_id']] = (bool) $row['enable_indexing'];
|
||||
}
|
||||
$db->sql_freeresult($result);
|
||||
|
||||
$sql = 'SELECT post_id
|
||||
FROM ' . POSTS_TABLE . '
|
||||
ORDER BY post_id DESC';
|
||||
$result = $db->sql_query_limit($sql, 1);
|
||||
$max_post_id = (int) $db->sql_fetchfield('post_id');
|
||||
|
||||
$post_counter = 0;
|
||||
while ($post_counter <= $max_post_id)
|
||||
{
|
||||
$row_count = 0;
|
||||
$time = time();
|
||||
|
||||
printf("Processing posts with %d <= post_id <= %d\n",
|
||||
$post_counter + 1,
|
||||
$post_counter + $batch_size
|
||||
);
|
||||
|
||||
$sql = 'SELECT post_id, post_subject, post_text, poster_id, forum_id
|
||||
FROM ' . POSTS_TABLE . '
|
||||
WHERE post_id >= ' . (int) ($post_counter + 1) . '
|
||||
AND post_id <= ' . (int) ($post_counter + $batch_size);
|
||||
$result = $db->sql_query($sql);
|
||||
|
||||
$buffer = $db->sql_buffer_nested_transactions();
|
||||
|
||||
if ($buffer)
|
||||
{
|
||||
$rows = $db->sql_fetchrowset($result);
|
||||
$rows[] = false; // indicate end of array for while loop below
|
||||
|
||||
$db->sql_freeresult($result);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
while ($row = ($buffer ? $rows[$i++] : $db->sql_fetchrow($result)))
|
||||
{
|
||||
// Indexing enabled for this forum or global announcement?
|
||||
// Global announcements get indexed by default.
|
||||
if (!$row['forum_id'] || !empty($forums[$row['forum_id']]))
|
||||
{
|
||||
++$row_count;
|
||||
|
||||
$search->index('post',
|
||||
$row['post_id'],
|
||||
$row['post_text'],
|
||||
$row['post_subject'],
|
||||
$row['poster_id'],
|
||||
$row['forum_id']
|
||||
);
|
||||
|
||||
if ($row_count % 10 == 0)
|
||||
{
|
||||
echo '.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$delta = (time() - $time);
|
||||
$delta = $delta <= 0 ? 1 : $delta;
|
||||
printf(" %d posts/sec\n", $row_count / $delta);
|
||||
|
||||
if (!$buffer)
|
||||
{
|
||||
$db->sql_freeresult($result);
|
||||
}
|
||||
|
||||
$post_counter += $batch_size;
|
||||
}
|
||||
}
|
||||
|
||||
$search->tidy();
|
||||
|
||||
$phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_SEARCH_INDEX_CREATED', false, array($search_name));
|
||||
|
||||
echo $user->lang['SEARCH_INDEX_CREATED'] . "\n";
|
||||
echo 'Peak Memory Usage: ' . get_formatted_filesize(memory_get_peak_usage()) . "\n";
|
||||
|
||||
exit(0);
|
|
@ -1,127 +0,0 @@
|
|||
<?php
|
||||
|
||||
//
|
||||
// Security message:
|
||||
//
|
||||
// This script is potentially dangerous.
|
||||
// Remove or comment the next line (die(".... ) to enable this script.
|
||||
// Do NOT FORGET to either remove this script or disable it after you have used it.
|
||||
//
|
||||
|
||||
//
|
||||
// Security message:
|
||||
//
|
||||
// This script is potentially dangerous.
|
||||
// Remove or comment the next line (die(".... ) to enable this script.
|
||||
// Do NOT FORGET to either remove this script or disable it after you have used it.
|
||||
//
|
||||
die("Please read the first lines of this script for instructions on how to enable it");
|
||||
|
||||
//
|
||||
// Do not change anything below this line.
|
||||
//
|
||||
set_time_limit(0);
|
||||
|
||||
define('IN_PHPBB', true);
|
||||
$phpbb_root_path = '../';
|
||||
$phpEx = substr(strrchr(__FILE__, '.'), 1);
|
||||
include($phpbb_root_path . 'common.'.$phpEx);
|
||||
|
||||
// Start session management
|
||||
$user->session_begin();
|
||||
$auth->acl($user->data);
|
||||
$user->setup();
|
||||
|
||||
$search_type = $config['search_type'];
|
||||
|
||||
if (!class_exists($search_type))
|
||||
{
|
||||
trigger_error('NO_SUCH_SEARCH_MODULE');
|
||||
}
|
||||
|
||||
$error = false;
|
||||
$search = new $search_type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher);
|
||||
|
||||
if ($error)
|
||||
{
|
||||
trigger_error($error);
|
||||
}
|
||||
|
||||
print "<html>\n<body>\n";
|
||||
|
||||
//
|
||||
// Fetch a batch of posts_text entries
|
||||
//
|
||||
$sql = "SELECT COUNT(*) as total, MAX(post_id) as max_post_id
|
||||
FROM ". POSTS_TABLE;
|
||||
if ( !($result = $db->sql_query($sql)) )
|
||||
{
|
||||
$error = $db->sql_error();
|
||||
die("Couldn't get maximum post ID :: " . $sql . " :: " . $error['message']);
|
||||
}
|
||||
|
||||
$max_post_id = $db->sql_fetchrow($result);
|
||||
|
||||
$totalposts = $max_post_id['total'];
|
||||
$max_post_id = $max_post_id['max_post_id'];
|
||||
|
||||
$postcounter = (!isset($HTTP_GET_VARS['batchstart'])) ? 0 : $HTTP_GET_VARS['batchstart'];
|
||||
|
||||
$batchsize = 200; // Process this many posts per loop
|
||||
$batchcount = 0;
|
||||
for(;$postcounter <= $max_post_id; $postcounter += $batchsize)
|
||||
{
|
||||
$batchstart = $postcounter + 1;
|
||||
$batchend = $postcounter + $batchsize;
|
||||
$batchcount++;
|
||||
|
||||
$sql = "SELECT *
|
||||
FROM " . POSTS_TABLE . "
|
||||
WHERE post_id
|
||||
BETWEEN $batchstart
|
||||
AND $batchend";
|
||||
if( !($result = $db->sql_query($sql)) )
|
||||
{
|
||||
$error = $db->sql_error();
|
||||
die("Couldn't get post_text :: " . $sql . " :: " . $error['message']);
|
||||
}
|
||||
|
||||
$rowset = $db->sql_fetchrowset($result);
|
||||
$db->sql_freeresult($result);
|
||||
|
||||
$post_rows = count($rowset);
|
||||
|
||||
if( $post_rows )
|
||||
{
|
||||
|
||||
// $sql = "LOCK TABLES ".POST_TEXT_TABLE." WRITE";
|
||||
// $result = $db->sql_query($sql);
|
||||
print "\n<p>\n<a href='{$_SERVER['PHP_SELF']}?batchstart=$batchstart'>Restart from posting $batchstart</a><br>\n";
|
||||
|
||||
// For every post in the batch:
|
||||
for($post_nr = 0; $post_nr < $post_rows; $post_nr++ )
|
||||
{
|
||||
print ".";
|
||||
flush();
|
||||
|
||||
$post_id = $rowset[$post_nr]['post_id'];
|
||||
|
||||
$search->index('post', $rowset[$post_nr]['post_id'], $rowset[$post_nr]['post_text'], $rowset[$post_nr]['post_subject'], $rowset[$post_nr]['poster_id']);
|
||||
}
|
||||
// $sql = "UNLOCK TABLES";
|
||||
// $result = $db->sql_query($sql);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
print "<br>Removing common words (words that appear in more than 50% of the posts)<br>\n";
|
||||
flush();
|
||||
$search->tidy();
|
||||
print "Removed words that where too common.<br>";
|
||||
|
||||
echo "<br>Done";
|
||||
|
||||
?>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -646,11 +646,24 @@ class acp_main
|
|||
}
|
||||
|
||||
// Warn if no search index is created
|
||||
if ($config['num_posts'] && class_exists($config['search_type']))
|
||||
if ($config['num_posts'])
|
||||
{
|
||||
$error = false;
|
||||
$search_type = $config['search_type'];
|
||||
$search = new $search_type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher);
|
||||
try
|
||||
{
|
||||
$search_backend_factory = $phpbb_container->get('search.backend_factory');
|
||||
$search = $search_backend_factory->get_active();
|
||||
}
|
||||
catch (RuntimeException $e)
|
||||
{
|
||||
if (strpos($e->getMessage(), 'No service found') === 0)
|
||||
{
|
||||
trigger_error('NO_SUCH_SEARCH_MODULE');
|
||||
}
|
||||
else
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$search->index_created())
|
||||
{
|
||||
|
|
|
@ -24,8 +24,10 @@ class acp_search
|
|||
var $u_action;
|
||||
var $state;
|
||||
var $search;
|
||||
var $max_post_id;
|
||||
var $batch_size = 100;
|
||||
|
||||
protected const STATE_SEARCH_TYPE = 0;
|
||||
protected const STATE_ACTION = 1;
|
||||
protected const STATE_POST_COUNTER = 2;
|
||||
|
||||
function main($id, $mode)
|
||||
{
|
||||
|
@ -52,6 +54,7 @@ class acp_search
|
|||
{
|
||||
global $user, $template, $phpbb_log, $request;
|
||||
global $config, $phpbb_admin_path, $phpEx;
|
||||
global $phpbb_container;
|
||||
|
||||
$submit = $request->is_set_post('submit');
|
||||
|
||||
|
@ -60,7 +63,7 @@ class acp_search
|
|||
trigger_error($user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING);
|
||||
}
|
||||
|
||||
$search_types = $this->get_search_types();
|
||||
$search_types = $phpbb_container->get('search.backend_collection');
|
||||
|
||||
$settings = [
|
||||
'search_interval' => 'float',
|
||||
|
@ -74,41 +77,41 @@ class acp_search
|
|||
];
|
||||
|
||||
$search = null;
|
||||
$error = false;
|
||||
$search_options = '';
|
||||
foreach ($search_types as $type)
|
||||
|
||||
foreach ($search_types as $search)
|
||||
{
|
||||
if ($this->init_search($type, $search, $error))
|
||||
// Only show available search backends
|
||||
if ($search->is_available())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$name = $search->get_name();
|
||||
|
||||
$name = $search->get_name();
|
||||
$type = get_class($search);
|
||||
|
||||
$selected = ($config['search_type'] == $type) ? ' selected="selected"' : '';
|
||||
$identifier = substr($type, strrpos($type, '\\') + 1);
|
||||
$search_options .= "<option value=\"$type\"$selected data-toggle-setting=\"#search_{$identifier}_settings\">$name</option>";
|
||||
$selected = ($config['search_type'] == $type) ? ' selected="selected"' : '';
|
||||
$identifier = substr($type, strrpos($type, '\\') + 1);
|
||||
$search_options .= "<option value=\"$type\"$selected data-toggle-setting=\"#search_{$identifier}_settings\">$name</option>";
|
||||
|
||||
if (method_exists($search, 'acp'))
|
||||
{
|
||||
$vars = $search->acp();
|
||||
|
||||
if (!$submit)
|
||||
if (method_exists($search, 'acp'))
|
||||
{
|
||||
$template->assign_block_vars('backend', array(
|
||||
'NAME' => $name,
|
||||
'SETTINGS' => $vars['tpl'],
|
||||
'IDENTIFIER' => $identifier,
|
||||
));
|
||||
}
|
||||
else if (is_array($vars['config']))
|
||||
{
|
||||
$settings = array_merge($settings, $vars['config']);
|
||||
$vars = $search->acp();
|
||||
|
||||
if (!$submit)
|
||||
{
|
||||
$template->assign_block_vars('backend', array(
|
||||
'NAME' => $name,
|
||||
'SETTINGS' => $vars['tpl'],
|
||||
'IDENTIFIER' => $identifier,
|
||||
));
|
||||
}
|
||||
else if (is_array($vars['config']))
|
||||
{
|
||||
$settings = array_merge($settings, $vars['config']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($search);
|
||||
unset($error);
|
||||
|
||||
$cfg_array = (isset($_REQUEST['config'])) ? $request->variable('config', array('' => ''), true) : array();
|
||||
$updated = $request->variable('updated', false);
|
||||
|
@ -152,66 +155,50 @@ class acp_search
|
|||
$phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_CONFIG_SEARCH');
|
||||
}
|
||||
|
||||
if (isset($cfg_array['search_type']) && in_array($cfg_array['search_type'], $search_types, true) && ($cfg_array['search_type'] != $config['search_type']))
|
||||
if (isset($cfg_array['search_type']) && ($cfg_array['search_type'] != $config['search_type']))
|
||||
{
|
||||
$search = null;
|
||||
$error = false;
|
||||
|
||||
if (!$this->init_search($cfg_array['search_type'], $search, $error))
|
||||
$search_backend_factory = $phpbb_container->get('search.backend_factory');
|
||||
$search = $search_backend_factory->get($cfg_array['search_type']);
|
||||
if (confirm_box(true))
|
||||
{
|
||||
if (confirm_box(true))
|
||||
// Initialize search backend, if $error is false means that everything is ok
|
||||
if (!($error = $search->init()))
|
||||
{
|
||||
if (!method_exists($search, 'init') || !($error = $search->init()))
|
||||
{
|
||||
$config->set('search_type', $cfg_array['search_type']);
|
||||
$config->set('search_type', $cfg_array['search_type']);
|
||||
|
||||
if (!$updated)
|
||||
{
|
||||
$phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_CONFIG_SEARCH');
|
||||
}
|
||||
$extra_message = '<br />' . $user->lang['SWITCHED_SEARCH_BACKEND'] . '<br /><a href="' . append_sid("{$phpbb_admin_path}index.$phpEx", 'i=search&mode=index') . '">» ' . $user->lang['GO_TO_SEARCH_INDEX'] . '</a>';
|
||||
}
|
||||
else
|
||||
if (!$updated)
|
||||
{
|
||||
trigger_error($error . adm_back_link($this->u_action), E_USER_WARNING);
|
||||
$phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_CONFIG_SEARCH');
|
||||
}
|
||||
$extra_message = '<br />' . $user->lang['SWITCHED_SEARCH_BACKEND'] . '<br /><a href="' . append_sid("{$phpbb_admin_path}index.$phpEx", 'i=search&mode=index') . '">» ' . $user->lang['GO_TO_SEARCH_INDEX'] . '</a>';
|
||||
}
|
||||
else
|
||||
{
|
||||
confirm_box(false, $user->lang['CONFIRM_SEARCH_BACKEND'], build_hidden_fields(array(
|
||||
'i' => $id,
|
||||
'mode' => $mode,
|
||||
'submit' => true,
|
||||
'updated' => $updated,
|
||||
'config' => array('search_type' => $cfg_array['search_type']),
|
||||
)));
|
||||
trigger_error($error . adm_back_link($this->u_action), E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
trigger_error($error . adm_back_link($this->u_action), E_USER_WARNING);
|
||||
confirm_box(false, $user->lang['CONFIRM_SEARCH_BACKEND'], build_hidden_fields(array(
|
||||
'i' => $id,
|
||||
'mode' => $mode,
|
||||
'submit' => true,
|
||||
'updated' => $updated,
|
||||
'config' => array('search_type' => $cfg_array['search_type']),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
$search = null;
|
||||
$error = false;
|
||||
if (!$this->init_search($config['search_type'], $search, $error))
|
||||
if ($updated)
|
||||
{
|
||||
if ($updated)
|
||||
if (method_exists($search, 'config_updated'))
|
||||
{
|
||||
if (method_exists($search, 'config_updated'))
|
||||
if ($search->config_updated())
|
||||
{
|
||||
if ($search->config_updated())
|
||||
{
|
||||
trigger_error($error . adm_back_link($this->u_action), E_USER_WARNING);
|
||||
}
|
||||
trigger_error($error . adm_back_link($this->u_action), E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
trigger_error($error . adm_back_link($this->u_action), E_USER_WARNING);
|
||||
}
|
||||
|
||||
trigger_error($user->lang['CONFIG_UPDATED'] . $extra_message . adm_back_link($this->u_action));
|
||||
}
|
||||
|
@ -239,8 +226,8 @@ class acp_search
|
|||
|
||||
function index($id, $mode)
|
||||
{
|
||||
global $db, $user, $template, $phpbb_log, $request;
|
||||
global $config, $phpbb_admin_path, $phpEx;
|
||||
global $user, $template, $phpbb_log, $request;
|
||||
global $config, $phpbb_admin_path, $phpEx, $phpbb_container;
|
||||
|
||||
$action = $request->variable('action', '');
|
||||
$this->state = explode(',', $config['search_indexing_state']);
|
||||
|
@ -268,96 +255,57 @@ class acp_search
|
|||
break;
|
||||
|
||||
case 'delete':
|
||||
$this->state[1] = 'delete';
|
||||
$this->state[self::STATE_ACTION] = 'delete';
|
||||
break;
|
||||
|
||||
case 'create':
|
||||
$this->state[1] = 'create';
|
||||
$this->state[self::STATE_ACTION] = 'create';
|
||||
break;
|
||||
|
||||
default:
|
||||
trigger_error('NO_ACTION', E_USER_ERROR);
|
||||
break;
|
||||
}
|
||||
|
||||
if (empty($this->state[0]))
|
||||
if (empty($this->state[self::STATE_SEARCH_TYPE]))
|
||||
{
|
||||
$this->state[0] = $request->variable('search_type', '');
|
||||
$this->state[self::STATE_SEARCH_TYPE] = $request->variable('search_type', '');
|
||||
}
|
||||
|
||||
$this->search = null;
|
||||
$error = false;
|
||||
if ($this->init_search($this->state[0], $this->search, $error))
|
||||
{
|
||||
trigger_error($error . adm_back_link($this->u_action), E_USER_WARNING);
|
||||
}
|
||||
$search_backend_factory = $phpbb_container->get('search.backend_factory');
|
||||
$this->search = $search_backend_factory->get($this->state[self::STATE_SEARCH_TYPE]);
|
||||
|
||||
$name = $this->search->get_name();
|
||||
|
||||
$action = &$this->state[1];
|
||||
|
||||
$this->max_post_id = $this->get_max_post_id();
|
||||
|
||||
$post_counter = (isset($this->state[2])) ? $this->state[2] : 0;
|
||||
$this->state[2] = &$post_counter;
|
||||
$this->save_state();
|
||||
|
||||
switch ($action)
|
||||
{
|
||||
case 'delete':
|
||||
if (method_exists($this->search, 'delete_index'))
|
||||
try
|
||||
{
|
||||
// pass a reference to myself so the $search object can make use of save_state() and attributes
|
||||
if ($error = $this->search->delete_index($this, append_sid("{$phpbb_admin_path}index.$phpEx", "i=$id&mode=$mode&action=delete&hash=" . generate_link_hash('acp_search'), false)))
|
||||
$this->state[self::STATE_POST_COUNTER] = $this->state[self::STATE_POST_COUNTER] ?? 0;
|
||||
if ($status = $this->search->delete_index($this->state[self::STATE_POST_COUNTER])) // Status is not null, so deleting is in progress....
|
||||
{
|
||||
$this->state = array('');
|
||||
// save the current state
|
||||
$this->save_state();
|
||||
trigger_error($error . adm_back_link($this->u_action) . $this->close_popup_js(), E_USER_WARNING);
|
||||
|
||||
$u_action = append_sid("{$phpbb_admin_path}index.$phpEx", "i=$id&mode=$mode&action=delete&hash=" . generate_link_hash('acp_search'), false);
|
||||
meta_refresh(1, $u_action);
|
||||
trigger_error($user->lang('SEARCH_INDEX_DELETE_REDIRECT', (int) $status['row_count'], $status['post_counter']) . $user->lang('SEARCH_INDEX_DELETE_REDIRECT_RATE', $status['rows_per_second']));
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception $e)
|
||||
{
|
||||
$starttime = microtime(true);
|
||||
$row_count = 0;
|
||||
while (still_on_time() && $post_counter <= $this->max_post_id)
|
||||
{
|
||||
$sql = 'SELECT post_id, poster_id, forum_id
|
||||
FROM ' . POSTS_TABLE . '
|
||||
WHERE post_id >= ' . (int) ($post_counter + 1) . '
|
||||
AND post_id <= ' . (int) ($post_counter + $this->batch_size);
|
||||
$result = $db->sql_query($sql);
|
||||
|
||||
$ids = $posters = $forum_ids = array();
|
||||
while ($row = $db->sql_fetchrow($result))
|
||||
{
|
||||
$ids[] = $row['post_id'];
|
||||
$posters[] = $row['poster_id'];
|
||||
$forum_ids[] = $row['forum_id'];
|
||||
}
|
||||
$db->sql_freeresult($result);
|
||||
$row_count += count($ids);
|
||||
|
||||
if (count($ids))
|
||||
{
|
||||
$this->search->index_remove($ids, $posters, $forum_ids);
|
||||
}
|
||||
|
||||
$post_counter += $this->batch_size;
|
||||
}
|
||||
// save the current state
|
||||
$this->state = [];
|
||||
$this->save_state();
|
||||
|
||||
if ($post_counter <= $this->max_post_id)
|
||||
{
|
||||
$totaltime = microtime(true) - $starttime;
|
||||
$rows_per_second = $row_count / $totaltime;
|
||||
meta_refresh(1, append_sid($this->u_action . '&action=delete&skip_rows=' . $post_counter . '&hash=' . generate_link_hash('acp_search')));
|
||||
trigger_error($user->lang('SEARCH_INDEX_DELETE_REDIRECT', (int) $row_count, $post_counter) . $user->lang('SEARCH_INDEX_DELETE_REDIRECT_RATE', $rows_per_second));
|
||||
}
|
||||
trigger_error($e->getMessage() . adm_back_link($this->u_action) . $this->close_popup_js(), E_USER_WARNING);
|
||||
}
|
||||
|
||||
$this->search->tidy();
|
||||
|
||||
$this->state = array('');
|
||||
$this->state = [];
|
||||
$this->save_state();
|
||||
|
||||
$phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_SEARCH_INDEX_REMOVED', false, array($name));
|
||||
|
@ -365,87 +313,32 @@ class acp_search
|
|||
break;
|
||||
|
||||
case 'create':
|
||||
if (method_exists($this->search, 'create_index'))
|
||||
try
|
||||
{
|
||||
// pass a reference to acp_search so the $search object can make use of save_state() and attributes
|
||||
if ($error = $this->search->create_index($this, append_sid("{$phpbb_admin_path}index.$phpEx", "i=$id&mode=$mode&action=create", false)))
|
||||
$this->state[self::STATE_POST_COUNTER] = $this->state[self::STATE_POST_COUNTER] ?? 0;
|
||||
if ($status = $this->search->create_index($this->state[self::STATE_POST_COUNTER])) // Status is not null, so indexing is in progress....
|
||||
{
|
||||
$this->state = array('');
|
||||
// save the current state
|
||||
$this->save_state();
|
||||
trigger_error($error . adm_back_link($this->u_action) . $this->close_popup_js(), E_USER_WARNING);
|
||||
|
||||
$u_action = append_sid("{$phpbb_admin_path}index.$phpEx", "i=$id&mode=$mode&action=create&hash=" . generate_link_hash('acp_search'), false);
|
||||
meta_refresh(1, $u_action);
|
||||
trigger_error($user->lang('SEARCH_INDEX_CREATE_REDIRECT', (int) $status['row_count'], $status['post_counter']) . $user->lang('SEARCH_INDEX_CREATE_REDIRECT_RATE', $status['rows_per_second']));
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception $e)
|
||||
{
|
||||
$sql = 'SELECT forum_id, enable_indexing
|
||||
FROM ' . FORUMS_TABLE;
|
||||
$result = $db->sql_query($sql, 3600);
|
||||
|
||||
while ($row = $db->sql_fetchrow($result))
|
||||
{
|
||||
$forums[$row['forum_id']] = (bool) $row['enable_indexing'];
|
||||
}
|
||||
$db->sql_freeresult($result);
|
||||
|
||||
$starttime = microtime(true);
|
||||
$row_count = 0;
|
||||
while (still_on_time() && $post_counter <= $this->max_post_id)
|
||||
{
|
||||
$sql = 'SELECT post_id, post_subject, post_text, poster_id, forum_id
|
||||
FROM ' . POSTS_TABLE . '
|
||||
WHERE post_id >= ' . (int) ($post_counter + 1) . '
|
||||
AND post_id <= ' . (int) ($post_counter + $this->batch_size);
|
||||
$result = $db->sql_query($sql);
|
||||
|
||||
$buffer = $db->sql_buffer_nested_transactions();
|
||||
|
||||
if ($buffer)
|
||||
{
|
||||
$rows = $db->sql_fetchrowset($result);
|
||||
$rows[] = false; // indicate end of array for while loop below
|
||||
|
||||
$db->sql_freeresult($result);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
while ($row = ($buffer ? $rows[$i++] : $db->sql_fetchrow($result)))
|
||||
{
|
||||
// Indexing enabled for this forum
|
||||
if (isset($forums[$row['forum_id']]) && $forums[$row['forum_id']])
|
||||
{
|
||||
$this->search->index('post', $row['post_id'], $row['post_text'], $row['post_subject'], $row['poster_id'], $row['forum_id']);
|
||||
}
|
||||
$row_count++;
|
||||
}
|
||||
if (!$buffer)
|
||||
{
|
||||
$db->sql_freeresult($result);
|
||||
}
|
||||
|
||||
$post_counter += $this->batch_size;
|
||||
}
|
||||
// save the current state
|
||||
// Error executing create_index
|
||||
$this->state = [];
|
||||
$this->save_state();
|
||||
|
||||
// pretend the number of posts was as big as the number of ids we indexed so far
|
||||
// just an estimation as it includes deleted posts
|
||||
$num_posts = $config['num_posts'];
|
||||
$config['num_posts'] = min($config['num_posts'], $post_counter);
|
||||
$this->search->tidy();
|
||||
$config['num_posts'] = $num_posts;
|
||||
|
||||
if ($post_counter <= $this->max_post_id)
|
||||
{
|
||||
$totaltime = microtime(true) - $starttime;
|
||||
$rows_per_second = $row_count / $totaltime;
|
||||
meta_refresh(1, append_sid($this->u_action . '&action=create&skip_rows=' . $post_counter . '&hash=' . generate_link_hash('acp_search')));
|
||||
trigger_error($user->lang('SEARCH_INDEX_CREATE_REDIRECT', (int) $row_count, $post_counter) . $user->lang('SEARCH_INDEX_CREATE_REDIRECT_RATE', $rows_per_second));
|
||||
}
|
||||
trigger_error($e->getMessage() . adm_back_link($this->u_action) . $this->close_popup_js(), E_USER_WARNING);
|
||||
}
|
||||
|
||||
// Indexing have finished
|
||||
|
||||
$this->search->tidy();
|
||||
|
||||
$this->state = array('');
|
||||
$this->state = [];
|
||||
$this->save_state();
|
||||
|
||||
$phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_SEARCH_INDEX_CREATED', false, array($name));
|
||||
|
@ -454,16 +347,13 @@ class acp_search
|
|||
}
|
||||
}
|
||||
|
||||
$search_types = $this->get_search_types();
|
||||
$search_types = $phpbb_container->get('search.backend_collection');
|
||||
|
||||
$search = null;
|
||||
$error = false;
|
||||
foreach ($search_types as $type)
|
||||
|
||||
foreach ($search_types as $search)
|
||||
{
|
||||
if ($this->init_search($type, $search, $error) || !method_exists($search, 'index_created'))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$type = get_class($search);
|
||||
|
||||
$name = $search->get_name();
|
||||
|
||||
|
@ -508,7 +398,6 @@ class acp_search
|
|||
}
|
||||
}
|
||||
unset($search);
|
||||
unset($error);
|
||||
unset($statistics);
|
||||
unset($data);
|
||||
|
||||
|
@ -522,13 +411,13 @@ class acp_search
|
|||
'UA_PROGRESS_BAR' => addslashes(append_sid("{$phpbb_admin_path}index.$phpEx", "i=$id&mode=$mode&action=progress_bar")),
|
||||
));
|
||||
|
||||
if (isset($this->state[1]))
|
||||
if (isset($this->state[self::STATE_ACTION]))
|
||||
{
|
||||
$template->assign_vars(array(
|
||||
'S_CONTINUE_INDEXING' => $this->state[1],
|
||||
'U_CONTINUE_INDEXING' => $this->u_action . '&action=' . $this->state[1] . '&hash=' . generate_link_hash('acp_search'),
|
||||
'L_CONTINUE' => ($this->state[1] == 'create') ? $user->lang['CONTINUE_INDEXING'] : $user->lang['CONTINUE_DELETING_INDEX'],
|
||||
'L_CONTINUE_EXPLAIN' => ($this->state[1] == 'create') ? $user->lang['CONTINUE_INDEXING_EXPLAIN'] : $user->lang['CONTINUE_DELETING_INDEX_EXPLAIN'])
|
||||
'U_CONTINUE_INDEXING' => $this->u_action . '&action=' . $this->state[self::STATE_ACTION] . '&hash=' . generate_link_hash('acp_search'),
|
||||
'L_CONTINUE' => ($this->state[self::STATE_ACTION] == 'create') ? $user->lang['CONTINUE_INDEXING'] : $user->lang['CONTINUE_DELETING_INDEX'],
|
||||
'L_CONTINUE_EXPLAIN' => ($this->state[self::STATE_ACTION] == 'create') ? $user->lang['CONTINUE_INDEXING_EXPLAIN'] : $user->lang['CONTINUE_DELETING_INDEX_EXPLAIN'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -562,32 +451,6 @@ class acp_search
|
|||
"</script>\n";
|
||||
}
|
||||
|
||||
function get_search_types()
|
||||
{
|
||||
global $phpbb_extension_manager;
|
||||
|
||||
$finder = $phpbb_extension_manager->get_finder();
|
||||
|
||||
return $finder
|
||||
->extension_suffix('_backend')
|
||||
->extension_directory('/search')
|
||||
->core_path('phpbb/search/')
|
||||
->get_classes();
|
||||
}
|
||||
|
||||
function get_max_post_id()
|
||||
{
|
||||
global $db;
|
||||
|
||||
$sql = 'SELECT MAX(post_id) as max_post_id
|
||||
FROM '. POSTS_TABLE;
|
||||
$result = $db->sql_query($sql);
|
||||
$max_post_id = (int) $db->sql_fetchfield('max_post_id');
|
||||
$db->sql_freeresult($result);
|
||||
|
||||
return $max_post_id;
|
||||
}
|
||||
|
||||
function save_state($state = false)
|
||||
{
|
||||
global $config;
|
||||
|
@ -601,25 +464,4 @@ class acp_search
|
|||
|
||||
$config->set('search_indexing_state', implode(',', $this->state), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises a search backend object
|
||||
*
|
||||
* @return false if no error occurred else an error message
|
||||
*/
|
||||
function init_search($type, &$search, &$error)
|
||||
{
|
||||
global $phpbb_root_path, $phpEx, $user, $auth, $config, $db, $phpbb_dispatcher;
|
||||
|
||||
if (!class_exists($type) || !method_exists($type, 'keyword_search'))
|
||||
{
|
||||
$error = $user->lang['NO_SUCH_SEARCH_MODULE'];
|
||||
return $error;
|
||||
}
|
||||
|
||||
$error = false;
|
||||
$search = new $type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher);
|
||||
|
||||
return $error;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -904,7 +904,7 @@ function delete_topics($where_type, $where_ids, $auto_sync = true, $post_count_s
|
|||
*/
|
||||
function delete_posts($where_type, $where_ids, $auto_sync = true, $posted_sync = true, $post_count_sync = true, $call_delete_topics = true)
|
||||
{
|
||||
global $db, $config, $phpbb_root_path, $phpEx, $auth, $user, $phpbb_container, $phpbb_dispatcher;
|
||||
global $db, $config, $phpbb_container, $phpbb_dispatcher;
|
||||
|
||||
// Notifications types to delete
|
||||
$delete_notifications_types = array(
|
||||
|
@ -1086,19 +1086,21 @@ function delete_posts($where_type, $where_ids, $auto_sync = true, $posted_sync =
|
|||
}
|
||||
|
||||
// Remove the message from the search index
|
||||
$search_type = $config['search_type'];
|
||||
|
||||
if (!class_exists($search_type))
|
||||
try
|
||||
{
|
||||
trigger_error('NO_SUCH_SEARCH_MODULE');
|
||||
$search_backend_factory = $phpbb_container->get('search.backend_factory');
|
||||
$search = $search_backend_factory->get_active();
|
||||
}
|
||||
|
||||
$error = false;
|
||||
$search = new $search_type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher);
|
||||
|
||||
if ($error)
|
||||
catch (RuntimeException $e)
|
||||
{
|
||||
trigger_error($error);
|
||||
if (strpos($e->getMessage(), 'No service found') === 0)
|
||||
{
|
||||
trigger_error('NO_SUCH_SEARCH_MODULE');
|
||||
}
|
||||
else
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
$search->index_remove($post_ids, $poster_ids, $forum_ids);
|
||||
|
|
|
@ -1039,34 +1039,6 @@ function set_user_options()
|
|||
return $option_field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Index messages on the fly as we convert them
|
||||
* @todo naderman, can you check that this works with the new search plugins as it's use is currently disabled (and thus untested)
|
||||
function search_indexing($message = '')
|
||||
{
|
||||
global $fulltext_search, $convert_row;
|
||||
|
||||
if (!isset($convert_row['post_id']))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$message)
|
||||
{
|
||||
if (!isset($convert_row['message']))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$message = $convert_row['message'];
|
||||
}
|
||||
|
||||
$title = (isset($convert_row['title'])) ? $convert_row['title'] : '';
|
||||
|
||||
$fulltext_search->index('post', $convert_row['post_id'], $message, $title, $convert_row['poster_id'], $convert_row['forum_id']);
|
||||
}
|
||||
*/
|
||||
|
||||
function make_unique_filename($filename)
|
||||
{
|
||||
if (!strlen($filename))
|
||||
|
|
|
@ -1634,7 +1634,7 @@ function submit_post($mode, $subject, $username, $topic_type, &$poll_ary, &$data
|
|||
|
||||
// Collect some basic information about which tables and which rows to update/insert
|
||||
$sql_data = array();
|
||||
$poster_id = ($mode == 'edit') ? $data_ary['poster_id'] : (int) $user->data['user_id'];
|
||||
$poster_id = ($mode == 'edit') ? (int) $data_ary['poster_id'] : (int) $user->data['user_id'];
|
||||
|
||||
// Retrieve some additional information if not present
|
||||
if ($mode == 'edit' && (!isset($data_ary['post_visibility']) || !isset($data_ary['topic_visibility']) || $data_ary['post_visibility'] === false || $data_ary['topic_visibility'] === false))
|
||||
|
@ -2215,7 +2215,7 @@ function submit_post($mode, $subject, $username, $topic_type, &$poll_ary, &$data
|
|||
$sql_data[TOPICS_TABLE]['stat'][] = "topic_last_post_subject = '" . $db->sql_escape($subject) . "'";
|
||||
|
||||
// Maybe not only the subject, but also changing anonymous usernames. ;)
|
||||
if ($data_ary['poster_id'] == ANONYMOUS)
|
||||
if ((int) $data_ary['poster_id'] == ANONYMOUS)
|
||||
{
|
||||
$sql_data[TOPICS_TABLE]['stat'][] = "topic_last_poster_name = '" . $db->sql_escape($username) . "'";
|
||||
}
|
||||
|
@ -2232,7 +2232,7 @@ function submit_post($mode, $subject, $username, $topic_type, &$poll_ary, &$data
|
|||
$db->sql_freeresult($result);
|
||||
|
||||
// this post is the latest post in the forum, better update
|
||||
if ($row['forum_last_post_id'] == $data_ary['post_id'] && ($row['forum_last_post_subject'] !== $subject || $data_ary['poster_id'] == ANONYMOUS))
|
||||
if ($row['forum_last_post_id'] == $data_ary['post_id'] && ($row['forum_last_post_subject'] !== $subject || (int) $data_ary['poster_id'] == ANONYMOUS))
|
||||
{
|
||||
// the post's subject changed
|
||||
if ($row['forum_last_post_subject'] !== $subject)
|
||||
|
@ -2241,7 +2241,7 @@ function submit_post($mode, $subject, $username, $topic_type, &$poll_ary, &$data
|
|||
}
|
||||
|
||||
// Update the user name if poster is anonymous... just in case a moderator changed it
|
||||
if ($data_ary['poster_id'] == ANONYMOUS)
|
||||
if ((int) $data_ary['poster_id'] == ANONYMOUS)
|
||||
{
|
||||
$sql_data[FORUMS_TABLE]['stat'][] = "forum_last_poster_name = '" . $db->sql_escape($username) . "'";
|
||||
}
|
||||
|
@ -2291,27 +2291,28 @@ function submit_post($mode, $subject, $username, $topic_type, &$poll_ary, &$data
|
|||
// Index message contents
|
||||
if ($update_search_index && $data_ary['enable_indexing'])
|
||||
{
|
||||
// Select the search method and do some additional checks to ensure it can actually be utilised
|
||||
$search_type = $config['search_type'];
|
||||
|
||||
if (!class_exists($search_type))
|
||||
try
|
||||
{
|
||||
trigger_error('NO_SUCH_SEARCH_MODULE');
|
||||
$search_backend_factory = $phpbb_container->get('search.backend_factory');
|
||||
$search = $search_backend_factory->get_active();
|
||||
}
|
||||
catch (RuntimeException $e)
|
||||
{
|
||||
if (strpos($e->getMessage(), 'No service found') === 0)
|
||||
{
|
||||
trigger_error('NO_SUCH_SEARCH_MODULE');
|
||||
}
|
||||
else
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
$error = false;
|
||||
$search = new $search_type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher);
|
||||
|
||||
if ($error)
|
||||
{
|
||||
trigger_error($error);
|
||||
}
|
||||
|
||||
$search->index($mode, $data_ary['post_id'], $data_ary['message'], $subject, $poster_id, $data_ary['forum_id']);
|
||||
$search->index($mode, (int) $data_ary['post_id'], $data_ary['message'], $subject, $poster_id, (int) $data_ary['forum_id']);
|
||||
}
|
||||
|
||||
// Topic Notification, do not change if moderator is changing other users posts...
|
||||
if ($user->data['user_id'] == $poster_id)
|
||||
if ((int) $user->data['user_id'] == $poster_id)
|
||||
{
|
||||
if (!$data_ary['notify_set'] && $data_ary['notify'])
|
||||
{
|
||||
|
|
|
@ -1330,7 +1330,7 @@ function mcp_delete_post($post_ids, $is_soft = false, $soft_delete_reason = '',
|
|||
*/
|
||||
function mcp_fork_topic($topic_ids)
|
||||
{
|
||||
global $auth, $user, $db, $template, $config;
|
||||
global $auth, $user, $db, $template, $config, $phpbb_container;
|
||||
global $phpEx, $phpbb_root_path, $phpbb_log, $request, $phpbb_dispatcher;
|
||||
|
||||
if (!phpbb_check_ids($topic_ids, TOPICS_TABLE, 'topic_id', array('m_')))
|
||||
|
@ -1398,28 +1398,30 @@ function mcp_fork_topic($topic_ids)
|
|||
|
||||
foreach ($topic_data as $topic_id => $topic_row)
|
||||
{
|
||||
if (!isset($search_type) && $topic_row['enable_indexing'])
|
||||
if (!isset($search) && $topic_row['enable_indexing'])
|
||||
{
|
||||
// Select the search method and do some additional checks to ensure it can actually be utilised
|
||||
$search_type = $config['search_type'];
|
||||
|
||||
if (!class_exists($search_type))
|
||||
try
|
||||
{
|
||||
trigger_error('NO_SUCH_SEARCH_MODULE');
|
||||
$search_backend_factory = $phpbb_container->get('search.backend_factory');
|
||||
$search = $search_backend_factory->get_active();
|
||||
}
|
||||
catch (RuntimeException $e)
|
||||
{
|
||||
if (strpos($e->getMessage(), 'No service found') === 0)
|
||||
{
|
||||
trigger_error('NO_SUCH_SEARCH_MODULE');
|
||||
}
|
||||
else
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
$error = false;
|
||||
$search = new $search_type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher);
|
||||
$search_mode = 'post';
|
||||
|
||||
if ($error)
|
||||
{
|
||||
trigger_error($error);
|
||||
}
|
||||
}
|
||||
else if (!isset($search_type) && !$topic_row['enable_indexing'])
|
||||
else if (!isset($search) && !$topic_row['enable_indexing'])
|
||||
{
|
||||
$search_type = false;
|
||||
$search = false;
|
||||
}
|
||||
|
||||
$sql_ary = array(
|
||||
|
@ -1564,7 +1566,7 @@ function mcp_fork_topic($topic_ids)
|
|||
}
|
||||
}
|
||||
$db->sql_query('INSERT INTO ' . POSTS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
|
||||
$new_post_id = $db->sql_nextid();
|
||||
$new_post_id = (int) $db->sql_nextid();
|
||||
|
||||
/**
|
||||
* Perform actions after forked topic is created.
|
||||
|
@ -1601,9 +1603,9 @@ function mcp_fork_topic($topic_ids)
|
|||
// Copy whether the topic is dotted
|
||||
markread('post', $to_forum_id, $new_topic_id, 0, $row['poster_id']);
|
||||
|
||||
if (!empty($search_type))
|
||||
if (!empty($search))
|
||||
{
|
||||
$search->index($search_mode, $new_post_id, $sql_ary['post_text'], $sql_ary['post_subject'], $sql_ary['poster_id'], ($topic_row['topic_type'] == POST_GLOBAL) ? 0 : $to_forum_id);
|
||||
$search->index($search_mode, $new_post_id, $sql_ary['post_text'], $sql_ary['post_subject'], (int) $sql_ary['poster_id'], ($topic_row['topic_type'] == POST_GLOBAL) ? 0 : $to_forum_id);
|
||||
$search_mode = 'reply'; // After one we index replies
|
||||
}
|
||||
|
||||
|
|
|
@ -561,7 +561,7 @@ function phpbb_get_num_ips_for_poster(\phpbb\db\driver\driver_interface $db, $po
|
|||
*/
|
||||
function change_poster(&$post_info, $userdata)
|
||||
{
|
||||
global $auth, $db, $config, $phpbb_root_path, $phpEx, $user, $phpbb_log, $phpbb_dispatcher;
|
||||
global $db, $config, $user, $phpbb_log, $phpbb_dispatcher, $phpbb_container;
|
||||
|
||||
if (empty($userdata) || $userdata['user_id'] == $post_info['user_id'])
|
||||
{
|
||||
|
@ -632,20 +632,25 @@ function change_poster(&$post_info, $userdata)
|
|||
}
|
||||
|
||||
// refresh search cache of this post
|
||||
$search_type = $config['search_type'];
|
||||
|
||||
if (class_exists($search_type))
|
||||
try
|
||||
{
|
||||
// We do some additional checks in the module to ensure it can actually be utilised
|
||||
$error = false;
|
||||
$search = new $search_type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher);
|
||||
|
||||
if (!$error && method_exists($search, 'destroy_cache'))
|
||||
$search_backend_factory = $phpbb_container->get('search.backend_factory');
|
||||
$search = $search_backend_factory->get_active();
|
||||
}
|
||||
catch (RuntimeException $e)
|
||||
{
|
||||
if (strpos($e->getMessage(), 'No service found') === 0)
|
||||
{
|
||||
$search->destroy_cache(array(), array($post_info['user_id'], $userdata['user_id']));
|
||||
trigger_error('NO_SUCH_SEARCH_MODULE');
|
||||
}
|
||||
else
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
$search->index_remove([], [$post_info['user_id'], $userdata['user_id']], []);
|
||||
|
||||
$from_username = $post_info['username'];
|
||||
$to_username = $userdata['username'];
|
||||
|
||||
|
|
|
@ -428,7 +428,7 @@ function mcp_topic_view($id, $mode, $action)
|
|||
*/
|
||||
function split_topic($action, $topic_id, $to_forum_id, $subject)
|
||||
{
|
||||
global $db, $template, $user, $phpEx, $phpbb_root_path, $auth, $config, $phpbb_log, $request, $phpbb_dispatcher;
|
||||
global $db, $template, $user, $phpEx, $phpbb_root_path, $auth, $config, $phpbb_log, $request, $phpbb_container;
|
||||
|
||||
$post_id_list = $request->variable('post_id_list', array(0));
|
||||
$forum_id = $request->variable('forum_id', 0);
|
||||
|
@ -625,22 +625,24 @@ function split_topic($action, $topic_id, $to_forum_id, $subject)
|
|||
if ($first_post_data['enable_indexing'])
|
||||
{
|
||||
// Select the search method and do some additional checks to ensure it can actually be utilised
|
||||
$search_type = $config['search_type'];
|
||||
|
||||
if (!class_exists($search_type))
|
||||
try
|
||||
{
|
||||
trigger_error('NO_SUCH_SEARCH_MODULE');
|
||||
$search_backend_factory = $phpbb_container->get('search.backend_factory');
|
||||
$search = $search_backend_factory->get_active();
|
||||
}
|
||||
catch (RuntimeException $e)
|
||||
{
|
||||
if (strpos($e->getMessage(), 'No service found') === 0)
|
||||
{
|
||||
trigger_error('NO_SUCH_SEARCH_MODULE');
|
||||
}
|
||||
else
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
$error = false;
|
||||
$search = new $search_type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher);
|
||||
|
||||
if ($error)
|
||||
{
|
||||
trigger_error($error);
|
||||
}
|
||||
|
||||
$search->index('edit', $first_post_data['post_id'], $first_post_data['post_text'], $subject, $first_post_data['poster_id'], $first_post_data['forum_id']);
|
||||
$search->index('edit', (int) $first_post_data['post_id'], $first_post_data['post_text'], $subject, (int) $first_post_data['poster_id'], (int) $first_post_data['forum_id']);
|
||||
}
|
||||
|
||||
// Copy topic subscriptions to new topic
|
||||
|
|
|
@ -39,8 +39,6 @@ class convert
|
|||
var $src_truncate_statement = 'DELETE FROM ';
|
||||
var $truncate_statement = 'DELETE FROM ';
|
||||
|
||||
var $fulltext_search;
|
||||
|
||||
// Batch size, can be adjusted by the conversion file
|
||||
// For big boards a value of 6000 seems to be optimal
|
||||
var $batch_size = 2000;
|
||||
|
|
|
@ -215,7 +215,7 @@ class convertor
|
|||
// For conversions we are a bit less strict and set to a search backend we know exist...
|
||||
if (!class_exists($search_type))
|
||||
{
|
||||
$search_type = '\phpbb\search\fulltext_native';
|
||||
$search_type = 'phpbb\search\backend\fulltext_native';
|
||||
$config->set('search_type', $search_type);
|
||||
}
|
||||
|
||||
|
@ -224,14 +224,6 @@ class convertor
|
|||
trigger_error('NO_SUCH_SEARCH_MODULE');
|
||||
}
|
||||
|
||||
$error = false;
|
||||
$convert->fulltext_search = new $search_type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher);
|
||||
|
||||
if ($error)
|
||||
{
|
||||
trigger_error($error);
|
||||
}
|
||||
|
||||
include_once($phpbb_root_path . 'includes/message_parser.' . $phpEx);
|
||||
$message_parser = new \parse_message();
|
||||
|
||||
|
|
|
@ -275,7 +275,7 @@ INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_block_size'
|
|||
INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_gc', '7200');
|
||||
INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_interval', '0');
|
||||
INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_store_results', '1800');
|
||||
INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_type', '\phpbb\search\fulltext_native');
|
||||
INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_type', 'phpbb\search\backend\fulltext_native');
|
||||
INSERT INTO phpbb_config (config_name, config_value) VALUES ('secure_allow_deny', '1');
|
||||
INSERT INTO phpbb_config (config_name, config_value) VALUES ('secure_allow_empty_referer', '1');
|
||||
INSERT INTO phpbb_config (config_name, config_value) VALUES ('secure_downloads', '0');
|
||||
|
|
18
phpBB/phpbb/cache/service.php
vendored
18
phpBB/phpbb/cache/service.php
vendored
|
@ -13,6 +13,8 @@
|
|||
|
||||
namespace phpbb\cache;
|
||||
|
||||
use phpbb\cache\driver\driver_interface;
|
||||
use phpbb\config\config;
|
||||
use phpbb\json\sanitizer as json_sanitizer;
|
||||
|
||||
/**
|
||||
|
@ -23,14 +25,14 @@ class service
|
|||
/**
|
||||
* Cache driver.
|
||||
*
|
||||
* @var \phpbb\cache\driver\driver_interface
|
||||
* @var driver_interface
|
||||
*/
|
||||
protected $driver;
|
||||
|
||||
/**
|
||||
* The config.
|
||||
*
|
||||
* @var \phpbb\config\config
|
||||
* @var config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
|
@ -58,13 +60,13 @@ class service
|
|||
/**
|
||||
* Creates a cache service around a cache driver
|
||||
*
|
||||
* @param \phpbb\cache\driver\driver_interface $driver The cache driver
|
||||
* @param \phpbb\config\config $config The config
|
||||
* @param driver_interface $driver The cache driver
|
||||
* @param config $config The config
|
||||
* @param \phpbb\db\driver\driver_interface $db Database connection
|
||||
* @param string $phpbb_root_path Root path
|
||||
* @param string $php_ext PHP file extension
|
||||
*/
|
||||
public function __construct(\phpbb\cache\driver\driver_interface $driver, \phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, $phpbb_root_path, $php_ext)
|
||||
public function __construct(driver_interface $driver, config $config, \phpbb\db\driver\driver_interface $db, $phpbb_root_path, $php_ext)
|
||||
{
|
||||
$this->set_driver($driver);
|
||||
$this->config = $config;
|
||||
|
@ -76,7 +78,7 @@ class service
|
|||
/**
|
||||
* Returns the cache driver used by this cache service.
|
||||
*
|
||||
* @return \phpbb\cache\driver\driver_interface The cache driver
|
||||
* @return driver_interface The cache driver
|
||||
*/
|
||||
public function get_driver()
|
||||
{
|
||||
|
@ -86,9 +88,9 @@ class service
|
|||
/**
|
||||
* Replaces the cache driver used by this cache service.
|
||||
*
|
||||
* @param \phpbb\cache\driver\driver_interface $driver The cache driver
|
||||
* @param driver_interface $driver The cache driver
|
||||
*/
|
||||
public function set_driver(\phpbb\cache\driver\driver_interface $driver)
|
||||
public function set_driver(driver_interface $driver)
|
||||
{
|
||||
$this->driver = $driver;
|
||||
}
|
||||
|
|
51
phpBB/phpbb/db/migration/data/v400/search_backend_update.php
Normal file
51
phpBB/phpbb/db/migration/data/v400/search_backend_update.php
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\db\migration\data\v400;
|
||||
|
||||
use phpbb\search\backend\fulltext_mysql;
|
||||
use phpbb\search\backend\fulltext_postgres;
|
||||
use phpbb\search\backend\fulltext_sphinx;
|
||||
use phpbb\search\backend\fulltext_native;
|
||||
|
||||
class search_backend_update extends \phpbb\db\migration\migration
|
||||
{
|
||||
static public function depends_on()
|
||||
{
|
||||
return [
|
||||
'\phpbb\db\migration\data\v400\dev',
|
||||
];
|
||||
}
|
||||
|
||||
public function update_data()
|
||||
{
|
||||
switch ($this->config['search_type'])
|
||||
{
|
||||
case '\\phpbb\\search\\fulltext_mysql':
|
||||
$new_search_type = fulltext_mysql::class;
|
||||
break;
|
||||
case '\\phpbb\\search\\fulltext_postgres':
|
||||
$new_search_type = fulltext_postgres::class;
|
||||
break;
|
||||
case '\\phpbb\\search\\fulltext_sphinx':
|
||||
$new_search_type = fulltext_sphinx::class;
|
||||
break;
|
||||
default:
|
||||
$new_search_type = fulltext_native::class;
|
||||
}
|
||||
|
||||
return [
|
||||
['config.update', ['search_type', $new_search_type]],
|
||||
];
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@ use phpbb\install\helper\container_factory;
|
|||
use phpbb\install\helper\database;
|
||||
use phpbb\install\helper\iohandler\iohandler_interface;
|
||||
use phpbb\install\sequential_task;
|
||||
use phpbb\search\fulltext_native;
|
||||
use phpbb\search\backend\fulltext_native;
|
||||
use phpbb\user;
|
||||
|
||||
class create_search_index extends database_task
|
||||
|
@ -98,12 +98,12 @@ class create_search_index extends database_task
|
|||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param config $config Installer config.
|
||||
* @param database $db_helper Database helper.
|
||||
* @param container_factory $container Installer's DI container
|
||||
* @param iohandler_interface $iohandler IO manager.
|
||||
* @param string $phpbb_root_path phpBB root path
|
||||
* @param string $php_ext PHP file extension
|
||||
* @param config $config Installer config.
|
||||
* @param database $db_helper Database helper.
|
||||
* @param container_factory $container Installer's DI container
|
||||
* @param iohandler_interface $iohandler IO manager.
|
||||
* @param string $phpbb_root_path phpBB root path
|
||||
* @param string $php_ext PHP file extension
|
||||
*/
|
||||
public function __construct(
|
||||
config $config,
|
||||
|
@ -127,16 +127,14 @@ class create_search_index extends database_task
|
|||
|
||||
$this->posts_table = $container->get_parameter('tables.posts');
|
||||
|
||||
$this->error = false;
|
||||
$this->search_indexer = new fulltext_native(
|
||||
$this->error,
|
||||
$this->phpbb_root_path,
|
||||
$this->php_ext,
|
||||
$this->auth,
|
||||
$this->config,
|
||||
$this->db,
|
||||
$this->phpbb_dispatcher,
|
||||
$container->get('language'),
|
||||
$this->user,
|
||||
$this->phpbb_dispatcher
|
||||
$this->phpbb_root_path,
|
||||
$this->php_ext
|
||||
);
|
||||
|
||||
parent::__construct($this->conn, $iohandler, true);
|
||||
|
@ -171,11 +169,11 @@ class create_search_index extends database_task
|
|||
{
|
||||
$this->search_indexer->index(
|
||||
'post',
|
||||
$value['post_id'],
|
||||
(int) $value['post_id'],
|
||||
$value['post_text'],
|
||||
$value['post_subject'],
|
||||
$value['poster_id'],
|
||||
$value['forum_id']
|
||||
(int) $value['poster_id'],
|
||||
(int) $value['forum_id']
|
||||
);
|
||||
}
|
||||
|
||||
|
|
483
phpBB/phpbb/search/backend/base.php
Normal file
483
phpBB/phpbb/search/backend/base.php
Normal file
|
@ -0,0 +1,483 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\search\backend;
|
||||
|
||||
use phpbb\cache\service;
|
||||
use phpbb\config\config;
|
||||
use phpbb\db\driver\driver_interface;
|
||||
use phpbb\user;
|
||||
|
||||
/**
|
||||
* optional base class for search plugins providing simple caching based on ACM
|
||||
* and functions to retrieve ignore_words and synonyms
|
||||
*/
|
||||
abstract class base implements search_backend_interface
|
||||
{
|
||||
public const SEARCH_RESULT_NOT_IN_CACHE = 0;
|
||||
public const SEARCH_RESULT_IN_CACHE = 1;
|
||||
public const SEARCH_RESULT_INCOMPLETE = 2;
|
||||
|
||||
// Batch size for create_index and delete_index
|
||||
private const BATCH_SIZE = 100;
|
||||
|
||||
/**
|
||||
* @var service
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* @var config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var driver_interface
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* @var user
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param service $cache
|
||||
* @param config $config
|
||||
* @param driver_interface $db
|
||||
* @param user $user
|
||||
*/
|
||||
public function __construct(service $cache, config $config, driver_interface $db, user $user)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
$this->config = $config;
|
||||
$this->db = $db;
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves cached search results
|
||||
*
|
||||
* @param string $search_key an md5 string generated from all the passed search options to identify the results
|
||||
* @param int &$result_count will contain the number of all results for the search (not only for the current page)
|
||||
* @param array &$id_ary is filled with the ids belonging to the requested page that are stored in the cache
|
||||
* @param int &$start indicates the first index of the page
|
||||
* @param int $per_page number of ids each page is supposed to contain
|
||||
* @param string $sort_dir is either a or d representing ASC and DESC
|
||||
*
|
||||
* @return int self::SEARCH_RESULT_NOT_IN_CACHE or self::SEARCH_RESULT_IN_CACHE or self::SEARCH_RESULT_INCOMPLETE
|
||||
*/
|
||||
protected function obtain_ids(string $search_key, int &$result_count, array &$id_ary, int &$start, int $per_page, string $sort_dir): int
|
||||
{
|
||||
if (!($stored_ids = $this->cache->get('_search_results_' . $search_key)))
|
||||
{
|
||||
// no search results cached for this search_key
|
||||
return self::SEARCH_RESULT_NOT_IN_CACHE;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result_count = $stored_ids[-1];
|
||||
$reverse_ids = $stored_ids[-2] != $sort_dir;
|
||||
$complete = true;
|
||||
|
||||
// Change start parameter in case out of bounds
|
||||
if ($result_count)
|
||||
{
|
||||
if ($start < 0)
|
||||
{
|
||||
$start = 0;
|
||||
}
|
||||
else if ($start >= $result_count)
|
||||
{
|
||||
$start = floor(($result_count - 1) / $per_page) * $per_page;
|
||||
}
|
||||
}
|
||||
|
||||
// change the start to the actual end of the current request if the sort direction differs
|
||||
// from the direction in the cache and reverse the ids later
|
||||
if ($reverse_ids)
|
||||
{
|
||||
$start = $result_count - $start - $per_page;
|
||||
|
||||
// the user requested a page past the last index
|
||||
if ($start < 0)
|
||||
{
|
||||
return self::SEARCH_RESULT_NOT_IN_CACHE;
|
||||
}
|
||||
}
|
||||
|
||||
for ($i = $start, $n = $start + $per_page; ($i < $n) && ($i < $result_count); $i++)
|
||||
{
|
||||
if (!isset($stored_ids[$i]))
|
||||
{
|
||||
$complete = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$id_ary[] = $stored_ids[$i];
|
||||
}
|
||||
}
|
||||
unset($stored_ids);
|
||||
|
||||
if ($reverse_ids)
|
||||
{
|
||||
$id_ary = array_reverse($id_ary);
|
||||
}
|
||||
|
||||
if (!$complete)
|
||||
{
|
||||
return self::SEARCH_RESULT_INCOMPLETE;
|
||||
}
|
||||
return self::SEARCH_RESULT_IN_CACHE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches post/topic ids
|
||||
*
|
||||
* @param string $search_key an md5 string generated from all the passed search options to identify the results
|
||||
* @param string $keywords contains the keywords as entered by the user
|
||||
* @param array $author_ary an array of author ids, if the author should be ignored during the search the array is empty
|
||||
* @param int $result_count contains the number of all results for the search (not only for the current page)
|
||||
* @param array &$id_ary contains a list of post or topic ids that shall be cached, the first element
|
||||
* must have the absolute index $start in the result set.
|
||||
* @param int $start indicates the first index of the page
|
||||
* @param string $sort_dir is either a or d representing ASC and DESC
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function save_ids(string $search_key, string $keywords, array $author_ary, int $result_count, array &$id_ary, int $start, string $sort_dir): void
|
||||
{
|
||||
global $user;
|
||||
|
||||
$length = min(count($id_ary), $this->config['search_block_size']);
|
||||
|
||||
// nothing to cache so exit
|
||||
if (!$length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$store_ids = array_slice($id_ary, 0, $length);
|
||||
|
||||
// create a new resultset if there is none for this search_key yet
|
||||
// or add the ids to the existing resultset
|
||||
if (!($store = $this->cache->get('_search_results_' . $search_key)))
|
||||
{
|
||||
// add the current keywords to the recent searches in the cache which are listed on the search page
|
||||
if (!empty($keywords) || count($author_ary))
|
||||
{
|
||||
$sql = 'SELECT search_time
|
||||
FROM ' . SEARCH_RESULTS_TABLE . '
|
||||
WHERE search_key = \'' . $this->db->sql_escape($search_key) . '\'';
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
if (!$this->db->sql_fetchrow($result))
|
||||
{
|
||||
$sql_ary = array(
|
||||
'search_key' => $search_key,
|
||||
'search_time' => time(),
|
||||
'search_keywords' => $keywords,
|
||||
'search_authors' => ' ' . implode(' ', $author_ary) . ' '
|
||||
);
|
||||
|
||||
$sql = 'INSERT INTO ' . SEARCH_RESULTS_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
}
|
||||
|
||||
$sql = 'UPDATE ' . USERS_TABLE . '
|
||||
SET user_last_search = ' . time() . '
|
||||
WHERE user_id = ' . $user->data['user_id'];
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
$store = array(-1 => $result_count, -2 => $sort_dir);
|
||||
$id_range = range($start, $start + $length - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we use one set of results for both sort directions so we have to calculate the indizes
|
||||
// for the reversed array and we also have to reverse the ids themselves
|
||||
if ($store[-2] != $sort_dir)
|
||||
{
|
||||
$store_ids = array_reverse($store_ids);
|
||||
$id_range = range($store[-1] - $start - $length, $store[-1] - $start - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
$id_range = range($start, $start + $length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
$store_ids = array_combine($id_range, $store_ids);
|
||||
|
||||
// append the ids
|
||||
if (is_array($store_ids))
|
||||
{
|
||||
$store += $store_ids;
|
||||
|
||||
// if the cache is too big
|
||||
if (count($store) - 2 > 20 * $this->config['search_block_size'])
|
||||
{
|
||||
// remove everything in front of two blocks in front of the current start index
|
||||
for ($i = 0, $n = $id_range[0] - 2 * $this->config['search_block_size']; $i < $n; $i++)
|
||||
{
|
||||
if (isset($store[$i]))
|
||||
{
|
||||
unset($store[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
// remove everything after two blocks after the current stop index
|
||||
end($id_range);
|
||||
for ($i = $store[-1] - 1, $n = current($id_range) + 2 * $this->config['search_block_size']; $i > $n; $i--)
|
||||
{
|
||||
if (isset($store[$i]))
|
||||
{
|
||||
unset($store[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->cache->put('_search_results_' . $search_key, $store, $this->config['search_store_results']);
|
||||
|
||||
$sql = 'UPDATE ' . SEARCH_RESULTS_TABLE . '
|
||||
SET search_time = ' . time() . '
|
||||
WHERE search_key = \'' . $this->db->sql_escape($search_key) . '\'';
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
|
||||
unset($store, $store_ids, $id_range);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes old entries from the search results table and removes searches with keywords that contain a word in $words.
|
||||
*
|
||||
* @param array $words
|
||||
* @param array|bool $authors
|
||||
*/
|
||||
protected function destroy_cache(array $words, $authors = false): void
|
||||
{
|
||||
// clear all searches that searched for the specified words
|
||||
if (count($words))
|
||||
{
|
||||
$sql_where = '';
|
||||
foreach ($words as $word)
|
||||
{
|
||||
$sql_where .= " OR search_keywords " . $this->db->sql_like_expression($this->db->get_any_char() . $word . $this->db->get_any_char());
|
||||
}
|
||||
|
||||
$sql = 'SELECT search_key
|
||||
FROM ' . SEARCH_RESULTS_TABLE . "
|
||||
WHERE search_keywords LIKE '%*%' $sql_where";
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$this->cache->destroy('_search_results_' . $row['search_key']);
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
}
|
||||
|
||||
// clear all searches that searched for the specified authors
|
||||
if (is_array($authors) && count($authors))
|
||||
{
|
||||
$sql_where = '';
|
||||
foreach ($authors as $author)
|
||||
{
|
||||
$sql_where .= (($sql_where) ? ' OR ' : '') . 'search_authors ' . $this->db->sql_like_expression($this->db->get_any_char() . ' ' . (int) $author . ' ' . $this->db->get_any_char());
|
||||
}
|
||||
|
||||
$sql = 'SELECT search_key
|
||||
FROM ' . SEARCH_RESULTS_TABLE . "
|
||||
WHERE $sql_where";
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$this->cache->destroy('_search_results_' . $row['search_key']);
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
}
|
||||
|
||||
$sql = 'DELETE
|
||||
FROM ' . SEARCH_RESULTS_TABLE . '
|
||||
WHERE search_time < ' . (time() - (int) $this->config['search_store_results']);
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function create_index(int &$post_counter = 0): ?array
|
||||
{
|
||||
$max_post_id = $this->get_max_post_id();
|
||||
$forums_indexing_enabled = $this->forum_ids_with_indexing_enabled();
|
||||
|
||||
$starttime = microtime(true);
|
||||
$row_count = 0;
|
||||
|
||||
while (still_on_time() && $post_counter <= $max_post_id)
|
||||
{
|
||||
$rows = $this->get_posts_between($post_counter + 1, $post_counter + self::BATCH_SIZE);
|
||||
|
||||
if ($this->db->sql_buffer_nested_transactions())
|
||||
{
|
||||
$rows = iterator_to_array($rows);
|
||||
}
|
||||
|
||||
foreach ($rows as $row)
|
||||
{
|
||||
// Indexing enabled for this forum
|
||||
if (in_array($row['forum_id'], $forums_indexing_enabled, true))
|
||||
{
|
||||
$this->index('post', (int) $row['post_id'], $row['post_text'], $row['post_subject'], (int) $row['poster_id'], (int) $row['forum_id']);
|
||||
}
|
||||
$row_count++;
|
||||
}
|
||||
|
||||
$post_counter += self::BATCH_SIZE;
|
||||
}
|
||||
|
||||
// pretend the number of posts was as big as the number of ids we indexed so far
|
||||
// just an estimation as it includes deleted posts
|
||||
$num_posts = $this->config['num_posts'];
|
||||
$this->config['num_posts'] = min($this->config['num_posts'], $post_counter);
|
||||
$this->tidy();
|
||||
$this->config['num_posts'] = $num_posts;
|
||||
|
||||
if ($post_counter <= $max_post_id)
|
||||
{
|
||||
$totaltime = microtime(true) - $starttime;
|
||||
$rows_per_second = $row_count / $totaltime;
|
||||
|
||||
return [
|
||||
'row_count' => $row_count,
|
||||
'post_counter' => $post_counter,
|
||||
'max_post_id' => $max_post_id,
|
||||
'rows_per_second' => $rows_per_second,
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete_index(int &$post_counter = null): ?array
|
||||
{
|
||||
$max_post_id = $this->get_max_post_id();
|
||||
|
||||
$starttime = microtime(true);
|
||||
$row_count = 0;
|
||||
while (still_on_time() && $post_counter <= $max_post_id)
|
||||
{
|
||||
$rows = $this->get_posts_between($post_counter + 1, $post_counter + self::BATCH_SIZE);
|
||||
$ids = $posters = $forum_ids = array();
|
||||
foreach ($rows as $row)
|
||||
{
|
||||
$ids[] = $row['post_id'];
|
||||
$posters[] = $row['poster_id'];
|
||||
$forum_ids[] = $row['forum_id'];
|
||||
}
|
||||
$row_count += count($ids);
|
||||
|
||||
if (count($ids))
|
||||
{
|
||||
$this->index_remove($ids, $posters, $forum_ids);
|
||||
}
|
||||
|
||||
$post_counter += self::BATCH_SIZE;
|
||||
}
|
||||
|
||||
if ($post_counter <= $max_post_id)
|
||||
{
|
||||
$totaltime = microtime(true) - $starttime;
|
||||
$rows_per_second = $row_count / $totaltime;
|
||||
|
||||
return [
|
||||
'row_count' => $row_count,
|
||||
'post_counter' => $post_counter,
|
||||
'max_post_id' => $max_post_id,
|
||||
'rows_per_second' => $rows_per_second,
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ids of the forums that have indexing enabled
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function forum_ids_with_indexing_enabled(): array
|
||||
{
|
||||
$forums = [];
|
||||
|
||||
$sql = 'SELECT forum_id, enable_indexing
|
||||
FROM ' . FORUMS_TABLE;
|
||||
$result = $this->db->sql_query($sql, 3600);
|
||||
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
if ((bool) $row['enable_indexing'])
|
||||
{
|
||||
$forums[] = $row['forum_id'];
|
||||
}
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return $forums;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get posts between 2 ids
|
||||
*
|
||||
* @param int $initial_id
|
||||
* @param int $final_id
|
||||
* @return \Generator
|
||||
*/
|
||||
protected function get_posts_between(int $initial_id, int $final_id): \Generator
|
||||
{
|
||||
$sql = 'SELECT post_id, post_subject, post_text, poster_id, forum_id
|
||||
FROM ' . POSTS_TABLE . '
|
||||
WHERE post_id >= ' . $initial_id . '
|
||||
AND post_id <= ' . $final_id;
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
yield $row;
|
||||
}
|
||||
|
||||
$this->db->sql_freeresult($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get post with higher id
|
||||
*/
|
||||
protected function get_max_post_id(): int
|
||||
{
|
||||
$sql = 'SELECT MAX(post_id) as max_post_id
|
||||
FROM '. POSTS_TABLE;
|
||||
$result = $this->db->sql_query($sql);
|
||||
$max_post_id = (int) $this->db->sql_fetchfield('max_post_id');
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
return $max_post_id;
|
||||
}
|
||||
}
|
|
@ -11,12 +11,19 @@
|
|||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\search;
|
||||
namespace phpbb\search\backend;
|
||||
|
||||
use phpbb\config\config;
|
||||
use phpbb\db\driver\driver_interface;
|
||||
use phpbb\event\dispatcher_interface;
|
||||
use phpbb\language\language;
|
||||
use phpbb\user;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Fulltext search for MySQL
|
||||
*/
|
||||
class fulltext_mysql extends \phpbb\search\base
|
||||
class fulltext_mysql extends base implements search_backend_interface
|
||||
{
|
||||
/**
|
||||
* Associative array holding index stats
|
||||
|
@ -30,29 +37,16 @@ class fulltext_mysql extends \phpbb\search\base
|
|||
*/
|
||||
protected $split_words = array();
|
||||
|
||||
/**
|
||||
* Config object
|
||||
* @var \phpbb\config\config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Database connection
|
||||
* @var \phpbb\db\driver\driver_interface
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* phpBB event dispatcher object
|
||||
* @var \phpbb\event\dispatcher_interface
|
||||
* @var dispatcher_interface
|
||||
*/
|
||||
protected $phpbb_dispatcher;
|
||||
|
||||
/**
|
||||
* User object
|
||||
* @var \phpbb\user
|
||||
* @var language
|
||||
*/
|
||||
protected $user;
|
||||
protected $language;
|
||||
|
||||
/**
|
||||
* Associative array stores the min and max word length to be searched
|
||||
|
@ -76,23 +70,23 @@ class fulltext_mysql extends \phpbb\search\base
|
|||
|
||||
/**
|
||||
* Constructor
|
||||
* Creates a new \phpbb\search\fulltext_mysql, which is used as a search backend
|
||||
* Creates a new \phpbb\search\backend\fulltext_mysql, which is used as a search backend
|
||||
*
|
||||
* @param string|bool $error Any error that occurs is passed on through this reference variable otherwise false
|
||||
* @param config $config Config object
|
||||
* @param driver_interface $db Database object
|
||||
* @param dispatcher_interface $phpbb_dispatcher Event dispatcher object
|
||||
* @param language $language
|
||||
* @param user $user User object
|
||||
* @param string $phpbb_root_path Relative path to phpBB root
|
||||
* @param string $phpEx PHP file extension
|
||||
* @param \phpbb\auth\auth $auth Auth object
|
||||
* @param \phpbb\config\config $config Config object
|
||||
* @param \phpbb\db\driver\driver_interface $db Database object
|
||||
* @param \phpbb\user $user User object
|
||||
* @param \phpbb\event\dispatcher_interface $phpbb_dispatcher Event dispatcher object
|
||||
*/
|
||||
public function __construct(&$error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher)
|
||||
public function __construct(config $config, driver_interface $db, dispatcher_interface $phpbb_dispatcher, language $language, user $user, string $phpbb_root_path, string $phpEx)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->db = $db;
|
||||
global $cache;
|
||||
|
||||
parent::__construct($cache, $config, $db, $user);
|
||||
$this->phpbb_dispatcher = $phpbb_dispatcher;
|
||||
$this->user = $user;
|
||||
$this->language = $language;
|
||||
|
||||
$this->word_length = array('min' => $this->config['fulltext_mysql_min_word_len'], 'max' => $this->config['fulltext_mysql_max_word_len']);
|
||||
|
||||
|
@ -103,75 +97,45 @@ class fulltext_mysql extends \phpbb\search\base
|
|||
{
|
||||
include($phpbb_root_path . 'includes/utf/utf_tools.' . $phpEx);
|
||||
}
|
||||
|
||||
$error = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this search backend to be displayed to administrators
|
||||
*
|
||||
* @return string Name
|
||||
*/
|
||||
public function get_name()
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_name(): string
|
||||
{
|
||||
return 'MySQL Fulltext';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the search_query
|
||||
*
|
||||
* @return string search query
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_search_query()
|
||||
{
|
||||
return $this->search_query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the common_words array
|
||||
*
|
||||
* @return array common words that are ignored by search backend
|
||||
*/
|
||||
public function get_common_words()
|
||||
{
|
||||
return $this->common_words;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the word_length array
|
||||
*
|
||||
* @return array min and max word length for searching
|
||||
*/
|
||||
public function get_word_length()
|
||||
{
|
||||
return $this->word_length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for correct MySQL version and stores min/max word length in the config
|
||||
*
|
||||
* @return string|bool Language key of the error/incompatibility occurred
|
||||
*/
|
||||
public function init()
|
||||
public function is_available(): bool
|
||||
{
|
||||
// Check if we are using mysql
|
||||
if ($this->db->get_sql_layer() != 'mysqli')
|
||||
{
|
||||
return $this->user->lang['FULLTEXT_MYSQL_INCOMPATIBLE_DATABASE'];
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
if (!$this->is_available())
|
||||
{
|
||||
return $this->language->lang('FULLTEXT_MYSQL_INCOMPATIBLE_DATABASE');
|
||||
}
|
||||
|
||||
$result = $this->db->sql_query('SHOW TABLE STATUS LIKE \'' . POSTS_TABLE . '\'');
|
||||
$info = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
$engine = '';
|
||||
if (isset($info['Engine']))
|
||||
{
|
||||
$engine = $info['Engine'];
|
||||
}
|
||||
else if (isset($info['Type']))
|
||||
{
|
||||
$engine = $info['Type'];
|
||||
}
|
||||
$engine = $info['Engine'] ?? $info['Type'] ?? '';
|
||||
|
||||
$fulltext_supported = $engine === 'Aria' || $engine === 'MyISAM'
|
||||
/**
|
||||
|
@ -180,12 +144,12 @@ class fulltext_mysql extends \phpbb\search\base
|
|||
* We also require https://bugs.mysql.com/bug.php?id=67004 to be
|
||||
* fixed for proper overall operation. Hence we require 5.6.8.
|
||||
*/
|
||||
|| $engine === 'InnoDB'
|
||||
&& phpbb_version_compare($this->db->sql_server_info(true), '5.6.8', '>=');
|
||||
|| ($engine === 'InnoDB'
|
||||
&& phpbb_version_compare($this->db->sql_server_info(true), '5.6.8', '>='));
|
||||
|
||||
if (!$fulltext_supported)
|
||||
{
|
||||
return $this->user->lang['FULLTEXT_MYSQL_NOT_SUPPORTED'];
|
||||
return $this->language->lang('FULLTEXT_MYSQL_NOT_SUPPORTED');
|
||||
}
|
||||
|
||||
$sql = 'SHOW VARIABLES
|
||||
|
@ -214,14 +178,33 @@ class fulltext_mysql extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* Splits keywords entered by a user into an array of words stored in $this->split_words
|
||||
* Stores the tidied search query in $this->search_query
|
||||
*
|
||||
* @param string &$keywords Contains the keyword as entered by the user
|
||||
* @param string $terms is either 'all' or 'any'
|
||||
* @return bool false if no valid keywords were found and otherwise true
|
||||
*/
|
||||
public function split_keywords(&$keywords, $terms)
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_search_query(): string
|
||||
{
|
||||
return $this->search_query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_common_words(): array
|
||||
{
|
||||
return $this->common_words;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_word_length()
|
||||
{
|
||||
return $this->word_length;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function split_keywords(string &$keywords, string $terms): bool
|
||||
{
|
||||
if ($terms == 'all')
|
||||
{
|
||||
|
@ -243,7 +226,7 @@ class fulltext_mysql extends \phpbb\search\base
|
|||
// We limit the number of allowed keywords to minimize load on the database
|
||||
if ($this->config['max_num_search_keywords'] && count($this->split_words) > $this->config['max_num_search_keywords'])
|
||||
{
|
||||
trigger_error($this->user->lang('MAX_NUM_SEARCH_KEYWORDS_REFINE', (int) $this->config['max_num_search_keywords'], count($this->split_words)));
|
||||
trigger_error($this->language->lang('MAX_NUM_SEARCH_KEYWORDS_REFINE', (int) $this->config['max_num_search_keywords'], count($this->split_words)));
|
||||
}
|
||||
|
||||
// to allow phrase search, we need to concatenate quoted words
|
||||
|
@ -357,52 +340,9 @@ class fulltext_mysql extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* Turns text into an array of words
|
||||
* @param string $text contains post text/subject
|
||||
*/
|
||||
public function split_message($text)
|
||||
{
|
||||
// Split words
|
||||
$text = preg_replace('#([^\p{L}\p{N}\'*])#u', '$1$1', str_replace('\'\'', '\' \'', trim($text)));
|
||||
$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];
|
||||
|
||||
// remove too short or too long words
|
||||
$text = array_values($text);
|
||||
for ($i = 0, $n = count($text); $i < $n; $i++)
|
||||
{
|
||||
$text[$i] = trim($text[$i]);
|
||||
if (utf8_strlen($text[$i]) < $this->config['fulltext_mysql_min_word_len'] || utf8_strlen($text[$i]) > $this->config['fulltext_mysql_max_word_len'])
|
||||
{
|
||||
unset($text[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a search on keywords depending on display specific params. You have to run split_keywords() first
|
||||
*
|
||||
* @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 string $post_visibility specifies which types of posts the user can view in which forums
|
||||
* @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 string $author_name specifies the author match, when ANONYMOUS is also a search-match
|
||||
* @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
|
||||
*/
|
||||
public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $post_visibility, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function keyword_search(string $type, string $fields, string $terms, array $sort_by_sql, string $sort_key, string $sort_dir, string $sort_days, array $ex_fid_ary, string $post_visibility, int $topic_id, array $author_ary, string $author_name, array &$id_ary, int &$start, int $per_page)
|
||||
{
|
||||
// No keywords? No posts
|
||||
if (!$this->search_query)
|
||||
|
@ -463,7 +403,7 @@ class fulltext_mysql extends \phpbb\search\base
|
|||
|
||||
// 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)
|
||||
if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == self::SEARCH_RESULT_IN_CACHE)
|
||||
{
|
||||
return $result_count;
|
||||
}
|
||||
|
@ -648,25 +588,9 @@ class fulltext_mysql extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* Performs a search on an author's posts without caring about message contents. Depends on display specific params
|
||||
*
|
||||
* @param string $type contains either posts or topics depending on what should be searched for
|
||||
* @param boolean $firstpost_only if true, only topic starting posts will be considered
|
||||
* @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 string $post_visibility specifies which types of posts the user can view in which forums
|
||||
* @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
|
||||
* @param string $author_name specifies the author match, when ANONYMOUS is also a search-match
|
||||
* @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
|
||||
*/
|
||||
public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $post_visibility, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function author_search(string $type, bool $firstpost_only, array $sort_by_sql, string $sort_key, string $sort_dir, string $sort_days, array $ex_fid_ary, string $post_visibility, int $topic_id, array $author_ary, string $author_name, array &$id_ary, int &$start, int $per_page)
|
||||
{
|
||||
// No author? No posts
|
||||
if (!count($author_ary))
|
||||
|
@ -729,7 +653,7 @@ class fulltext_mysql extends \phpbb\search\base
|
|||
|
||||
// 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)
|
||||
if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == self::SEARCH_RESULT_IN_CACHE)
|
||||
{
|
||||
return $result_count;
|
||||
}
|
||||
|
@ -910,16 +834,17 @@ class fulltext_mysql extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* 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 ...
|
||||
* @param int $post_id contains the post id of the post to index
|
||||
* @param string $message contains the post text of the post
|
||||
* @param string $subject contains the subject of the post to index
|
||||
* @param int $poster_id contains the user id of the poster
|
||||
* @param int $forum_id contains the forum id of parent forum of the post
|
||||
*/
|
||||
public function index($mode, $post_id, &$message, &$subject, $poster_id, $forum_id)
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports_phrase_search(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function index(string $mode, int $post_id, string &$message, string &$subject, int $poster_id, int $forum_id)
|
||||
{
|
||||
// Split old and new post/subject to obtain array of words
|
||||
$split_text = $this->split_message($message);
|
||||
|
@ -955,8 +880,7 @@ class fulltext_mysql extends \phpbb\search\base
|
|||
);
|
||||
extract($this->phpbb_dispatcher->trigger_event('core.search_mysql_index_before', compact($vars)));
|
||||
|
||||
unset($split_text);
|
||||
unset($split_title);
|
||||
unset($split_text, $split_title);
|
||||
|
||||
// destroy cached search results containing any of the words removed or added
|
||||
$this->destroy_cache($words, array($poster_id));
|
||||
|
@ -965,35 +889,33 @@ class fulltext_mysql extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* Destroy cached results, that might be outdated after deleting a post
|
||||
*/
|
||||
public function index_remove($post_ids, $author_ids, $forum_ids)
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function index_remove(array $post_ids, array $author_ids, array $forum_ids): void
|
||||
{
|
||||
$this->destroy_cache(array(), array_unique($author_ids));
|
||||
$this->destroy_cache([], array_unique($author_ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy old cache entries
|
||||
*/
|
||||
public function tidy()
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function tidy(): void
|
||||
{
|
||||
// destroy too old cached search results
|
||||
$this->destroy_cache(array());
|
||||
$this->destroy_cache([]);
|
||||
|
||||
$this->config->set('search_last_gc', time(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create fulltext index
|
||||
*
|
||||
* @return string|bool error string is returned incase of errors otherwise false
|
||||
*/
|
||||
public function create_index($acp_module, $u_action)
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function create_index(int &$post_counter = 0): ?array
|
||||
{
|
||||
// Make sure we can actually use MySQL with fulltext indexes
|
||||
if ($error = $this->init())
|
||||
{
|
||||
return $error;
|
||||
throw new RuntimeException($error);
|
||||
}
|
||||
|
||||
if (empty($this->stats))
|
||||
|
@ -1054,20 +976,18 @@ class fulltext_mysql extends \phpbb\search\base
|
|||
|
||||
$this->db->sql_query('TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE);
|
||||
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop fulltext index
|
||||
*
|
||||
* @return string|bool error string is returned incase of errors otherwise false
|
||||
*/
|
||||
public function delete_index($acp_module, $u_action)
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete_index(int &$post_counter = null): ?array
|
||||
{
|
||||
// Make sure we can actually use MySQL with fulltext indexes
|
||||
if ($error = $this->init())
|
||||
{
|
||||
return $error;
|
||||
throw new RuntimeException($error);
|
||||
}
|
||||
|
||||
if (empty($this->stats))
|
||||
|
@ -1122,13 +1042,13 @@ class fulltext_mysql extends \phpbb\search\base
|
|||
|
||||
$this->db->sql_query('TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE);
|
||||
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if both FULLTEXT indexes exist
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function index_created()
|
||||
public function index_created(): bool
|
||||
{
|
||||
if (empty($this->stats))
|
||||
{
|
||||
|
@ -1139,8 +1059,8 @@ class fulltext_mysql extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns an associative array containing information about the indexes
|
||||
*/
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function index_stats()
|
||||
{
|
||||
if (empty($this->stats))
|
||||
|
@ -1149,7 +1069,7 @@ class fulltext_mysql extends \phpbb\search\base
|
|||
}
|
||||
|
||||
return array(
|
||||
$this->user->lang['FULLTEXT_MYSQL_TOTAL_POSTS'] => ($this->index_created()) ? $this->stats['total_posts'] : 0,
|
||||
$this->language->lang('FULLTEXT_MYSQL_TOTAL_POSTS') => ($this->index_created()) ? $this->stats['total_posts'] : 0,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1195,19 +1115,45 @@ class fulltext_mysql extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* Display a note, that UTF-8 support is not available with certain versions of PHP
|
||||
*
|
||||
* @return associative array containing template and config variables
|
||||
*/
|
||||
public function acp()
|
||||
* Turns text into an array of words
|
||||
* @param string $text contains post text/subject
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function split_message($text): array
|
||||
{
|
||||
// Split words
|
||||
$text = preg_replace('#([^\p{L}\p{N}\'*])#u', '$1$1', str_replace('\'\'', '\' \'', trim($text)));
|
||||
$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];
|
||||
|
||||
// remove too short or too long words
|
||||
$text = array_values($text);
|
||||
for ($i = 0, $n = count($text); $i < $n; $i++)
|
||||
{
|
||||
$text[$i] = trim($text[$i]);
|
||||
if (utf8_strlen($text[$i]) < $this->config['fulltext_mysql_min_word_len'] || utf8_strlen($text[$i]) > $this->config['fulltext_mysql_max_word_len'])
|
||||
{
|
||||
unset($text[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_acp_options(): array
|
||||
{
|
||||
$tpl = '
|
||||
<dl>
|
||||
<dt><label>' . $this->user->lang['MIN_SEARCH_CHARS'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['FULLTEXT_MYSQL_MIN_SEARCH_CHARS_EXPLAIN'] . '</span></dt>
|
||||
<dt><label>' . $this->language->lang('MIN_SEARCH_CHARS') . $this->language->lang('COLON') . '</label><br /><span>' . $this->language->lang('FULLTEXT_MYSQL_MIN_SEARCH_CHARS_EXPLAIN') . '</span></dt>
|
||||
<dd>' . $this->config['fulltext_mysql_min_word_len'] . '</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt><label>' . $this->user->lang['MAX_SEARCH_CHARS'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['FULLTEXT_MYSQL_MAX_SEARCH_CHARS_EXPLAIN'] . '</span></dt>
|
||||
<dt><label>' . $this->language->lang('MAX_SEARCH_CHARS') . $this->language->lang('COLON') . '</label><br /><span>' . $this->language->lang('FULLTEXT_MYSQL_MAX_SEARCH_CHARS_EXPLAIN') . '</span></dt>
|
||||
<dd>' . $this->config['fulltext_mysql_max_word_len'] . '</dd>
|
||||
</dl>
|
||||
';
|
|
@ -11,19 +11,25 @@
|
|||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\search;
|
||||
namespace phpbb\search\backend;
|
||||
|
||||
use phpbb\config\config;
|
||||
use phpbb\db\driver\driver_interface;
|
||||
use phpbb\event\dispatcher_interface;
|
||||
use phpbb\language\language;
|
||||
use phpbb\user;
|
||||
|
||||
/**
|
||||
* phpBB's own db driven fulltext search, version 2
|
||||
*/
|
||||
class fulltext_native extends \phpbb\search\base
|
||||
class fulltext_native extends base implements search_backend_interface
|
||||
{
|
||||
const UTF8_HANGUL_FIRST = "\xEA\xB0\x80";
|
||||
const UTF8_HANGUL_LAST = "\xED\x9E\xA3";
|
||||
const UTF8_CJK_FIRST = "\xE4\xB8\x80";
|
||||
const UTF8_CJK_LAST = "\xE9\xBE\xBB";
|
||||
const UTF8_CJK_B_FIRST = "\xF0\xA0\x80\x80";
|
||||
const UTF8_CJK_B_LAST = "\xF0\xAA\x9B\x96";
|
||||
protected const UTF8_HANGUL_FIRST = "\xEA\xB0\x80";
|
||||
protected const UTF8_HANGUL_LAST = "\xED\x9E\xA3";
|
||||
protected const UTF8_CJK_FIRST = "\xE4\xB8\x80";
|
||||
protected const UTF8_CJK_LAST = "\xE9\xBE\xBB";
|
||||
protected const UTF8_CJK_B_FIRST = "\xF0\xA0\x80\x80";
|
||||
protected const UTF8_CJK_B_LAST = "\xF0\xAA\x9B\x96";
|
||||
|
||||
/**
|
||||
* Associative array holding index stats
|
||||
|
@ -81,50 +87,38 @@ class fulltext_native extends \phpbb\search\base
|
|||
*/
|
||||
protected $php_ext;
|
||||
|
||||
/**
|
||||
* Config object
|
||||
* @var \phpbb\config\config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Database connection
|
||||
* @var \phpbb\db\driver\driver_interface
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* phpBB event dispatcher object
|
||||
* @var \phpbb\event\dispatcher_interface
|
||||
* @var dispatcher_interface
|
||||
*/
|
||||
protected $phpbb_dispatcher;
|
||||
|
||||
/**
|
||||
* User object
|
||||
* @var \phpbb\user
|
||||
* @var language
|
||||
*/
|
||||
protected $user;
|
||||
protected $language;
|
||||
|
||||
/**
|
||||
* Initialises the fulltext_native search backend with min/max word length
|
||||
*
|
||||
* @param boolean|string &$error is passed by reference and should either be set to false on success or an error message on failure
|
||||
* @param string $phpbb_root_path phpBB root path
|
||||
* @param string $phpEx PHP file extension
|
||||
* @param \phpbb\auth\auth $auth Auth object
|
||||
* @param \phpbb\config\config $config Config object
|
||||
* @param \phpbb\db\driver\driver_interface $db Database object
|
||||
* @param \phpbb\user $user User object
|
||||
* @param \phpbb\event\dispatcher_interface $phpbb_dispatcher Event dispatcher object
|
||||
*/
|
||||
public function __construct(&$error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher)
|
||||
* Initialises the fulltext_native search backend with min/max word length
|
||||
*
|
||||
* @param config $config Config object
|
||||
* @param driver_interface $db Database object
|
||||
* @param dispatcher_interface $phpbb_dispatcher Event dispatcher object
|
||||
* @param language $language
|
||||
* @param user $user User object
|
||||
* @param string $phpbb_root_path phpBB root path
|
||||
* @param string $phpEx PHP file extension
|
||||
*/
|
||||
public function __construct(config $config, driver_interface $db, dispatcher_interface $phpbb_dispatcher, language $language, user $user, string $phpbb_root_path, string $phpEx)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
parent::__construct($cache, $config, $db, $user);
|
||||
$this->phpbb_dispatcher = $phpbb_dispatcher;
|
||||
$this->language = $language;
|
||||
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $phpEx;
|
||||
$this->config = $config;
|
||||
$this->db = $db;
|
||||
$this->phpbb_dispatcher = $phpbb_dispatcher;
|
||||
$this->user = $user;
|
||||
|
||||
$this->word_length = array('min' => (int) $this->config['fulltext_native_min_chars'], 'max' => (int) $this->config['fulltext_native_max_chars']);
|
||||
|
||||
|
@ -135,44 +129,50 @@ class fulltext_native extends \phpbb\search\base
|
|||
{
|
||||
include($this->phpbb_root_path . 'includes/utf/utf_tools.' . $this->php_ext);
|
||||
}
|
||||
|
||||
$error = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this search backend to be displayed to administrators
|
||||
*
|
||||
* @return string Name
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_name()
|
||||
public function get_name(): string
|
||||
{
|
||||
return 'phpBB Native Fulltext';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the search_query
|
||||
*
|
||||
* @return string search query
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_search_query()
|
||||
public function is_available(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_search_query(): string
|
||||
{
|
||||
return $this->search_query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the common_words array
|
||||
*
|
||||
* @return array common words that are ignored by search backend
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_common_words()
|
||||
public function get_common_words(): array
|
||||
{
|
||||
return $this->common_words;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the word_length array
|
||||
*
|
||||
* @return array min and max word length for searching
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_word_length()
|
||||
{
|
||||
|
@ -180,21 +180,9 @@ class fulltext_native extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* This function fills $this->search_query with the cleaned user search query
|
||||
*
|
||||
* If $terms is 'any' then the words will be extracted from the search query
|
||||
* and combined with | inside brackets. They will afterwards be treated like
|
||||
* an standard search query.
|
||||
*
|
||||
* Then it analyses the query and fills the internal arrays $must_not_contain_ids,
|
||||
* $must_contain_ids and $must_exclude_one_ids which are later used by keyword_search()
|
||||
*
|
||||
* @param string $keywords contains the search query string as entered by the user
|
||||
* @param string $terms is either 'all' (use search query as entered, default words to 'must be contained in post')
|
||||
* or 'any' (find all posts containing at least one of the given words)
|
||||
* @return boolean false if no valid keywords were found and otherwise true
|
||||
*/
|
||||
public function split_keywords($keywords, $terms)
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function split_keywords(string &$keywords, string $terms): bool
|
||||
{
|
||||
$tokens = '+-|()* ';
|
||||
|
||||
|
@ -294,7 +282,7 @@ class fulltext_native extends \phpbb\search\base
|
|||
// We limit the number of allowed keywords to minimize load on the database
|
||||
if ($this->config['max_num_search_keywords'] && $num_keywords > $this->config['max_num_search_keywords'])
|
||||
{
|
||||
trigger_error($this->user->lang('MAX_NUM_SEARCH_KEYWORDS_REFINE', (int) $this->config['max_num_search_keywords'], $num_keywords));
|
||||
trigger_error($this->language->lang('MAX_NUM_SEARCH_KEYWORDS_REFINE', (int) $this->config['max_num_search_keywords'], $num_keywords));
|
||||
}
|
||||
|
||||
// $keywords input format: each word separated by a space, words in a bracket are not separated
|
||||
|
@ -468,7 +456,7 @@ class fulltext_native extends \phpbb\search\base
|
|||
// throw an error if we shall not ignore unexistant words
|
||||
else if (!$ignore_no_id && count($non_common_words))
|
||||
{
|
||||
trigger_error(sprintf($this->user->lang['WORDS_IN_NO_POST'], implode($this->user->lang['COMMA_SEPARATOR'], $non_common_words)));
|
||||
trigger_error(sprintf($this->language->lang('WORDS_IN_NO_POST'), implode($this->language->lang('COMMA_SEPARATOR'), $non_common_words)));
|
||||
}
|
||||
unset($non_common_words);
|
||||
}
|
||||
|
@ -514,26 +502,9 @@ class fulltext_native extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* Performs a search on keywords depending on display specific params. You have to run split_keywords() first
|
||||
*
|
||||
* @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 string $post_visibility specifies which types of posts the user can view in which forums
|
||||
* @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 string $author_name specifies the author match, when ANONYMOUS is also a search-match
|
||||
* @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
|
||||
*/
|
||||
public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $post_visibility, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function keyword_search(string $type, string $fields, string $terms, array $sort_by_sql, string $sort_key, string $sort_dir, string $sort_days, array $ex_fid_ary, string $post_visibility, int $topic_id, array $author_ary, string $author_name, array &$id_ary, int &$start, int $per_page)
|
||||
{
|
||||
// No keywords? No posts.
|
||||
if (empty($this->search_query))
|
||||
|
@ -612,7 +583,7 @@ class fulltext_native extends \phpbb\search\base
|
|||
|
||||
// try reading the results from cache
|
||||
$total_results = 0;
|
||||
if ($this->obtain_ids($search_key, $total_results, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE)
|
||||
if ($this->obtain_ids($search_key, $total_results, $id_ary, $start, $per_page, $sort_dir) == self::SEARCH_RESULT_IN_CACHE)
|
||||
{
|
||||
return $total_results;
|
||||
}
|
||||
|
@ -1016,25 +987,9 @@ class fulltext_native extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* Performs a search on an author's posts without caring about message contents. Depends on display specific params
|
||||
*
|
||||
* @param string $type contains either posts or topics depending on what should be searched for
|
||||
* @param boolean $firstpost_only if true, only topic starting posts will be considered
|
||||
* @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 string $post_visibility specifies which types of posts the user can view in which forums
|
||||
* @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
|
||||
* @param string $author_name specifies the author match, when ANONYMOUS is also a search-match
|
||||
* @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
|
||||
*/
|
||||
public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $post_visibility, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function author_search(string $type, bool $firstpost_only, array $sort_by_sql, string $sort_key, string $sort_dir, string $sort_days, array $ex_fid_ary, string $post_visibility, int $topic_id, array $author_ary, string $author_name, array &$id_ary, int &$start, int $per_page)
|
||||
{
|
||||
// No author? No posts
|
||||
if (!count($author_ary))
|
||||
|
@ -1092,7 +1047,7 @@ class fulltext_native extends \phpbb\search\base
|
|||
|
||||
// try reading the results from cache
|
||||
$total_results = 0;
|
||||
if ($this->obtain_ids($search_key, $total_results, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE)
|
||||
if ($this->obtain_ids($search_key, $total_results, $id_ary, $start, $per_page, $sort_dir) == self::SEARCH_RESULT_IN_CACHE)
|
||||
{
|
||||
return $total_results;
|
||||
}
|
||||
|
@ -1326,91 +1281,17 @@ class fulltext_native extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* Split a text into words of a given length
|
||||
*
|
||||
* The text is converted to UTF-8, cleaned up, and split. Then, words that
|
||||
* conform to the defined length range are returned in an array.
|
||||
*
|
||||
* NOTE: duplicates are NOT removed from the return array
|
||||
*
|
||||
* @param string $text Text to split, encoded in UTF-8
|
||||
* @return array Array of UTF-8 words
|
||||
*/
|
||||
public function split_message($text)
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports_phrase_search(): bool
|
||||
{
|
||||
$match = $words = array();
|
||||
|
||||
/**
|
||||
* Taken from the original code
|
||||
*/
|
||||
// Do not index code
|
||||
$match[] = '#\[code(?:=.*?)?(\:?[0-9a-z]{5,})\].*?\[\/code(\:?[0-9a-z]{5,})\]#is';
|
||||
// BBcode
|
||||
$match[] = '#\[\/?[a-z0-9\*\+\-]+(?:=.*?)?(?::[a-z])?(\:?[0-9a-z]{5,})\]#';
|
||||
|
||||
$min = $this->word_length['min'];
|
||||
|
||||
$isset_min = $min - 1;
|
||||
|
||||
/**
|
||||
* Clean up the string, remove HTML tags, remove BBCodes
|
||||
*/
|
||||
$word = strtok($this->cleanup(preg_replace($match, ' ', strip_tags($text)), -1), ' ');
|
||||
|
||||
while (strlen($word))
|
||||
{
|
||||
if (strlen($word) > 255 || strlen($word) <= $isset_min)
|
||||
{
|
||||
/**
|
||||
* Words longer than 255 bytes are ignored. This will have to be
|
||||
* changed whenever we change the length of search_wordlist.word_text
|
||||
*
|
||||
* Words shorter than $isset_min bytes are ignored, too
|
||||
*/
|
||||
$word = strtok(' ');
|
||||
continue;
|
||||
}
|
||||
|
||||
$len = utf8_strlen($word);
|
||||
|
||||
/**
|
||||
* Test whether the word is too short to be indexed.
|
||||
*
|
||||
* Note that this limit does NOT apply to CJK and Hangul
|
||||
*/
|
||||
if ($len < $min)
|
||||
{
|
||||
/**
|
||||
* Note: this could be optimized. If the codepoint is lower than Hangul's range
|
||||
* we know that it will also be lower than CJK ranges
|
||||
*/
|
||||
if ((strncmp($word, self::UTF8_HANGUL_FIRST, 3) < 0 || strncmp($word, self::UTF8_HANGUL_LAST, 3) > 0)
|
||||
&& (strncmp($word, self::UTF8_CJK_FIRST, 3) < 0 || strncmp($word, self::UTF8_CJK_LAST, 3) > 0)
|
||||
&& (strncmp($word, self::UTF8_CJK_B_FIRST, 4) < 0 || strncmp($word, self::UTF8_CJK_B_LAST, 4) > 0))
|
||||
{
|
||||
$word = strtok(' ');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$words[] = $word;
|
||||
$word = strtok(' ');
|
||||
}
|
||||
|
||||
return $words;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates wordlist and wordmatch tables when a message is posted or changed
|
||||
*
|
||||
* @param string $mode Contains the post mode: edit, post, reply, quote
|
||||
* @param int $post_id The id of the post which is modified/created
|
||||
* @param string &$message New or updated post content
|
||||
* @param string &$subject New or updated post subject
|
||||
* @param int $poster_id Post author's user id
|
||||
* @param int $forum_id The id of the forum in which the post is located
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function index($mode, $post_id, &$message, &$subject, $poster_id, $forum_id)
|
||||
public function index(string $mode, int $post_id, string &$message, string &$subject, int $poster_id, int $forum_id)
|
||||
{
|
||||
if (!$this->config['fulltext_native_load_upd'])
|
||||
{
|
||||
|
@ -1597,9 +1478,9 @@ class fulltext_native extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* Removes entries from the wordmatch table for the specified post_ids
|
||||
*/
|
||||
public function index_remove($post_ids, $author_ids, $forum_ids)
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function index_remove(array $post_ids, array $author_ids, array $forum_ids): void
|
||||
{
|
||||
if (count($post_ids))
|
||||
{
|
||||
|
@ -1654,10 +1535,9 @@ class fulltext_native extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* Tidy up indexes: Tag 'common words' and remove
|
||||
* words no longer referenced in the match table
|
||||
*/
|
||||
public function tidy()
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function tidy(): void
|
||||
{
|
||||
// Is the fulltext indexer disabled? If yes then we need not
|
||||
// carry on ... it's okay ... I know when I'm not wanted boo hoo
|
||||
|
@ -1717,10 +1597,12 @@ class fulltext_native extends \phpbb\search\base
|
|||
$this->config->set('search_last_gc', time(), false);
|
||||
}
|
||||
|
||||
// create_index is inherited from base.php
|
||||
|
||||
/**
|
||||
* Deletes all words from the index
|
||||
*/
|
||||
public function delete_index($acp_module, $u_action)
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete_index(int &$post_counter = null): ?array
|
||||
{
|
||||
$sql_queries = [];
|
||||
|
||||
|
@ -1759,24 +1641,26 @@ class fulltext_native extends \phpbb\search\base
|
|||
{
|
||||
$this->db->sql_query($sql_query);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if both FULLTEXT indexes exist
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function index_created()
|
||||
public function index_created(): bool
|
||||
{
|
||||
if (!count($this->stats))
|
||||
{
|
||||
$this->get_stats();
|
||||
}
|
||||
|
||||
return ($this->stats['total_words'] && $this->stats['total_matches']) ? true : false;
|
||||
return $this->stats['total_words'] && $this->stats['total_matches'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an associative array containing information about the indexes
|
||||
*/
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function index_stats()
|
||||
{
|
||||
if (!count($this->stats))
|
||||
|
@ -1785,16 +1669,94 @@ class fulltext_native extends \phpbb\search\base
|
|||
}
|
||||
|
||||
return array(
|
||||
$this->user->lang['TOTAL_WORDS'] => $this->stats['total_words'],
|
||||
$this->user->lang['TOTAL_MATCHES'] => $this->stats['total_matches']);
|
||||
$this->language->lang('TOTAL_WORDS') => $this->stats['total_words'],
|
||||
$this->language->lang('TOTAL_MATCHES') => $this->stats['total_matches']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the stats and store them in the $this->stats associative array
|
||||
*/
|
||||
protected function get_stats()
|
||||
{
|
||||
$this->stats['total_words'] = $this->db->get_estimated_row_count(SEARCH_WORDLIST_TABLE);
|
||||
$this->stats['total_matches'] = $this->db->get_estimated_row_count(SEARCH_WORDMATCH_TABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a text into words of a given length
|
||||
*
|
||||
* The text is converted to UTF-8, cleaned up, and split. Then, words that
|
||||
* conform to the defined length range are returned in an array.
|
||||
*
|
||||
* NOTE: duplicates are NOT removed from the return array
|
||||
*
|
||||
* @param string $text Text to split, encoded in UTF-8
|
||||
* @return array Array of UTF-8 words
|
||||
*/
|
||||
protected function split_message($text)
|
||||
{
|
||||
$match = $words = array();
|
||||
|
||||
/**
|
||||
* Taken from the original code
|
||||
*/
|
||||
// Do not index code
|
||||
$match[] = '#\[code(?:=.*?)?(\:?[0-9a-z]{5,})\].*?\[\/code(\:?[0-9a-z]{5,})\]#is';
|
||||
// BBcode
|
||||
$match[] = '#\[\/?[a-z0-9\*\+\-]+(?:=.*?)?(?::[a-z])?(\:?[0-9a-z]{5,})\]#';
|
||||
|
||||
$min = $this->word_length['min'];
|
||||
|
||||
$isset_min = $min - 1;
|
||||
|
||||
/**
|
||||
* Clean up the string, remove HTML tags, remove BBCodes
|
||||
*/
|
||||
$word = strtok($this->cleanup(preg_replace($match, ' ', strip_tags($text)), -1), ' ');
|
||||
|
||||
while (strlen($word))
|
||||
{
|
||||
if (strlen($word) > 255 || strlen($word) <= $isset_min)
|
||||
{
|
||||
/**
|
||||
* Words longer than 255 bytes are ignored. This will have to be
|
||||
* changed whenever we change the length of search_wordlist.word_text
|
||||
*
|
||||
* Words shorter than $isset_min bytes are ignored, too
|
||||
*/
|
||||
$word = strtok(' ');
|
||||
continue;
|
||||
}
|
||||
|
||||
$len = utf8_strlen($word);
|
||||
|
||||
/**
|
||||
* Test whether the word is too short to be indexed.
|
||||
*
|
||||
* Note that this limit does NOT apply to CJK and Hangul
|
||||
*/
|
||||
if ($len < $min)
|
||||
{
|
||||
/**
|
||||
* Note: this could be optimized. If the codepoint is lower than Hangul's range
|
||||
* we know that it will also be lower than CJK ranges
|
||||
*/
|
||||
if ((strncmp($word, self::UTF8_HANGUL_FIRST, 3) < 0 || strncmp($word, self::UTF8_HANGUL_LAST, 3) > 0)
|
||||
&& (strncmp($word, self::UTF8_CJK_FIRST, 3) < 0 || strncmp($word, self::UTF8_CJK_LAST, 3) > 0)
|
||||
&& (strncmp($word, self::UTF8_CJK_B_FIRST, 4) < 0 || strncmp($word, self::UTF8_CJK_B_LAST, 4) > 0))
|
||||
{
|
||||
$word = strtok(' ');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$words[] = $word;
|
||||
$word = strtok(' ');
|
||||
}
|
||||
|
||||
return $words;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up a text to remove non-alphanumeric characters
|
||||
*
|
||||
|
@ -2032,9 +1994,9 @@ class fulltext_native extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a list of options for the ACP to display
|
||||
*/
|
||||
public function acp()
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_acp_options(): array
|
||||
{
|
||||
/**
|
||||
* if we need any options, copied from fulltext_native for now, will have to be adjusted or removed
|
||||
|
@ -2042,19 +2004,19 @@ class fulltext_native extends \phpbb\search\base
|
|||
|
||||
$tpl = '
|
||||
<dl>
|
||||
<dt><label for="fulltext_native_load_upd">' . $this->user->lang['YES_SEARCH_UPDATE'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['YES_SEARCH_UPDATE_EXPLAIN'] . '</span></dt>
|
||||
<dd><label><input type="radio" id="fulltext_native_load_upd" name="config[fulltext_native_load_upd]" value="1"' . (($this->config['fulltext_native_load_upd']) ? ' checked="checked"' : '') . ' class="radio" /> ' . $this->user->lang['YES'] . '</label><label><input type="radio" name="config[fulltext_native_load_upd]" value="0"' . ((!$this->config['fulltext_native_load_upd']) ? ' checked="checked"' : '') . ' class="radio" /> ' . $this->user->lang['NO'] . '</label></dd>
|
||||
<dt><label for="fulltext_native_load_upd">' . $this->language->lang('YES_SEARCH_UPDATE') . $this->language->lang('COLON') . '</label><br /><span>' . $this->language->lang('YES_SEARCH_UPDATE_EXPLAIN') . '</span></dt>
|
||||
<dd><label><input type="radio" id="fulltext_native_load_upd" name="config[fulltext_native_load_upd]" value="1"' . (($this->config['fulltext_native_load_upd']) ? ' checked="checked"' : '') . ' class="radio" /> ' . $this->language->lang('YES') . '</label><label><input type="radio" name="config[fulltext_native_load_upd]" value="0"' . ((!$this->config['fulltext_native_load_upd']) ? ' checked="checked"' : '') . ' class="radio" /> ' . $this->language->lang('NO') . '</label></dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt><label for="fulltext_native_min_chars">' . $this->user->lang['MIN_SEARCH_CHARS'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['MIN_SEARCH_CHARS_EXPLAIN'] . '</span></dt>
|
||||
<dt><label for="fulltext_native_min_chars">' . $this->language->lang('MIN_SEARCH_CHARS') . $this->language->lang('COLON') . '</label><br /><span>' . $this->language->lang('MIN_SEARCH_CHARS_EXPLAIN') . '</span></dt>
|
||||
<dd><input id="fulltext_native_min_chars" type="number" min="0" max="255" name="config[fulltext_native_min_chars]" value="' . (int) $this->config['fulltext_native_min_chars'] . '" /></dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt><label for="fulltext_native_max_chars">' . $this->user->lang['MAX_SEARCH_CHARS'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['MAX_SEARCH_CHARS_EXPLAIN'] . '</span></dt>
|
||||
<dt><label for="fulltext_native_max_chars">' . $this->language->lang('MAX_SEARCH_CHARS') . $this->language->lang('COLON') . '</label><br /><span>' . $this->language->lang('MAX_SEARCH_CHARS_EXPLAIN') . '</span></dt>
|
||||
<dd><input id="fulltext_native_max_chars" type="number" min="0" max="255" name="config[fulltext_native_max_chars]" value="' . (int) $this->config['fulltext_native_max_chars'] . '" /></dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt><label for="fulltext_native_common_thres">' . $this->user->lang['COMMON_WORD_THRESHOLD'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['COMMON_WORD_THRESHOLD_EXPLAIN'] . '</span></dt>
|
||||
<dt><label for="fulltext_native_common_thres">' . $this->language->lang('COMMON_WORD_THRESHOLD') . $this->language->lang('COLON') . '</label><br /><span>' . $this->language->lang('COMMON_WORD_THRESHOLD_EXPLAIN') . '</span></dt>
|
||||
<dd><input id="fulltext_native_common_thres" type="text" name="config[fulltext_native_common_thres]" value="' . (double) $this->config['fulltext_native_common_thres'] . '" /> %</dd>
|
||||
</dl>
|
||||
';
|
|
@ -11,12 +11,19 @@
|
|||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\search;
|
||||
namespace phpbb\search\backend;
|
||||
|
||||
use phpbb\config\config;
|
||||
use phpbb\db\driver\driver_interface;
|
||||
use phpbb\event\dispatcher_interface;
|
||||
use phpbb\language\language;
|
||||
use phpbb\user;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Fulltext search for PostgreSQL
|
||||
*/
|
||||
class fulltext_postgres extends \phpbb\search\base
|
||||
class fulltext_postgres extends base implements search_backend_interface
|
||||
{
|
||||
/**
|
||||
* Associative array holding index stats
|
||||
|
@ -43,30 +50,16 @@ class fulltext_postgres extends \phpbb\search\base
|
|||
*/
|
||||
protected $phrase_search = false;
|
||||
|
||||
/**
|
||||
* Config object
|
||||
* @var \phpbb\config\config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Database connection
|
||||
* @var \phpbb\db\driver\driver_interface
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* phpBB event dispatcher object
|
||||
* @var \phpbb\event\dispatcher_interface
|
||||
* @var dispatcher_interface
|
||||
*/
|
||||
protected $phpbb_dispatcher;
|
||||
|
||||
/**
|
||||
* User object
|
||||
* @var \phpbb\user
|
||||
* @var language
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
protected $language;
|
||||
/**
|
||||
* Contains tidied search query.
|
||||
* Operators are prefixed in search query and common words excluded
|
||||
|
@ -89,23 +82,23 @@ class fulltext_postgres extends \phpbb\search\base
|
|||
|
||||
/**
|
||||
* Constructor
|
||||
* Creates a new \phpbb\search\fulltext_postgres, which is used as a search backend
|
||||
* Creates a new \phpbb\search\backend\fulltext_postgres, which is used as a search backend
|
||||
*
|
||||
* @param string|bool $error Any error that occurs is passed on through this reference variable otherwise false
|
||||
* @param config $config Config object
|
||||
* @param driver_interface $db Database object
|
||||
* @param dispatcher_interface $phpbb_dispatcher Event dispatcher object
|
||||
* @param language $language
|
||||
* @param user $user User object
|
||||
* @param string $phpbb_root_path Relative path to phpBB root
|
||||
* @param string $phpEx PHP file extension
|
||||
* @param \phpbb\auth\auth $auth Auth object
|
||||
* @param \phpbb\config\config $config Config object
|
||||
* @param \phpbb\db\driver\driver_interface Database object
|
||||
* @param \phpbb\user $user User object
|
||||
* @param \phpbb\event\dispatcher_interface $phpbb_dispatcher Event dispatcher object
|
||||
*/
|
||||
public function __construct(&$error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher)
|
||||
public function __construct(config $config, driver_interface $db, dispatcher_interface $phpbb_dispatcher, language $language, user $user, string $phpbb_root_path, string $phpEx)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->db = $db;
|
||||
global $cache;
|
||||
|
||||
parent::__construct($cache, $config, $db, $user);
|
||||
$this->phpbb_dispatcher = $phpbb_dispatcher;
|
||||
$this->user = $user;
|
||||
$this->language = $language;
|
||||
|
||||
$this->word_length = array('min' => $this->config['fulltext_postgres_min_word_len'], 'max' => $this->config['fulltext_postgres_max_word_len']);
|
||||
|
||||
|
@ -116,44 +109,55 @@ class fulltext_postgres extends \phpbb\search\base
|
|||
{
|
||||
include($phpbb_root_path . 'includes/utf/utf_tools.' . $phpEx);
|
||||
}
|
||||
|
||||
$error = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this search backend to be displayed to administrators
|
||||
*
|
||||
* @return string Name
|
||||
*/
|
||||
public function get_name()
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_name(): string
|
||||
{
|
||||
return 'PostgreSQL Fulltext';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the search_query
|
||||
*
|
||||
* @return string search query
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_search_query()
|
||||
public function is_available(): bool
|
||||
{
|
||||
return $this->db->get_sql_layer() == 'postgres';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
if (!$this->is_available())
|
||||
{
|
||||
return $this->language->lang('FULLTEXT_POSTGRES_INCOMPATIBLE_DATABASE');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_search_query(): string
|
||||
{
|
||||
return $this->search_query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the common_words array
|
||||
*
|
||||
* @return array common words that are ignored by search backend
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_common_words()
|
||||
public function get_common_words(): array
|
||||
{
|
||||
return $this->common_words;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the word_length array
|
||||
*
|
||||
* @return array min and max word length for searching
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_word_length()
|
||||
{
|
||||
|
@ -161,39 +165,9 @@ class fulltext_postgres extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns if phrase search is supported or not
|
||||
*
|
||||
* @return bool
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports_phrase_search()
|
||||
{
|
||||
return $this->phrase_search;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for correct PostgreSQL version and stores min/max word length in the config
|
||||
*
|
||||
* @return string|bool Language key of the error/incompatibility occurred
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
if ($this->db->get_sql_layer() != 'postgres')
|
||||
{
|
||||
return $this->user->lang['FULLTEXT_POSTGRES_INCOMPATIBLE_DATABASE'];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits keywords entered by a user into an array of words stored in $this->split_words
|
||||
* Stores the tidied search query in $this->search_query
|
||||
*
|
||||
* @param string &$keywords Contains the keyword as entered by the user
|
||||
* @param string $terms is either 'all' or 'any'
|
||||
* @return bool false if no valid keywords were found and otherwise true
|
||||
*/
|
||||
public function split_keywords(&$keywords, $terms)
|
||||
public function split_keywords(string &$keywords, string $terms): bool
|
||||
{
|
||||
if ($terms == 'all')
|
||||
{
|
||||
|
@ -280,53 +254,11 @@ class fulltext_postgres extends \phpbb\search\base
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns text into an array of words
|
||||
* @param string $text contains post text/subject
|
||||
*/
|
||||
public function split_message($text)
|
||||
{
|
||||
// Split words
|
||||
$text = preg_replace('#([^\p{L}\p{N}\'*])#u', '$1$1', str_replace('\'\'', '\' \'', trim($text)));
|
||||
$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];
|
||||
|
||||
// remove too short or too long words
|
||||
$text = array_values($text);
|
||||
for ($i = 0, $n = count($text); $i < $n; $i++)
|
||||
{
|
||||
$text[$i] = trim($text[$i]);
|
||||
if (utf8_strlen($text[$i]) < $this->config['fulltext_postgres_min_word_len'] || utf8_strlen($text[$i]) > $this->config['fulltext_postgres_max_word_len'])
|
||||
{
|
||||
unset($text[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a search on keywords depending on display specific params. You have to run split_keywords() first
|
||||
*
|
||||
* @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 string $post_visibility specifies which types of posts the user can view in which forums
|
||||
* @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 string $author_name specifies the author match, when ANONYMOUS is also a search-match
|
||||
* @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
|
||||
*/
|
||||
public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $post_visibility, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function keyword_search(string $type, string $fields, string $terms, array $sort_by_sql, string $sort_key, string $sort_dir, string $sort_days, array $ex_fid_ary, string $post_visibility, int $topic_id, array $author_ary, string $author_name, array &$id_ary, int &$start, int $per_page)
|
||||
{
|
||||
// No keywords? No posts
|
||||
if (!$this->search_query)
|
||||
|
@ -355,21 +287,21 @@ class fulltext_postgres extends \phpbb\search\base
|
|||
);
|
||||
|
||||
/**
|
||||
* Allow changing the search_key for cached results
|
||||
*
|
||||
* @event core.search_postgres_by_keyword_modify_search_key
|
||||
* @var array search_key_array Array with search parameters to generate the search_key
|
||||
* @var string type Searching type ('posts', 'topics')
|
||||
* @var string fields Searching fields ('titleonly', 'msgonly', 'firstpost', 'all')
|
||||
* @var string terms Searching terms ('all', 'any')
|
||||
* @var int sort_days Time, in days, of the oldest possible post to list
|
||||
* @var string sort_key The sort type used from the possible sort types
|
||||
* @var int topic_id Limit the search to this topic_id only
|
||||
* @var array ex_fid_ary Which forums not to search on
|
||||
* @var string post_visibility Post visibility data
|
||||
* @var array author_ary Array of user_id containing the users to filter the results to
|
||||
* @since 3.1.7-RC1
|
||||
*/
|
||||
* Allow changing the search_key for cached results
|
||||
*
|
||||
* @event core.search_postgres_by_keyword_modify_search_key
|
||||
* @var array search_key_array Array with search parameters to generate the search_key
|
||||
* @var string type Searching type ('posts', 'topics')
|
||||
* @var string fields Searching fields ('titleonly', 'msgonly', 'firstpost', 'all')
|
||||
* @var string terms Searching terms ('all', 'any')
|
||||
* @var int sort_days Time, in days, of the oldest possible post to list
|
||||
* @var string sort_key The sort type used from the possible sort types
|
||||
* @var int topic_id Limit the search to this topic_id only
|
||||
* @var array ex_fid_ary Which forums not to search on
|
||||
* @var string post_visibility Post visibility data
|
||||
* @var array author_ary Array of user_id containing the users to filter the results to
|
||||
* @since 3.1.7-RC1
|
||||
*/
|
||||
$vars = array(
|
||||
'search_key_array',
|
||||
'type',
|
||||
|
@ -393,7 +325,7 @@ class fulltext_postgres extends \phpbb\search\base
|
|||
|
||||
// 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)
|
||||
if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == self::SEARCH_RESULT_IN_CACHE)
|
||||
{
|
||||
return $result_count;
|
||||
}
|
||||
|
@ -430,53 +362,53 @@ class fulltext_postgres extends \phpbb\search\base
|
|||
$sql_match = 'p.post_subject';
|
||||
$sql_match_where = ' AND p.post_id = t.topic_first_post_id';
|
||||
$join_topic = true;
|
||||
break;
|
||||
break;
|
||||
|
||||
case 'msgonly':
|
||||
$sql_match = 'p.post_text';
|
||||
$sql_match_where = '';
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
|
||||
default:
|
||||
$sql_match = 'p.post_subject, p.post_text';
|
||||
$sql_match_where = '';
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
$tsearch_query = $this->tsearch_query;
|
||||
|
||||
/**
|
||||
* Allow changing the query used to search for posts using fulltext_postgres
|
||||
*
|
||||
* @event core.search_postgres_keywords_main_query_before
|
||||
* @var string tsearch_query The parsed keywords used for this search
|
||||
* @var int result_count The previous result count for the format of the query.
|
||||
* Set to 0 to force a re-count
|
||||
* @var bool join_topic Weather or not TOPICS_TABLE should be CROSS JOIN'ED
|
||||
* @var array author_ary Array of user_id containing the users to filter the results to
|
||||
* @var string author_name An extra username to search on (!empty(author_ary) must be true, to be relevant)
|
||||
* @var array ex_fid_ary Which forums not to search on
|
||||
* @var int topic_id Limit the search to this topic_id only
|
||||
* @var string sql_sort_table Extra tables to include in the SQL query.
|
||||
* Used in conjunction with sql_sort_join
|
||||
* @var string sql_sort_join SQL conditions to join all the tables used together.
|
||||
* Used in conjunction with sql_sort_table
|
||||
* @var int sort_days Time, in days, of the oldest possible post to list
|
||||
* @var string sql_match Which columns to do the search on.
|
||||
* @var string sql_match_where Extra conditions to use to properly filter the matching process
|
||||
* @var string sort_by_sql The possible predefined sort types
|
||||
* @var string sort_key The sort type used from the possible sort types
|
||||
* @var string sort_dir "a" for ASC or "d" dor DESC for the sort order used
|
||||
* @var string sql_sort The result SQL when processing sort_by_sql + sort_key + sort_dir
|
||||
* @var int start How many posts to skip in the search results (used for pagination)
|
||||
* @since 3.1.5-RC1
|
||||
*/
|
||||
* Allow changing the query used to search for posts using fulltext_postgres
|
||||
*
|
||||
* @event core.search_postgres_keywords_main_query_before
|
||||
* @var string tsearch_query The parsed keywords used for this search
|
||||
* @var int result_count The previous result count for the format of the query.
|
||||
* Set to 0 to force a re-count
|
||||
* @var bool join_topic Weather or not TOPICS_TABLE should be CROSS JOIN'ED
|
||||
* @var array author_ary Array of user_id containing the users to filter the results to
|
||||
* @var string author_name An extra username to search on (!empty(author_ary) must be true, to be relevant)
|
||||
* @var array ex_fid_ary Which forums not to search on
|
||||
* @var int topic_id Limit the search to this topic_id only
|
||||
* @var string sql_sort_table Extra tables to include in the SQL query.
|
||||
* Used in conjunction with sql_sort_join
|
||||
* @var string sql_sort_join SQL conditions to join all the tables used together.
|
||||
* Used in conjunction with sql_sort_table
|
||||
* @var int sort_days Time, in days, of the oldest possible post to list
|
||||
* @var string sql_match Which columns to do the search on.
|
||||
* @var string sql_match_where Extra conditions to use to properly filter the matching process
|
||||
* @var string sort_by_sql The possible predefined sort types
|
||||
* @var string sort_key The sort type used from the possible sort types
|
||||
* @var string sort_dir "a" for ASC or "d" dor DESC for the sort order used
|
||||
* @var string sql_sort The result SQL when processing sort_by_sql + sort_key + sort_dir
|
||||
* @var int start How many posts to skip in the search results (used for pagination)
|
||||
* @since 3.1.5-RC1
|
||||
*/
|
||||
$vars = array(
|
||||
'tsearch_query',
|
||||
'result_count',
|
||||
|
@ -588,25 +520,9 @@ class fulltext_postgres extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* Performs a search on an author's posts without caring about message contents. Depends on display specific params
|
||||
*
|
||||
* @param string $type contains either posts or topics depending on what should be searched for
|
||||
* @param boolean $firstpost_only if true, only topic starting posts will be considered
|
||||
* @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 string $post_visibility specifies which types of posts the user can view in which forums
|
||||
* @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
|
||||
* @param string $author_name specifies the author match, when ANONYMOUS is also a search-match
|
||||
* @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
|
||||
*/
|
||||
public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $post_visibility, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function author_search(string $type, bool $firstpost_only, array $sort_by_sql, string $sort_key, string $sort_dir, string $sort_days, array $ex_fid_ary, string $post_visibility, int $topic_id, array $author_ary, string $author_name, array &$id_ary, int &$start, int $per_page)
|
||||
{
|
||||
// No author? No posts
|
||||
if (!count($author_ary))
|
||||
|
@ -669,7 +585,7 @@ class fulltext_postgres extends \phpbb\search\base
|
|||
|
||||
// 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)
|
||||
if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == self::SEARCH_RESULT_IN_CACHE)
|
||||
{
|
||||
return $result_count;
|
||||
}
|
||||
|
@ -872,16 +788,17 @@ class fulltext_postgres extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* 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 ...
|
||||
* @param int $post_id contains the post id of the post to index
|
||||
* @param string $message contains the post text of the post
|
||||
* @param string $subject contains the subject of the post to index
|
||||
* @param int $poster_id contains the user id of the poster
|
||||
* @param int $forum_id contains the forum id of parent forum of the post
|
||||
*/
|
||||
public function index($mode, $post_id, &$message, &$subject, $poster_id, $forum_id)
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports_phrase_search(): bool
|
||||
{
|
||||
return $this->phrase_search;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function index(string $mode, int $post_id, string &$message, string &$subject, int $poster_id, int $forum_id)
|
||||
{
|
||||
// Split old and new post/subject to obtain array of words
|
||||
$split_text = $this->split_message($message);
|
||||
|
@ -927,17 +844,17 @@ class fulltext_postgres extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* Destroy cached results, that might be outdated after deleting a post
|
||||
*/
|
||||
public function index_remove($post_ids, $author_ids, $forum_ids)
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function index_remove(array $post_ids, array $author_ids, array $forum_ids): void
|
||||
{
|
||||
$this->destroy_cache(array(), $author_ids);
|
||||
$this->destroy_cache([], $author_ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy old cache entries
|
||||
*/
|
||||
public function tidy()
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function tidy(): void
|
||||
{
|
||||
// destroy too old cached search results
|
||||
$this->destroy_cache(array());
|
||||
|
@ -946,16 +863,14 @@ class fulltext_postgres extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* Create fulltext index
|
||||
*
|
||||
* @return string|bool error string is returned incase of errors otherwise false
|
||||
*/
|
||||
public function create_index($acp_module, $u_action)
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function create_index(int &$post_counter = 0): ?array
|
||||
{
|
||||
// Make sure we can actually use PostgreSQL with fulltext indexes
|
||||
if ($error = $this->init())
|
||||
{
|
||||
return $error;
|
||||
throw new RuntimeException($error);
|
||||
}
|
||||
|
||||
if (empty($this->stats))
|
||||
|
@ -1003,20 +918,18 @@ class fulltext_postgres extends \phpbb\search\base
|
|||
|
||||
$this->db->sql_query('TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE);
|
||||
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop fulltext index
|
||||
*
|
||||
* @return string|bool error string is returned incase of errors otherwise false
|
||||
*/
|
||||
public function delete_index($acp_module, $u_action)
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete_index(int &$post_counter = null): ?array
|
||||
{
|
||||
// Make sure we can actually use PostgreSQL with fulltext indexes
|
||||
if ($error = $this->init())
|
||||
{
|
||||
return $error;
|
||||
throw new RuntimeException($error);
|
||||
}
|
||||
|
||||
if (empty($this->stats))
|
||||
|
@ -1064,13 +977,13 @@ class fulltext_postgres extends \phpbb\search\base
|
|||
|
||||
$this->db->sql_query('TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE);
|
||||
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if both FULLTEXT indexes exist
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function index_created()
|
||||
public function index_created(): bool
|
||||
{
|
||||
if (empty($this->stats))
|
||||
{
|
||||
|
@ -1081,7 +994,7 @@ class fulltext_postgres extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns an associative array containing information about the indexes
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function index_stats()
|
||||
{
|
||||
|
@ -1091,12 +1004,12 @@ class fulltext_postgres extends \phpbb\search\base
|
|||
}
|
||||
|
||||
return array(
|
||||
$this->user->lang['FULLTEXT_POSTGRES_TOTAL_POSTS'] => ($this->index_created()) ? $this->stats['total_posts'] : 0,
|
||||
$this->language->lang('FULLTEXT_POSTGRES_TOTAL_POSTS') => ($this->index_created()) ? $this->stats['total_posts'] : 0,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the stats and store them in the $this->stats associative array
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function get_stats()
|
||||
{
|
||||
|
@ -1139,19 +1052,44 @@ class fulltext_postgres extends \phpbb\search\base
|
|||
}
|
||||
|
||||
/**
|
||||
* Display various options that can be configured for the backend from the acp
|
||||
*
|
||||
* @return associative array containing template and config variables
|
||||
*/
|
||||
public function acp()
|
||||
* Turns text into an array of words
|
||||
* @param string $text contains post text/subject
|
||||
* @return array
|
||||
*/
|
||||
protected function split_message($text)
|
||||
{
|
||||
// Split words
|
||||
$text = preg_replace('#([^\p{L}\p{N}\'*])#u', '$1$1', str_replace('\'\'', '\' \'', trim($text)));
|
||||
$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];
|
||||
|
||||
// remove too short or too long words
|
||||
$text = array_values($text);
|
||||
for ($i = 0, $n = count($text); $i < $n; $i++)
|
||||
{
|
||||
$text[$i] = trim($text[$i]);
|
||||
if (utf8_strlen($text[$i]) < $this->config['fulltext_postgres_min_word_len'] || utf8_strlen($text[$i]) > $this->config['fulltext_postgres_max_word_len'])
|
||||
{
|
||||
unset($text[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_acp_options(): array
|
||||
{
|
||||
$tpl = '
|
||||
<dl>
|
||||
<dt><label>' . $this->user->lang['FULLTEXT_POSTGRES_VERSION_CHECK'] . '</label><br /><span>' . $this->user->lang['FULLTEXT_POSTGRES_VERSION_CHECK_EXPLAIN'] . '</span></dt>
|
||||
<dd>' . (($this->db->get_sql_layer() == 'postgres') ? $this->user->lang['YES'] : $this->user->lang['NO']) . '</dd>
|
||||
<dt><label>' . $this->language->lang('FULLTEXT_POSTGRES_VERSION_CHECK') . '</label><br /><span>' . $this->language->lang('FULLTEXT_POSTGRES_VERSION_CHECK_EXPLAIN') . '</span></dt>
|
||||
<dd>' . (($this->db->get_sql_layer() == 'postgres') ? $this->language->lang('YES') : $this->language->lang('NO')) . '</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt><label>' . $this->user->lang['FULLTEXT_POSTGRES_TS_NAME'] . '</label><br /><span>' . $this->user->lang['FULLTEXT_POSTGRES_TS_NAME_EXPLAIN'] . '</span></dt>
|
||||
<dt><label>' . $this->language->lang('FULLTEXT_POSTGRES_TS_NAME') . '</label><br /><span>' . $this->language->lang('FULLTEXT_POSTGRES_TS_NAME_EXPLAIN') . '</span></dt>
|
||||
<dd><select name="config[fulltext_postgres_ts_name]">';
|
||||
|
||||
if ($this->db->get_sql_layer() == 'postgres')
|
||||
|
@ -1174,11 +1112,11 @@ class fulltext_postgres extends \phpbb\search\base
|
|||
$tpl .= '</select></dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt><label for="fulltext_postgres_min_word_len">' . $this->user->lang['FULLTEXT_POSTGRES_MIN_WORD_LEN'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['FULLTEXT_POSTGRES_MIN_WORD_LEN_EXPLAIN'] . '</span></dt>
|
||||
<dt><label for="fulltext_postgres_min_word_len">' . $this->language->lang('FULLTEXT_POSTGRES_MIN_WORD_LEN') . $this->language->lang('COLON') . '</label><br /><span>' . $this->language->lang('FULLTEXT_POSTGRES_MIN_WORD_LEN_EXPLAIN') . '</span></dt>
|
||||
<dd><input id="fulltext_postgres_min_word_len" type="number" min="0" max="255" name="config[fulltext_postgres_min_word_len]" value="' . (int) $this->config['fulltext_postgres_min_word_len'] . '" /></dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt><label for="fulltext_postgres_max_word_len">' . $this->user->lang['FULLTEXT_POSTGRES_MAX_WORD_LEN'] . $this->user->lang['COLON'] . '</label><br /><span>' . $this->user->lang['FULLTEXT_POSTGRES_MAX_WORD_LEN_EXPLAIN'] . '</span></dt>
|
||||
<dt><label for="fulltext_postgres_max_word_len">' . $this->language->lang('FULLTEXT_POSTGRES_MAX_WORD_LEN') . $this->language->lang('COLON') . '</label><br /><span>' . $this->language->lang('FULLTEXT_POSTGRES_MAX_WORD_LEN_EXPLAIN') . '</span></dt>
|
||||
<dd><input id="fulltext_postgres_max_word_len" type="number" min="0" max="255" name="config[fulltext_postgres_max_word_len]" value="' . (int) $this->config['fulltext_postgres_max_word_len'] . '" /></dd>
|
||||
</dl>
|
||||
';
|
File diff suppressed because it is too large
Load diff
196
phpBB/phpbb/search/backend/search_backend_interface.php
Normal file
196
phpBB/phpbb/search/backend/search_backend_interface.php
Normal file
|
@ -0,0 +1,196 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\search\backend;
|
||||
|
||||
interface search_backend_interface
|
||||
{
|
||||
/**
|
||||
* Returns the name of this search backend to be displayed to administrators
|
||||
*
|
||||
* @return string Name
|
||||
*/
|
||||
public function get_name(): string;
|
||||
|
||||
/**
|
||||
* Returns if the search engine is available
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_available(): bool;
|
||||
|
||||
/**
|
||||
* Method executed when a search backend is set from acp.
|
||||
*
|
||||
* Checks permissions and paths, if everything is correct it generates the config file
|
||||
*
|
||||
* @return string|false False if everything was ok or string with error message
|
||||
*/
|
||||
public function init();
|
||||
|
||||
/**
|
||||
* Returns the search_query
|
||||
*
|
||||
* @return string search query
|
||||
*/
|
||||
public function get_search_query(): string;
|
||||
|
||||
/**
|
||||
* Returns the common_words array
|
||||
*
|
||||
* @return array common words that are ignored by search backend
|
||||
*/
|
||||
public function get_common_words(): array;
|
||||
|
||||
/**
|
||||
* Returns the word_length array
|
||||
*
|
||||
* @return array|false min and max word length for searching
|
||||
*/
|
||||
public function get_word_length();
|
||||
|
||||
/**
|
||||
* Splits keywords entered by a user into an array of words stored in $this->split_words
|
||||
* This function fills $this->search_query with the cleaned user search query
|
||||
*
|
||||
* If $terms is 'any' then the words will be extracted from the search query
|
||||
* and combined with | inside brackets. They will afterwards be treated like
|
||||
* an standard search query.
|
||||
*
|
||||
* Then it analyses the query and fills the internal arrays $must_not_contain_ids,
|
||||
* $must_contain_ids and $must_exclude_one_ids which are later used by keyword_search()
|
||||
*
|
||||
* @param string $keywords contains the search query string as entered by the user
|
||||
* @param string $terms is either 'all' (use search query as entered, default words to 'must be contained in post')
|
||||
* or 'any' (find all posts containing at least one of the given words)
|
||||
* @return boolean false if no valid keywords were found and otherwise true
|
||||
*/
|
||||
public function split_keywords(string &$keywords, string $terms): bool;
|
||||
|
||||
/**
|
||||
* Performs a search on keywords depending on display specific params. You have to run split_keywords() first
|
||||
*
|
||||
* @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 string $post_visibility specifies which types of posts the user can view in which forums
|
||||
* @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 string $author_name specifies the author match, when ANONYMOUS is also a search-match
|
||||
* @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
|
||||
*/
|
||||
public function keyword_search(string $type, string $fields, string $terms, array $sort_by_sql, string $sort_key, string $sort_dir, string $sort_days, array $ex_fid_ary, string $post_visibility, int $topic_id, array $author_ary, string $author_name, array &$id_ary, int &$start, int $per_page);
|
||||
|
||||
/**
|
||||
* Performs a search on an author's posts without caring about message contents. Depends on display specific params
|
||||
*
|
||||
* @param string $type contains either posts or topics depending on what should be searched for
|
||||
* @param boolean $firstpost_only if true, only topic starting posts will be considered
|
||||
* @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 string $post_visibility specifies which types of posts the user can view in which forums
|
||||
* @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
|
||||
* @param string $author_name specifies the author match, when ANONYMOUS is also a search-match
|
||||
* @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
|
||||
*/
|
||||
public function author_search(string $type, bool $firstpost_only, array $sort_by_sql, string $sort_key, string $sort_dir, string $sort_days, array $ex_fid_ary, string $post_visibility, int $topic_id, array $author_ary, string $author_name, array &$id_ary, int &$start, int $per_page);
|
||||
|
||||
/**
|
||||
* Returns if phrase search is supported or not
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_phrase_search(): bool;
|
||||
|
||||
/**
|
||||
* Updates wordlist and wordmatch tables when a message is posted or changed
|
||||
* 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 ...
|
||||
* @param int $post_id contains the post id of the post to index
|
||||
* @param string $message contains the post text of the post
|
||||
* @param string $subject contains the subject of the post to index
|
||||
* @param int $poster_id contains the user id of the poster
|
||||
* @param int $forum_id contains the forum id of parent forum of the post
|
||||
*/
|
||||
public function index(string $mode, int $post_id, string &$message, string &$subject, int $poster_id, int $forum_id);
|
||||
|
||||
/**
|
||||
* Destroy cached results, that might be outdated after deleting a post
|
||||
* @param array $post_ids
|
||||
* @param array $author_ids
|
||||
* @param array $forum_ids
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function index_remove(array $post_ids, array $author_ids, array $forum_ids): void;
|
||||
|
||||
/**
|
||||
* Destroy old cache entries
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function tidy(): void;
|
||||
|
||||
/**
|
||||
* Create fulltext index
|
||||
*
|
||||
* @param int $post_counter
|
||||
* @return array|null array with current status or null if finished
|
||||
*/
|
||||
public function create_index(int &$post_counter = 0): ?array;
|
||||
|
||||
/**
|
||||
* Drop fulltext index
|
||||
*
|
||||
* @param int $post_counter
|
||||
* @return array|null array with current status or null if finished
|
||||
*/
|
||||
public function delete_index(int &$post_counter = 0): ?array;
|
||||
|
||||
/**
|
||||
* Returns true if both FULLTEXT indexes exist
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function index_created(): bool;
|
||||
|
||||
/**
|
||||
* Returns an associative array containing information about the indexes
|
||||
*
|
||||
* @return array|false Language string of error false otherwise
|
||||
*/
|
||||
public function index_stats();
|
||||
|
||||
/**
|
||||
* Display various options that can be configured for the backend from the acp
|
||||
*
|
||||
* @return array array containing template and config variables
|
||||
*/
|
||||
public function get_acp_options(): array;
|
||||
}
|
|
@ -11,7 +11,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\search\sphinx;
|
||||
namespace phpbb\search\backend\sphinx;
|
||||
|
||||
/**
|
||||
* An object representing the sphinx configuration
|
|
@ -11,7 +11,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\search\sphinx;
|
||||
namespace phpbb\search\backend\sphinx;
|
||||
|
||||
/**
|
||||
* \phpbb\search\sphinx\config_comment
|
|
@ -11,7 +11,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\search\sphinx;
|
||||
namespace phpbb\search\backend\sphinx;
|
||||
|
||||
/**
|
||||
* \phpbb\search\sphinx\config_section
|
|
@ -11,7 +11,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\search\sphinx;
|
||||
namespace phpbb\search\backend\sphinx;
|
||||
|
||||
/**
|
||||
* \phpbb\search\sphinx\config_variable
|
|
@ -1,292 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\search;
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
define('SEARCH_RESULT_NOT_IN_CACHE', 0);
|
||||
define('SEARCH_RESULT_IN_CACHE', 1);
|
||||
define('SEARCH_RESULT_INCOMPLETE', 2);
|
||||
|
||||
/**
|
||||
* optional base class for search plugins providing simple caching based on ACM
|
||||
* and functions to retrieve ignore_words and synonyms
|
||||
*/
|
||||
class base
|
||||
{
|
||||
var $ignore_words = array();
|
||||
var $match_synonym = array();
|
||||
var $replace_synonym = array();
|
||||
|
||||
function search_backend(&$error)
|
||||
{
|
||||
// This class cannot be used as a search plugin
|
||||
$error = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves cached search results
|
||||
*
|
||||
* @param string $search_key an md5 string generated from all the passed search options to identify the results
|
||||
* @param int &$result_count will contain the number of all results for the search (not only for the current page)
|
||||
* @param array &$id_ary is filled with the ids belonging to the requested page that are stored in the cache
|
||||
* @param int &$start indicates the first index of the page
|
||||
* @param int $per_page number of ids each page is supposed to contain
|
||||
* @param string $sort_dir is either a or d representing ASC and DESC
|
||||
*
|
||||
* @return int SEARCH_RESULT_NOT_IN_CACHE or SEARCH_RESULT_IN_CACHE or SEARCH_RESULT_INCOMPLETE
|
||||
*/
|
||||
function obtain_ids($search_key, &$result_count, &$id_ary, &$start, $per_page, $sort_dir)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if (!($stored_ids = $cache->get('_search_results_' . $search_key)))
|
||||
{
|
||||
// no search results cached for this search_key
|
||||
return SEARCH_RESULT_NOT_IN_CACHE;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result_count = $stored_ids[-1];
|
||||
$reverse_ids = ($stored_ids[-2] != $sort_dir) ? true : false;
|
||||
$complete = true;
|
||||
|
||||
// Change start parameter in case out of bounds
|
||||
if ($result_count)
|
||||
{
|
||||
if ($start < 0)
|
||||
{
|
||||
$start = 0;
|
||||
}
|
||||
else if ($start >= $result_count)
|
||||
{
|
||||
$start = floor(($result_count - 1) / $per_page) * $per_page;
|
||||
}
|
||||
}
|
||||
|
||||
// change the start to the actual end of the current request if the sort direction differs
|
||||
// from the dirction in the cache and reverse the ids later
|
||||
if ($reverse_ids)
|
||||
{
|
||||
$start = $result_count - $start - $per_page;
|
||||
|
||||
// the user requested a page past the last index
|
||||
if ($start < 0)
|
||||
{
|
||||
return SEARCH_RESULT_NOT_IN_CACHE;
|
||||
}
|
||||
}
|
||||
|
||||
for ($i = $start, $n = $start + $per_page; ($i < $n) && ($i < $result_count); $i++)
|
||||
{
|
||||
if (!isset($stored_ids[$i]))
|
||||
{
|
||||
$complete = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$id_ary[] = $stored_ids[$i];
|
||||
}
|
||||
}
|
||||
unset($stored_ids);
|
||||
|
||||
if ($reverse_ids)
|
||||
{
|
||||
$id_ary = array_reverse($id_ary);
|
||||
}
|
||||
|
||||
if (!$complete)
|
||||
{
|
||||
return SEARCH_RESULT_INCOMPLETE;
|
||||
}
|
||||
return SEARCH_RESULT_IN_CACHE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches post/topic ids
|
||||
*
|
||||
* @param string $search_key an md5 string generated from all the passed search options to identify the results
|
||||
* @param string $keywords contains the keywords as entered by the user
|
||||
* @param array $author_ary an array of author ids, if the author should be ignored during the search the array is empty
|
||||
* @param int $result_count contains the number of all results for the search (not only for the current page)
|
||||
* @param array &$id_ary contains a list of post or topic ids that shall be cached, the first element
|
||||
* must have the absolute index $start in the result set.
|
||||
* @param int $start indicates the first index of the page
|
||||
* @param string $sort_dir is either a or d representing ASC and DESC
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
function save_ids($search_key, $keywords, $author_ary, $result_count, &$id_ary, $start, $sort_dir)
|
||||
{
|
||||
global $cache, $config, $db, $user;
|
||||
|
||||
$length = min(count($id_ary), $config['search_block_size']);
|
||||
|
||||
// nothing to cache so exit
|
||||
if (!$length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$store_ids = array_slice($id_ary, 0, $length);
|
||||
|
||||
// create a new resultset if there is none for this search_key yet
|
||||
// or add the ids to the existing resultset
|
||||
if (!($store = $cache->get('_search_results_' . $search_key)))
|
||||
{
|
||||
// add the current keywords to the recent searches in the cache which are listed on the search page
|
||||
if (!empty($keywords) || count($author_ary))
|
||||
{
|
||||
$sql = 'SELECT search_time
|
||||
FROM ' . SEARCH_RESULTS_TABLE . '
|
||||
WHERE search_key = \'' . $db->sql_escape($search_key) . '\'';
|
||||
$result = $db->sql_query($sql);
|
||||
|
||||
if (!$db->sql_fetchrow($result))
|
||||
{
|
||||
$sql_ary = array(
|
||||
'search_key' => $search_key,
|
||||
'search_time' => time(),
|
||||
'search_keywords' => $keywords,
|
||||
'search_authors' => ' ' . implode(' ', $author_ary) . ' '
|
||||
);
|
||||
|
||||
$sql = 'INSERT INTO ' . SEARCH_RESULTS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
|
||||
$db->sql_query($sql);
|
||||
}
|
||||
$db->sql_freeresult($result);
|
||||
}
|
||||
|
||||
$sql = 'UPDATE ' . USERS_TABLE . '
|
||||
SET user_last_search = ' . time() . '
|
||||
WHERE user_id = ' . $user->data['user_id'];
|
||||
$db->sql_query($sql);
|
||||
|
||||
$store = array(-1 => $result_count, -2 => $sort_dir);
|
||||
$id_range = range($start, $start + $length - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we use one set of results for both sort directions so we have to calculate the indizes
|
||||
// for the reversed array and we also have to reverse the ids themselves
|
||||
if ($store[-2] != $sort_dir)
|
||||
{
|
||||
$store_ids = array_reverse($store_ids);
|
||||
$id_range = range($store[-1] - $start - $length, $store[-1] - $start - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
$id_range = range($start, $start + $length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
$store_ids = array_combine($id_range, $store_ids);
|
||||
|
||||
// append the ids
|
||||
if (is_array($store_ids))
|
||||
{
|
||||
$store += $store_ids;
|
||||
|
||||
// if the cache is too big
|
||||
if (count($store) - 2 > 20 * $config['search_block_size'])
|
||||
{
|
||||
// remove everything in front of two blocks in front of the current start index
|
||||
for ($i = 0, $n = $id_range[0] - 2 * $config['search_block_size']; $i < $n; $i++)
|
||||
{
|
||||
if (isset($store[$i]))
|
||||
{
|
||||
unset($store[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
// remove everything after two blocks after the current stop index
|
||||
end($id_range);
|
||||
for ($i = $store[-1] - 1, $n = current($id_range) + 2 * $config['search_block_size']; $i > $n; $i--)
|
||||
{
|
||||
if (isset($store[$i]))
|
||||
{
|
||||
unset($store[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$cache->put('_search_results_' . $search_key, $store, $config['search_store_results']);
|
||||
|
||||
$sql = 'UPDATE ' . SEARCH_RESULTS_TABLE . '
|
||||
SET search_time = ' . time() . '
|
||||
WHERE search_key = \'' . $db->sql_escape($search_key) . '\'';
|
||||
$db->sql_query($sql);
|
||||
}
|
||||
|
||||
unset($store);
|
||||
unset($store_ids);
|
||||
unset($id_range);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes old entries from the search results table and removes searches with keywords that contain a word in $words.
|
||||
*/
|
||||
function destroy_cache($words, $authors = false)
|
||||
{
|
||||
global $db, $cache, $config;
|
||||
|
||||
// clear all searches that searched for the specified words
|
||||
if (count($words))
|
||||
{
|
||||
$sql_where = '';
|
||||
foreach ($words as $word)
|
||||
{
|
||||
$sql_where .= " OR search_keywords " . $db->sql_like_expression($db->get_any_char() . $word . $db->get_any_char());
|
||||
}
|
||||
|
||||
$sql = 'SELECT search_key
|
||||
FROM ' . SEARCH_RESULTS_TABLE . "
|
||||
WHERE search_keywords LIKE '%*%' $sql_where";
|
||||
$result = $db->sql_query($sql);
|
||||
|
||||
while ($row = $db->sql_fetchrow($result))
|
||||
{
|
||||
$cache->destroy('_search_results_' . $row['search_key']);
|
||||
}
|
||||
$db->sql_freeresult($result);
|
||||
}
|
||||
|
||||
// clear all searches that searched for the specified authors
|
||||
if (is_array($authors) && count($authors))
|
||||
{
|
||||
$sql_where = '';
|
||||
foreach ($authors as $author)
|
||||
{
|
||||
$sql_where .= (($sql_where) ? ' OR ' : '') . 'search_authors ' . $db->sql_like_expression($db->get_any_char() . ' ' . (int) $author . ' ' . $db->get_any_char());
|
||||
}
|
||||
|
||||
$sql = 'SELECT search_key
|
||||
FROM ' . SEARCH_RESULTS_TABLE . "
|
||||
WHERE $sql_where";
|
||||
$result = $db->sql_query($sql);
|
||||
|
||||
while ($row = $db->sql_fetchrow($result))
|
||||
{
|
||||
$cache->destroy('_search_results_' . $row['search_key']);
|
||||
}
|
||||
$db->sql_freeresult($result);
|
||||
}
|
||||
|
||||
$sql = 'DELETE
|
||||
FROM ' . SEARCH_RESULTS_TABLE . '
|
||||
WHERE search_time < ' . (time() - (int) $config['search_store_results']);
|
||||
$db->sql_query($sql);
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||||
</head>
|
||||
|
||||
<body bgcolor="#FFFFFF" text="#000000">
|
||||
|
||||
</body>
|
||||
</html>
|
65
phpBB/phpbb/search/search_backend_factory.php
Normal file
65
phpBB/phpbb/search/search_backend_factory.php
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace phpbb\search;
|
||||
|
||||
use phpbb\config\config;
|
||||
use phpbb\di\service_collection;
|
||||
use phpbb\search\backend\search_backend_interface;
|
||||
|
||||
class search_backend_factory
|
||||
{
|
||||
/**
|
||||
* @var config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var service_collection
|
||||
*/
|
||||
protected $search_backends;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param config $config
|
||||
* @param service_collection $search_backends
|
||||
*/
|
||||
public function __construct(config $config, service_collection $search_backends)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->search_backends = $search_backends;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a specified search backend
|
||||
*
|
||||
* @param string $class
|
||||
*
|
||||
* @return search_backend_interface
|
||||
*/
|
||||
public function get(string $class): search_backend_interface
|
||||
{
|
||||
return $this->search_backends->get_by_class($class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains active search backend
|
||||
*
|
||||
* @return search_backend_interface
|
||||
*/
|
||||
public function get_active(): search_backend_interface
|
||||
{
|
||||
return $this->get($this->config['search_type']);
|
||||
}
|
||||
}
|
|
@ -294,19 +294,21 @@ if ($keywords || $author || $author_id || $search_id || $submit)
|
|||
}
|
||||
|
||||
// Select which method we'll use to obtain the post_id or topic_id information
|
||||
$search_type = $config['search_type'];
|
||||
|
||||
if (!class_exists($search_type))
|
||||
try
|
||||
{
|
||||
trigger_error('NO_SUCH_SEARCH_MODULE');
|
||||
$search_backend_factory = $phpbb_container->get('search.backend_factory');
|
||||
$search = $search_backend_factory->get_active();
|
||||
}
|
||||
// We do some additional checks in the module to ensure it can actually be utilised
|
||||
$error = false;
|
||||
$search = new $search_type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher);
|
||||
|
||||
if ($error)
|
||||
catch (RuntimeException $e)
|
||||
{
|
||||
trigger_error($error);
|
||||
if (strpos($e->getMessage(), 'No service found') === 0)
|
||||
{
|
||||
trigger_error('NO_SUCH_SEARCH_MODULE');
|
||||
}
|
||||
else
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
// let the search module split up the keywords
|
||||
|
@ -542,7 +544,7 @@ if ($keywords || $author || $author_id || $search_id || $submit)
|
|||
extract($phpbb_dispatcher->trigger_event('core.search_modify_param_after', compact($vars)));
|
||||
|
||||
// show_results should not change after this
|
||||
$per_page = ($show_results == 'posts') ? $config['posts_per_page'] : $config['topics_per_page'];
|
||||
$per_page = ($show_results == 'posts') ? (int) $config['posts_per_page'] : (int) $config['topics_per_page'];
|
||||
$total_match_count = 0;
|
||||
|
||||
// Set limit for the $total_match_count to reduce server load
|
||||
|
@ -1299,7 +1301,7 @@ if ($keywords || $author || $author_id || $search_id || $submit)
|
|||
|
||||
// Check if search backend supports phrase search or not
|
||||
$phrase_search_disabled = '';
|
||||
if (strpos(html_entity_decode($keywords), '"') !== false && method_exists($search, 'supports_phrase_search'))
|
||||
if (strpos(html_entity_decode($keywords), '"') !== false)
|
||||
{
|
||||
$phrase_search_disabled = $search->supports_phrase_search() ? false : true;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
require_once __DIR__ . '/../../phpBB/includes/functions_admin.php';
|
||||
require_once __DIR__ . '/../../phpBB/includes/functions_posting.php';
|
||||
require_once __DIR__ . '/../mock/search.php';
|
||||
|
||||
class phpbb_content_visibility_delete_post_test extends phpbb_database_test_case
|
||||
{
|
||||
|
@ -292,7 +291,7 @@ class phpbb_content_visibility_delete_post_test extends phpbb_database_test_case
|
|||
$config = new \phpbb\config\config(array(
|
||||
'num_posts' => 3,
|
||||
'num_topics' => 1,
|
||||
'search_type' => 'phpbb_mock_search',
|
||||
'search_type' => 'foo',
|
||||
));
|
||||
$cache = new phpbb_mock_cache;
|
||||
$db = $this->new_dbal();
|
||||
|
@ -323,6 +322,11 @@ class phpbb_content_visibility_delete_post_test extends phpbb_database_test_case
|
|||
// Works as a workaround for tests
|
||||
$phpbb_container->set('attachment.manager', $attachment_delete);
|
||||
|
||||
$search_backend = $this->createMock(\phpbb\search\backend\search_backend_interface::class);
|
||||
$search_backend_factory = $this->createMock(\phpbb\search\search_backend_factory::class);
|
||||
$search_backend_factory->method('get_active')->willReturn($search_backend);
|
||||
$phpbb_container->set('search.backend_factory', $search_backend_factory);
|
||||
|
||||
delete_post($forum_id, $topic_id, $post_id, $data, $is_soft, $reason);
|
||||
|
||||
$result = $db->sql_query('SELECT post_id, post_visibility, post_delete_reason
|
||||
|
|
|
@ -38,7 +38,7 @@ abstract class phpbb_functional_search_base extends phpbb_functional_test_case
|
|||
$this->login();
|
||||
$this->admin_login();
|
||||
|
||||
$this->create_search_index('\phpbb\search\fulltext_native');
|
||||
$this->create_search_index('phpbb\\search\\backend\\fulltext_native');
|
||||
|
||||
$post = $this->create_topic(2, 'Test Topic 1 foosubject', 'This is a test topic posted by the barsearch testing framework.');
|
||||
|
||||
|
@ -49,18 +49,28 @@ abstract class phpbb_functional_search_base extends phpbb_functional_test_case
|
|||
if ($values["config[search_type]"] != $this->search_backend)
|
||||
{
|
||||
$values["config[search_type]"] = $this->search_backend;
|
||||
$form->setValues($values);
|
||||
|
||||
try
|
||||
{
|
||||
$form->setValues($values);
|
||||
}
|
||||
catch(\InvalidArgumentException $e)
|
||||
{
|
||||
// Search backed is not supported because don't appear in the select
|
||||
$this->delete_topic($post['topic_id']);
|
||||
$this->markTestSkipped("Search backend is not supported/running");
|
||||
}
|
||||
|
||||
$crawler = self::submit($form);
|
||||
|
||||
$form = $crawler->selectButton('Yes')->form();
|
||||
$values = $form->getValues();
|
||||
$crawler = self::submit($form);
|
||||
|
||||
// check if search backend is not supported
|
||||
// Unknown error selecting search backend
|
||||
if ($crawler->filter('.errorbox')->count() > 0)
|
||||
{
|
||||
$this->delete_topic($post['topic_id']);
|
||||
$this->markTestSkipped("Search backend is not supported/running");
|
||||
$this->fail('Error when trying to select available search backend');
|
||||
}
|
||||
|
||||
$this->create_search_index();
|
||||
|
|
|
@ -18,7 +18,7 @@ require_once __DIR__ . '/base.php';
|
|||
*/
|
||||
class phpbb_functional_search_mysql_test extends phpbb_functional_search_base
|
||||
{
|
||||
protected $search_backend = '\phpbb\search\fulltext_mysql';
|
||||
protected $search_backend = 'phpbb\search\backend\fulltext_mysql';
|
||||
|
||||
protected function create_search_index($backend = null)
|
||||
{
|
||||
|
|
|
@ -18,5 +18,5 @@ require_once __DIR__ . '/base.php';
|
|||
*/
|
||||
class phpbb_functional_search_native_test extends phpbb_functional_search_base
|
||||
{
|
||||
protected $search_backend = '\phpbb\search\fulltext_native';
|
||||
protected $search_backend = 'phpbb\search\backend\fulltext_native';
|
||||
}
|
||||
|
|
|
@ -18,6 +18,6 @@ require_once __DIR__ . '/base.php';
|
|||
*/
|
||||
class phpbb_functional_search_postgres_test extends phpbb_functional_search_base
|
||||
{
|
||||
protected $search_backend = '\phpbb\search\fulltext_postgres';
|
||||
protected $search_backend = 'phpbb\search\backend\fulltext_postgres';
|
||||
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ require_once __DIR__ . '/base.php';
|
|||
*/
|
||||
class phpbb_functional_search_sphinx_test extends phpbb_functional_search_base
|
||||
{
|
||||
protected $search_backend = '\phpbb\search\fulltext_sphinx';
|
||||
protected $search_backend = 'phpbb\search\backend\fulltext_sphinx';
|
||||
|
||||
public function test_search_backend()
|
||||
{
|
||||
|
|
|
@ -33,7 +33,7 @@ class phpbb_functions_user_delete_user_test extends phpbb_database_test_case
|
|||
|
||||
$config = new \phpbb\config\config(array(
|
||||
'load_online_time' => 5,
|
||||
'search_type' => '\phpbb\search\fulltext_mysql',
|
||||
'search_type' => '\phpbb\search\backend\fulltext_mysql',
|
||||
));
|
||||
$cache = new phpbb_mock_null_cache();
|
||||
$phpbb_dispatcher = new phpbb_mock_event_dispatcher();
|
||||
|
@ -54,6 +54,13 @@ class phpbb_functions_user_delete_user_test extends phpbb_database_test_case
|
|||
'auth.provider_collection',
|
||||
$provider_collection
|
||||
);
|
||||
|
||||
$search_backend = $this->createMock(\phpbb\search\backend\search_backend_interface::class);
|
||||
$search_backend_factory = $this->createMock(\phpbb\search\search_backend_factory::class);
|
||||
$search_backend_factory->method('get_active')->willReturn($search_backend);
|
||||
$phpbb_container->set('search.backend_factory', $search_backend_factory);
|
||||
|
||||
|
||||
$phpbb_container->setParameter('tables.auth_provider_oauth_token_storage', 'phpbb_oauth_tokens');
|
||||
$phpbb_container->setParameter('tables.auth_provider_oauth_states', 'phpbb_oauth_states');
|
||||
$phpbb_container->setParameter('tables.auth_provider_oauth_account_assoc', 'phpbb_oauth_accounts');
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of the phpBB Forum Software package.
|
||||
*
|
||||
* @copyright (c) phpBB Limited <https://www.phpbb.com>
|
||||
* @license GNU General Public License, version 2 (GPL-2.0)
|
||||
*
|
||||
* For full copyright and license information, please see
|
||||
* the docs/CREDITS.txt file.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
*/
|
||||
class phpbb_mock_search
|
||||
{
|
||||
|
||||
public function __construct($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user)
|
||||
{
|
||||
}
|
||||
|
||||
public function index_remove($post_ids, $poster_ids, $forum_ids)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -26,7 +26,7 @@ class phpbb_mock_user
|
|||
public $lang = [];
|
||||
|
||||
private $options = array();
|
||||
public function optionget($item)
|
||||
public function optionget($item, $data = false)
|
||||
{
|
||||
if (!isset($this->options[$item]))
|
||||
{
|
||||
|
@ -36,7 +36,7 @@ class phpbb_mock_user
|
|||
return $this->options[$item];
|
||||
}
|
||||
|
||||
public function optionset($item, $value)
|
||||
public function optionset($item, $value, $data = false)
|
||||
{
|
||||
$this->options[$item] = $value;
|
||||
}
|
||||
|
|
|
@ -24,12 +24,14 @@ class phpbb_search_mysql_test extends phpbb_search_common_test_case
|
|||
|
||||
protected function setUp(): void
|
||||
{
|
||||
global $phpbb_root_path, $phpEx, $config, $user, $cache;
|
||||
global $phpbb_root_path, $phpEx, $config, $cache;
|
||||
|
||||
parent::setUp();
|
||||
|
||||
// dbal uses cache
|
||||
$cache = new phpbb_mock_cache();
|
||||
$cache = $this->createMock('\phpbb\cache\service');
|
||||
$language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx));
|
||||
$user = $this->createMock('\phpbb\user');
|
||||
|
||||
// set config values
|
||||
$config['fulltext_mysql_min_word_len'] = 4;
|
||||
|
@ -37,8 +39,7 @@ class phpbb_search_mysql_test extends phpbb_search_common_test_case
|
|||
|
||||
$this->db = $this->new_dbal();
|
||||
$phpbb_dispatcher = new phpbb_mock_event_dispatcher();
|
||||
$error = null;
|
||||
$class = self::get_search_wrapper('\phpbb\search\fulltext_mysql');
|
||||
$this->search = new $class($error, $phpbb_root_path, $phpEx, null, $config, $this->db, $user, $phpbb_dispatcher);
|
||||
$class = self::get_search_wrapper('\phpbb\search\backend\fulltext_mysql');
|
||||
$this->search = new $class($config, $this->db, $phpbb_dispatcher, $language, $user, $phpbb_root_path, $phpEx);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,20 +24,21 @@ class phpbb_search_native_test extends phpbb_search_test_case
|
|||
|
||||
protected function setUp(): void
|
||||
{
|
||||
global $phpbb_root_path, $phpEx, $config, $user, $cache;
|
||||
global $phpbb_root_path, $phpEx, $config, $cache;
|
||||
|
||||
parent::setUp();
|
||||
|
||||
// dbal uses cache
|
||||
$cache = new phpbb_mock_cache();
|
||||
$cache = $this->createMock('\phpbb\cache\service');
|
||||
$language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx));
|
||||
$user = $this->createMock('\phpbb\user');
|
||||
|
||||
$this->db = $this->new_dbal();
|
||||
$phpbb_dispatcher = new phpbb_mock_event_dispatcher();
|
||||
$error = null;
|
||||
$class = self::get_search_wrapper('\phpbb\search\fulltext_native');
|
||||
$class = self::get_search_wrapper('\phpbb\search\backend\fulltext_native');
|
||||
$config['fulltext_native_min_chars'] = 2;
|
||||
$config['fulltext_native_max_chars'] = 14;
|
||||
$this->search = new $class($error, $phpbb_root_path, $phpEx, null, $config, $this->db, $user, $phpbb_dispatcher);
|
||||
$this->search = new $class($config, $this->db, $phpbb_dispatcher, $language, $user, $phpbb_root_path, $phpEx);
|
||||
}
|
||||
|
||||
public function keywords()
|
||||
|
|
|
@ -24,12 +24,14 @@ class phpbb_search_postgres_test extends phpbb_search_common_test_case
|
|||
|
||||
protected function setUp(): void
|
||||
{
|
||||
global $phpbb_root_path, $phpEx, $config, $user, $cache;
|
||||
global $phpbb_root_path, $phpEx, $config, $cache;
|
||||
|
||||
parent::setUp();
|
||||
|
||||
// dbal uses cache
|
||||
$cache = new phpbb_mock_cache();
|
||||
$cache = $this->createMock('\phpbb\cache\service');
|
||||
$language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx));
|
||||
$user = $this->createMock('\phpbb\user');
|
||||
|
||||
// set config values
|
||||
$config['fulltext_postgres_min_word_len'] = 4;
|
||||
|
@ -37,8 +39,7 @@ class phpbb_search_postgres_test extends phpbb_search_common_test_case
|
|||
|
||||
$this->db = $this->new_dbal();
|
||||
$phpbb_dispatcher = new phpbb_mock_event_dispatcher();
|
||||
$error = null;
|
||||
$class = self::get_search_wrapper('\phpbb\search\fulltext_postgres');
|
||||
$this->search = new $class($error, $phpbb_root_path, $phpEx, null, $config, $this->db, $user, $phpbb_dispatcher);
|
||||
$class = self::get_search_wrapper('\phpbb\search\backend\fulltext_postgres');
|
||||
$this->search = new $class($config, $this->db, $phpbb_dispatcher, $language, $user, $phpbb_root_path, $phpEx);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue