Merge pull request #5439 from rubencm/ticket/12683

[ticket/12683] Add a CLI command to generate the search index
This commit is contained in:
Marc Alexander 2022-12-11 22:03:19 +01:00 committed by GitHub
commit 995717f4a0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 1527 additions and 221 deletions

View file

@ -23,6 +23,7 @@ imports:
- { resource: services_notification.yml }
- { resource: services_password.yml }
- { resource: services_php.yml }
- { resource: services_posting.yml }
- { resource: services_profilefield.yml }
- { resource: services_report.yml }
- { resource: services_routing.yml }

View file

@ -246,6 +246,40 @@ services:
tags:
- { name: console.command }
console.command.searchindex.list_all:
class: phpbb\console\command\searchindex\list_all
arguments:
- '@config'
- '@language'
- '@search.backend_collection'
- '@user'
tags:
- { name: console.command }
console.command.searchindex.create:
class: phpbb\console\command\searchindex\create
arguments:
- '@language'
- '@log'
- '@post.helper'
- '@search.backend_factory'
- '@search.state_helper'
- '@user'
tags:
- { name: console.command }
console.command.searchindex.delete:
class: phpbb\console\command\searchindex\delete
arguments:
- '@language'
- '@log'
- '@post.helper'
- '@search.backend_factory'
- '@search.state_helper'
- '@user'
tags:
- { name: console.command }
console.command.thumbnail.delete:
class: phpbb\console\command\thumbnail\delete
arguments:

View file

@ -0,0 +1,5 @@
services:
post.helper:
class: phpbb\posting\post_helper
arguments:
- '@dbal.conn'

View file

@ -1,5 +1,11 @@
services:
search.state_helper:
class: phpbb\search\state_helper
arguments:
- '@config'
- '@search.backend_factory'
# Search backends
search.fulltext.mysql:
class: phpbb\search\backend\fulltext_mysql
@ -9,6 +15,7 @@ services:
- '@dispatcher'
- '@language'
- '@user'
- '%tables.search_results%'
- '%core.root_path%'
- '%core.php_ext%'
tags:
@ -22,6 +29,9 @@ services:
- '@dispatcher'
- '@language'
- '@user'
- '%tables.search_results%'
- '%tables.search_wordlist%'
- '%tables.search_wordmatch%'
- '%core.root_path%'
- '%core.php_ext%'
tags:
@ -35,6 +45,7 @@ services:
- '@dispatcher'
- '@language'
- '@user'
- '%tables.search_results%'
- '%core.root_path%'
- '%core.php_ext%'
tags:

View file

@ -652,16 +652,9 @@ class acp_main
$search_backend_factory = $phpbb_container->get('search.backend_factory');
$search = $search_backend_factory->get_active();
}
catch (RuntimeException $e)
catch (\phpbb\search\exception\no_search_backend_found_exception $e)
{
if (strpos($e->getMessage(), 'No service found') === 0)
{
trigger_error('NO_SUCH_SEARCH_MODULE');
}
else
{
throw $e;
}
trigger_error('NO_SUCH_SEARCH_MODULE');
}
if (!$search->index_created())

View file

@ -21,6 +21,7 @@ use phpbb\language\language;
use phpbb\log\log;
use phpbb\request\request;
use phpbb\search\search_backend_factory;
use phpbb\search\state_helper;
use phpbb\template\template;
use phpbb\user;
@ -35,10 +36,6 @@ class acp_search
public $tpl_name;
public $page_title;
protected const STATE_SEARCH_TYPE = 0;
protected const STATE_ACTION = 1;
protected const STATE_POST_COUNTER = 2;
/** @var config */
protected $config;
@ -57,6 +54,9 @@ class acp_search
/** @var search_backend_factory */
protected $search_backend_factory;
/** @var state_helper */
protected $search_state_helper;
/** @var template */
protected $template;
@ -79,6 +79,7 @@ class acp_search
$this->request = $request;
$this->search_backend_collection = $phpbb_container->get('search.backend_collection');
$this->search_backend_factory = $phpbb_container->get('search.backend_factory');
$this->search_state_helper = $phpbb_container->get('search.state_helper');
$this->template = $template;
$this->user = $user;
$this->phpbb_admin_path = $phpbb_admin_path;
@ -272,7 +273,6 @@ class acp_search
public function index(string $id, string $mode): void
{
$action = $this->request->variable('action', '');
$state = !empty($this->config['search_indexing_state']) ? explode(',', $this->config['search_indexing_state']) : [];
if ($action && !$this->request->is_set_post('cancel'))
{
@ -284,7 +284,7 @@ class acp_search
case 'create':
case 'delete':
$this->index_action($id, $mode, $action, $state);
$this->index_action($id, $mode, $action);
break;
default:
@ -296,13 +296,12 @@ class acp_search
// If clicked to cancel the indexing progress (acp_search_index_inprogress form)
if ($this->request->is_set_post('cancel'))
{
$state = [];
$this->save_state($state);
$this->search_state_helper->clear_state();
}
if (!empty($state))
if ($this->search_state_helper->is_action_in_progress())
{
$this->index_inprogress($id, $mode, $state[self::STATE_ACTION]);
$this->index_inprogress($id, $mode);
}
else
{
@ -325,8 +324,8 @@ class acp_search
foreach ($this->search_backend_collection as $search)
{
$this->template->assign_block_vars('backends', [
'NAME' => $search->get_name(),
'TYPE' => $search->get_type(),
'NAME' => $search->get_name(),
'TYPE' => $search->get_type(),
'S_ACTIVE' => $search->get_type() === $this->config['search_type'],
'S_HIDDEN_FIELDS' => build_hidden_fields(['search_type' => $search->get_type()]),
@ -336,8 +335,8 @@ class acp_search
}
$this->template->assign_vars([
'U_ACTION' => $this->u_action . '&hash=' . generate_link_hash('acp_search'),
'UA_PROGRESS_BAR' => addslashes($this->u_action . '&action=progress_bar'),
'U_ACTION' => $this->u_action . '&hash=' . generate_link_hash('acp_search'),
'UA_PROGRESS_BAR' => addslashes($this->u_action . '&action=progress_bar'),
]);
}
@ -346,13 +345,14 @@ class acp_search
*
* @param string $id
* @param string $mode
* @param string $action Action in progress: 'create' or 'delete'
*/
private function index_inprogress(string $id, string $mode, string $action): void
private function index_inprogress(string $id, string $mode): void
{
$this->tpl_name = 'acp_search_index_inprogress';
$this->page_title = 'ACP_SEARCH_INDEX';
$action = $this->search_state_helper->action();
$this->template->assign_vars([
'U_ACTION' => $this->u_action . '&action=' . $action . '&hash=' . generate_link_hash('acp_search'),
'UA_PROGRESS_BAR' => addslashes($this->u_action . '&action=progress_bar'),
@ -368,9 +368,8 @@ class acp_search
* @param string $id
* @param string $mode
* @param string $action
* @param array $state
*/
private function index_action(string $id, string $mode, string $action, array $state): void
private function index_action(string $id, string $mode, string $action): void
{
// For some this may be of help...
@ini_set('memory_limit', '128M');
@ -381,29 +380,23 @@ class acp_search
}
// Entering here for the first time
if (empty($state))
if (!$this->search_state_helper->is_action_in_progress())
{
if ($this->request->is_set_post('search_type', ''))
{
$state = [
self::STATE_SEARCH_TYPE => $this->request->variable('search_type', ''),
self::STATE_ACTION => $action,
self::STATE_POST_COUNTER => 0
];
$this->search_state_helper->init($this->request->variable('search_type', ''), $action);
}
else
{
trigger_error($this->language->lang('FORM_INVALID') . adm_back_link($this->u_action), E_USER_WARNING);
}
$this->save_state($state); // Create new state in the database
}
$type = $state[self::STATE_SEARCH_TYPE];
$action = $state[self::STATE_ACTION];
$post_counter = &$state[self::STATE_POST_COUNTER];
// Execute create/delete
$type = $this->search_state_helper->type();
$action = $this->search_state_helper->action();
$post_counter = $this->search_state_helper->counter();
$search = $this->search_backend_factory->get($type);
try
@ -411,7 +404,7 @@ class acp_search
$status = ($action == 'create') ? $search->create_index($post_counter) : $search->delete_index($post_counter);
if ($status) // Status is not null, so action is in progress....
{
$this->save_state($state); // update $post_counter in $state in the database
$this->search_state_helper->update_counter($status['post_counter']);
$u_action = append_sid($this->phpbb_admin_path . "index." . $this->php_ex, "i=$id&mode=$mode&action=$action&hash=" . generate_link_hash('acp_search'), false);
meta_refresh(1, $u_action);
@ -423,13 +416,13 @@ class acp_search
}
catch (Exception $e)
{
$this->save_state([]); // Unexpected error, cancel action
$this->search_state_helper->clear_state(); // Unexpected error, cancel action
trigger_error($e->getMessage() . adm_back_link($this->u_action) . $this->close_popup_js(), E_USER_WARNING);
}
$search->tidy();
$this->save_state([]); // finished operation, cancel action
$this->search_state_helper->clear_state(); // finished operation, cancel action
$log_operation = ($action == 'create') ? 'LOG_SEARCH_INDEX_CREATED' : 'LOG_SEARCH_INDEX_REMOVED';
$this->log->add('admin', $this->user->data['user_id'], $this->user->ip, $log_operation, false, [$search->get_name()]);
@ -473,14 +466,4 @@ class acp_search
"// ]]>\n" .
"</script>\n";
}
/**
* @param array $state
*/
private function save_state(array $state = []): void
{
ksort($state);
$this->config->set('search_indexing_state', implode(',', $state), true);
}
}

View file

@ -1092,16 +1092,9 @@ function delete_posts($where_type, $where_ids, $auto_sync = true, $posted_sync =
$search_backend_factory = $phpbb_container->get('search.backend_factory');
$search = $search_backend_factory->get_active();
}
catch (RuntimeException $e)
catch (\phpbb\search\exception\no_search_backend_found_exception $e)
{
if (strpos($e->getMessage(), 'No service found') === 0)
{
trigger_error('NO_SUCH_SEARCH_MODULE');
}
else
{
throw $e;
}
trigger_error('NO_SUCH_SEARCH_MODULE');
}
$search->index_remove($post_ids, $poster_ids, $forum_ids);

View file

@ -2356,16 +2356,9 @@ function submit_post($mode, $subject, $username, $topic_type, &$poll_ary, &$data
$search_backend_factory = $phpbb_container->get('search.backend_factory');
$search = $search_backend_factory->get_active();
}
catch (RuntimeException $e)
catch (\phpbb\search\exception\no_search_backend_found_exception $e)
{
if (strpos($e->getMessage(), 'No service found') === 0)
{
trigger_error('NO_SUCH_SEARCH_MODULE');
}
else
{
throw $e;
}
trigger_error('NO_SUCH_SEARCH_MODULE');
}
$search->index($mode, (int) $data_ary['post_id'], $data_ary['message'], $subject, $poster_id, (int) $data_ary['forum_id']);

View file

@ -1397,16 +1397,9 @@ function mcp_fork_topic($topic_ids)
$search_backend_factory = $phpbb_container->get('search.backend_factory');
$search = $search_backend_factory->get_active();
}
catch (RuntimeException $e)
catch (\phpbb\search\exception\no_search_backend_found_exception $e)
{
if (strpos($e->getMessage(), 'No service found') === 0)
{
trigger_error('NO_SUCH_SEARCH_MODULE');
}
else
{
throw $e;
}
trigger_error('NO_SUCH_SEARCH_MODULE');
}
$search_mode = 'post';
}

View file

@ -637,16 +637,9 @@ function change_poster(&$post_info, $userdata)
$search_backend_factory = $phpbb_container->get('search.backend_factory');
$search = $search_backend_factory->get_active();
}
catch (RuntimeException $e)
catch (\phpbb\search\exception\no_search_backend_found_exception $e)
{
if (strpos($e->getMessage(), 'No service found') === 0)
{
trigger_error('NO_SUCH_SEARCH_MODULE');
}
else
{
throw $e;
}
trigger_error('NO_SUCH_SEARCH_MODULE');
}
$search->index_remove([], [$post_info['user_id'], $userdata['user_id']], []);

View file

@ -680,16 +680,9 @@ function split_topic($action, $topic_id, $to_forum_id, $subject)
$search_backend_factory = $phpbb_container->get('search.backend_factory');
$search = $search_backend_factory->get_active();
}
catch (RuntimeException $e)
catch (\phpbb\search\exception\no_search_backend_found_exception $e)
{
if (strpos($e->getMessage(), 'No service found') === 0)
{
trigger_error('NO_SUCH_SEARCH_MODULE');
}
else
{
throw $e;
}
trigger_error('NO_SUCH_SEARCH_MODULE');
}
$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']);

View file

@ -91,6 +91,8 @@ $lang = array_merge($lang, array(
'GENERAL_SEARCH_SETTINGS' => 'General search settings',
'GO_TO_SEARCH_INDEX' => 'Go to search index page',
'INVALID_ACTION' => 'Invalid action',
'INDEX_STATS' => 'Index statistics',
'INDEXING_IN_PROGRESS' => 'Indexing in progress',
'INDEXING_IN_PROGRESS_EXPLAIN' => 'The search backend is currently indexing all posts on the board. This can take from a few minutes to a few hours depending on your boards size.',

View file

@ -83,6 +83,10 @@ $lang = array_merge($lang, array(
'CLI_DESCRIPTION_SET_ATOMIC_CONFIG' => 'Sets a configuration options value only if the old matches the current value',
'CLI_DESCRIPTION_SET_CONFIG' => 'Sets a configuration options value',
'CLI_DESCRIPTION_SEARCHINDEX_DELETE' => 'Delete search index.',
'CLI_DESCRIPTION_SEARCHINDEX_CREATE' => 'Create search index.',
'CLI_DESCRIPTION_SEARCHINDEX_LIST' => 'List all search backends.',
'CLI_DESCRIPTION_THUMBNAIL_DELETE' => 'Delete all existing thumbnails.',
'CLI_DESCRIPTION_THUMBNAIL_GENERATE' => 'Generate all missing thumbnails.',
'CLI_DESCRIPTION_THUMBNAIL_RECREATE' => 'Recreate all thumbnails.',
@ -142,6 +146,16 @@ $lang = array_merge($lang, array(
'CLI_REPARSER_REPARSE_REPARSING_START' => 'Reparsing %s...',
'CLI_REPARSER_REPARSE_SUCCESS' => 'Reparsing ended with success',
'CLI_SEARCHINDEX_SEARCH_BACKEND_NAME' => 'Backend class',
'CLI_SEARCHINDEX_BACKEND_NOT_FOUND' => 'Search module not found',
'CLI_SEARCHINDEX_CREATE_SUCCESS' => 'Search index created successfully',
'CLI_SEARCHINDEX_CREATE_FAILURE' => 'Error creating search index',
'CLI_SEARCHINDEX_DELETE_SUCCESS' => 'Search index deleted successfully',
'CLI_SEARCHINDEX_DELETE_FAILURE' => 'Error deleting search index',
'CLI_SEARCHINDEX_ACTION_IN_PROGRESS' => 'There is an action currently in progress. CLI doesnt support incomplete index/delete actions, please solve it from the ACP.',
'CLI_SEARCHINDEX_ACTIVE_NOT_INDEXED' => 'Active search backend isnt indexed',
'CLI_SEARCHINDEX_BACKEND_NOT_AVAILABLE' => 'Search backend isnt available.',
// In all the case %1$s is the logical name of the file and %2$s the real name on the filesystem
// eg: big_image.png (2_a51529ae7932008cf8454a95af84cacd) generated.
'CLI_THUMBNAIL_DELETED' => '%1$s (%2$s) deleted.',

View file

@ -215,6 +215,9 @@ $lang = array_merge($lang, array(
2 => 'Downloaded %d times',
),
'DI_SERVICE_NOT_FOUND' => 'Service for class "%1$s" not found in collection.',
'DI_MULTIPLE_SERVICE_DEFINITIONS' => 'More than one service definitions found for class "%1$s" in collection.',
'EDIT_POST' => 'Edit post',
'ELLIPSIS' => '…',
'EMAIL' => 'Email', // Short form for EMAIL_ADDRESS
@ -715,6 +718,7 @@ $lang = array_merge($lang, array(
'SEARCH_UNANSWERED' => 'Unanswered topics',
'SEARCH_UNREAD' => 'Unread posts',
'SEARCH_USER_POSTS' => 'Search users posts',
'SEARCH_BACKEND_NOT_FOUND' => 'No search backend could be found.',
'SECONDS' => 'Seconds',
'SEE_ALL' => 'See All',
'SELECT' => 'Select',

View file

@ -49,10 +49,8 @@ class list_all extends \phpbb\console\command\command
*/
protected function configure()
{
$this
->setName('reparser:list')
->setDescription($this->user->lang('CLI_DESCRIPTION_REPARSER_LIST'))
;
$this->setName('reparser:list')
->setDescription($this->user->lang('CLI_DESCRIPTION_REPARSER_LIST'));
}
/**

View file

@ -0,0 +1,165 @@
<?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\console\command\searchindex;
use phpbb\console\command\command;
use phpbb\language\language;
use phpbb\log\log;
use phpbb\posting\post_helper;
use phpbb\search\exception\no_search_backend_found_exception;
use phpbb\search\search_backend_factory;
use phpbb\search\state_helper;
use phpbb\user;
use Symfony\Component\Console\Command\Command as symfony_command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class create extends command
{
/** @var language */
protected $language;
/** @var log */
protected $log;
/** @var post_helper */
protected $post_helper;
/** @var search_backend_factory */
protected $search_backend_factory;
/** @var state_helper */
protected $state_helper;
/**
* Construct method
*
* @param language $language
* @param log $log
* @param post_helper $post_helper
* @param search_backend_factory $search_backend_factory
* @param state_helper $state_helper
* @param user $user
*/
public function __construct(language $language, log $log, post_helper $post_helper, search_backend_factory $search_backend_factory, state_helper $state_helper, user $user)
{
$this->language = $language;
$this->log = $log;
$this->post_helper = $post_helper;
$this->search_backend_factory = $search_backend_factory;
$this->state_helper = $state_helper;
$this->language->add_lang(array('acp/common', 'acp/search'));
parent::__construct($user);
}
/**
* Sets the command name and description
*
* @return void
*/
protected function configure()
{
$this->setName('searchindex:create')
->setDescription($this->language->lang('CLI_DESCRIPTION_SEARCHINDEX_CREATE'))
->addArgument(
'search-backend',
InputArgument::REQUIRED,
$this->language->lang('CLI_SEARCHINDEX_SEARCH_BACKEND_NAME')
);
}
/**
* Executes the command searchindex:create
*
* Create search index
*
* @param InputInterface $input The input stream used to get the options
* @param OutputInterface $output The output stream, used to print messages
*
* @return int 0 if all is well, 1 if any errors occurred
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->section($this->language->lang('CLI_DESCRIPTION_SEARCHINDEX_CREATE'));
$search_backend = $input->getArgument('search-backend');
try
{
$search = $this->search_backend_factory->get($search_backend);
$name = $search->get_name();
}
catch (no_search_backend_found_exception $e)
{
$io->error($this->language->lang('CLI_SEARCHINDEX_BACKEND_NOT_FOUND', $search_backend));
return symfony_command::FAILURE;
}
if ($this->state_helper->is_action_in_progress())
{
$io->error($this->language->lang('CLI_SEARCHINDEX_ACTION_IN_PROGRESS', $search_backend));
return symfony_command::FAILURE;
}
if (!$search->is_available())
{
$io->error($this->language->lang('CLI_SEARCHINDEX_BACKEND_NOT_AVAILABLE', $search_backend));
return symfony_command::FAILURE;
}
try
{
$progress = $this->create_progress_bar($this->post_helper->get_max_post_id(), $io, $output, true);
$progress->setMessage('');
$progress->start();
$this->state_helper->init($search->get_type(), 'create');
$counter = 0;
while (($status = $search->create_index($counter)) !== null)
{
$this->state_helper->update_counter($status['post_counter']);
$progress->setProgress($status['post_counter']);
$progress->setMessage(round($status['rows_per_second'], 2) . ' rows/s');
}
$progress->finish();
$io->newLine(2);
}
catch (\Exception $e)
{
$this->state_helper->clear_state(); // Unexpected error, cancel action
$io->error($e->getMessage()); // Show also exception message like in acp
$io->error($this->language->lang('CLI_SEARCHINDEX_CREATE_FAILURE', $name));
return symfony_command::FAILURE;
}
$search->tidy();
$this->state_helper->clear_state();
$this->log->add('admin', ANONYMOUS, '', 'LOG_SEARCH_INDEX_CREATED', false, array($name));
$io->success($this->language->lang('CLI_SEARCHINDEX_CREATE_SUCCESS', $name));
return symfony_command::SUCCESS;
}
}

View file

@ -0,0 +1,165 @@
<?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\console\command\searchindex;
use phpbb\console\command\command;
use phpbb\language\language;
use phpbb\log\log;
use phpbb\posting\post_helper;
use phpbb\search\exception\no_search_backend_found_exception;
use phpbb\search\search_backend_factory;
use phpbb\search\state_helper;
use phpbb\user;
use Symfony\Component\Console\Command\Command as symfony_command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class delete extends command
{
/** @var language */
protected $language;
/** @var log */
protected $log;
/** @var post_helper */
protected $post_helper;
/** @var search_backend_factory */
protected $search_backend_factory;
/** @var state_helper */
protected $state_helper;
/**
* Construct method
*
* @param language $language
* @param log $log
* @param post_helper $post_helper
* @param search_backend_factory $search_backend_factory
* @param state_helper $state_helper
* @param user $user
*/
public function __construct(language $language, log $log, post_helper $post_helper, search_backend_factory $search_backend_factory, state_helper $state_helper, user $user)
{
$this->language = $language;
$this->log = $log;
$this->post_helper = $post_helper;
$this->search_backend_factory = $search_backend_factory;
$this->state_helper = $state_helper;
$this->language->add_lang(array('acp/common', 'acp/search'));
parent::__construct($user);
}
/**
* Sets the command name and description
*
* @return void
*/
protected function configure()
{
$this->setName('searchindex:delete')
->setDescription($this->language->lang('CLI_DESCRIPTION_SEARCHINDEX_DELETE'))
->addArgument(
'search-backend',
InputArgument::REQUIRED,
$this->language->lang('CLI_SEARCHINDEX_SEARCH_BACKEND_NAME')
);
}
/**
* Executes the command searchindex:delete
*
* Delete search index
*
* @param InputInterface $input The input stream used to get the options
* @param OutputInterface $output The output stream, used to print messages
*
* @return int 0 if all is well, 1 if any errors occurred
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->section($this->language->lang('CLI_DESCRIPTION_SEARCHINDEX_DELETE'));
$search_backend = $input->getArgument('search-backend');
try
{
$search = $this->search_backend_factory->get($search_backend);
$name = $search->get_name();
}
catch (no_search_backend_found_exception $e)
{
$io->error($this->language->lang('CLI_SEARCHINDEX_BACKEND_NOT_FOUND', $search_backend));
return symfony_command::FAILURE;
}
if ($this->state_helper->is_action_in_progress())
{
$io->error($this->language->lang('CLI_SEARCHINDEX_ACTION_IN_PROGRESS', $search_backend));
return symfony_command::FAILURE;
}
if (!$search->is_available())
{
$io->error($this->language->lang('CLI_SEARCHINDEX_BACKEND_NOT_AVAILABLE', $search_backend));
return symfony_command::FAILURE;
}
try
{
$progress = $this->create_progress_bar($this->post_helper->get_max_post_id(), $io, $output, true);
$progress->setMessage('');
$progress->start();
$this->state_helper->init($search->get_type(), 'delete');
$counter = 0;
while (($status = $search->delete_index($counter)) !== null)
{
$this->state_helper->update_counter($status['post_counter']);
$progress->setProgress($status['post_counter']);
$progress->setMessage(round($status['rows_per_second'], 2) . ' rows/s');
}
$progress->finish();
$io->newLine(2);
}
catch (\Exception $e)
{
$this->state_helper->clear_state(); // Unexpected error, cancel action
$io->error($e->getMessage()); // Show also exception message like in acp
$io->error($this->language->lang('CLI_SEARCHINDEX_DELETE_FAILURE', $name));
return symfony_command::FAILURE;
}
$search->tidy();
$this->state_helper->clear_state();
$this->log->add('admin', ANONYMOUS, '', 'LOG_SEARCH_INDEX_REMOVED', false, array($name));
$io->success($this->language->lang('CLI_SEARCHINDEX_DELETE_SUCCESS', $name));
return symfony_command::SUCCESS;
}
}

View file

@ -0,0 +1,96 @@
<?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\console\command\searchindex;
use phpbb\config\config;
use phpbb\console\command\command;
use phpbb\di\service_collection;
use phpbb\language\language;
use phpbb\user;
use Symfony\Component\Console\Command\Command as symfony_command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class list_all extends command
{
/** @var config */
protected $config;
/** @var language */
protected $language;
/** @var service_collection */
protected $search_backend_collection;
/**
* Construct method
*
* @param config $config
* @param language $language
* @param service_collection $search_backend_collection
* @param user $user
*/
public function __construct(config $config, language $language, service_collection $search_backend_collection, user $user)
{
$this->config = $config;
$this->language = $language;
$this->search_backend_collection = $search_backend_collection;
parent::__construct($user);
}
/**
* Sets the command name and description
*
* @return void
*/
protected function configure()
{
$this->setName('searchindex:list')
->setDescription($this->language->lang('CLI_DESCRIPTION_SEARCHINDEX_LIST'));
}
/**
* Executes the command searchindex:list
*
* List all search backends.
*
* @param InputInterface $input The input stream used to get the options
* @param OutputInterface $output The output stream, used to print messages
*
* @return int 0 if all is well, 1 if any errors occurred
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$search_backends = [];
foreach ($this->search_backend_collection as $search_backend)
{
$name = $search_backend->get_type();
$active = ($name === $this->config['search_type']) ? '(<comment>' . $this->language->lang('ACTIVE') . '</comment>) ' : '';
$search_backends[] = '<info>' . $name . '</info> ' . $active . $search_backend->get_name();
if ($name === $this->config['search_type'] && !$search_backend->index_created())
{
$io->error($this->language->lang('CLI_SEARCHINDEX_ACTIVE_NOT_INDEXED'));
}
}
$io->listing($search_backends);
return symfony_command::SUCCESS;
}
}

View file

@ -15,6 +15,7 @@ namespace phpbb\cron\task\core;
use phpbb\config\config;
use phpbb\cron\task\base;
use phpbb\di\exception\di_exception;
use phpbb\search\backend\search_backend_interface;
use phpbb\search\search_backend_factory;
@ -88,7 +89,7 @@ class tidy_search extends base
$this->active_search = $this->search_backend_factory->get_active();
}
}
catch (\RuntimeException $e)
catch (di_exception $e)
{
return false;
}

View file

@ -0,0 +1,20 @@
<?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\di\exception;
use phpbb\exception\runtime_exception;
class di_exception extends runtime_exception
{
}

View file

@ -0,0 +1,18 @@
<?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\di\exception;
class multiple_service_definitions_exception extends di_exception
{
}

View file

@ -0,0 +1,18 @@
<?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\di\exception;
class service_not_found_exception extends di_exception
{
}

View file

@ -13,6 +13,8 @@
namespace phpbb\di;
use phpbb\di\exception\multiple_service_definitions_exception;
use phpbb\di\exception\service_not_found_exception;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@ -105,7 +107,7 @@ class service_collection extends \ArrayObject
{
if ($service_id !== null)
{
throw new \RuntimeException('More than one service definitions found for class "'.$class.'" in collection.');
throw new multiple_service_definitions_exception('DI_MULTIPLE_SERVICE_DEFINITIONS', [$class]);
}
$service_id = $id;
@ -114,7 +116,7 @@ class service_collection extends \ArrayObject
if ($service_id === null)
{
throw new \RuntimeException('No service found for class "'.$class.'" in collection.');
throw new service_not_found_exception('DI_SERVICE_NOT_FOUND', [$class]);
}
return $this->offsetGet($service_id);

View file

@ -133,6 +133,9 @@ class create_search_index extends database_task
$this->phpbb_dispatcher,
$container->get('language'),
$this->user,
SEARCH_RESULTS_TABLE,
SEARCH_WORDLIST_TABLE,
SEARCH_WORDMATCH_TABLE,
$this->phpbb_root_path,
$this->php_ext
);

View file

@ -0,0 +1,43 @@
<?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\posting;
use phpbb\db\driver\driver_interface;
class post_helper
{
/**
* @var driver_interface
*/
protected $db;
public function __construct(driver_interface $db)
{
$this->db = $db;
}
/**
* Get last post id
*/
public 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;
}
}

View file

@ -1,15 +1,15 @@
<?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.
*
*/
*
* 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;
@ -19,9 +19,9 @@ 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
*/
* 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;
@ -51,20 +51,27 @@ abstract class base implements search_backend_interface
*/
protected $user;
/**
* @var string
*/
protected $search_results_table;
/**
* Constructor.
*
* @param service $cache
* @param config $config
* @param driver_interface $db
* @param user $user
* @param service $cache
* @param config $config
* @param driver_interface $db
* @param user $user
* @param string $search_results_table
*/
public function __construct(service $cache, config $config, driver_interface $db, user $user)
public function __construct(service $cache, config $config, driver_interface $db, user $user, string $search_results_table)
{
$this->cache = $cache;
$this->config = $config;
$this->db = $db;
$this->user = $user;
$this->search_results_table = $search_results_table;
}
/**
@ -180,7 +187,7 @@ abstract class base implements search_backend_interface
if (!empty($keywords) || count($author_ary))
{
$sql = 'SELECT search_time
FROM ' . SEARCH_RESULTS_TABLE . '
FROM ' . $this->search_results_table . '
WHERE search_key = \'' . $this->db->sql_escape($search_key) . '\'';
$result = $this->db->sql_query($sql);
@ -193,7 +200,7 @@ abstract class base implements search_backend_interface
'search_authors' => ' ' . implode(' ', $author_ary) . ' '
);
$sql = 'INSERT INTO ' . SEARCH_RESULTS_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
$sql = 'INSERT INTO ' . $this->search_results_table . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
$this->db->sql_query($sql);
}
$this->db->sql_freeresult($result);
@ -253,7 +260,7 @@ abstract class base implements search_backend_interface
}
$this->cache->put('_search_results_' . $search_key, $store, $this->config['search_store_results']);
$sql = 'UPDATE ' . SEARCH_RESULTS_TABLE . '
$sql = 'UPDATE ' . $this->search_results_table . '
SET search_time = ' . time() . '
WHERE search_key = \'' . $this->db->sql_escape($search_key) . '\'';
$this->db->sql_query($sql);
@ -280,7 +287,7 @@ abstract class base implements search_backend_interface
}
$sql = 'SELECT search_key
FROM ' . SEARCH_RESULTS_TABLE . "
FROM ' . $this->search_results_table . "
WHERE search_keywords LIKE '%*%' $sql_where";
$result = $this->db->sql_query($sql);
@ -301,7 +308,7 @@ abstract class base implements search_backend_interface
}
$sql = 'SELECT search_key
FROM ' . SEARCH_RESULTS_TABLE . "
FROM ' . $this->search_results_table . "
WHERE $sql_where";
$result = $this->db->sql_query($sql);
@ -313,7 +320,7 @@ abstract class base implements search_backend_interface
}
$sql = 'DELETE
FROM ' . SEARCH_RESULTS_TABLE . '
FROM ' . $this->search_results_table . '
WHERE search_time < ' . (time() - (int) $this->config['search_store_results']);
$this->db->sql_query($sql);
}
@ -329,7 +336,7 @@ abstract class base implements search_backend_interface
$starttime = microtime(true);
$row_count = 0;
while (still_on_time() && $post_counter <= $max_post_id)
while (still_on_time() && $post_counter < $max_post_id)
{
$rows = $this->get_posts_batch_after($post_counter);
@ -346,7 +353,13 @@ abstract class base implements search_backend_interface
$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 = $row['post_id'];
$post_counter = (int) $row['post_id'];
}
// With cli process only one batch each time to be able to track progress
if (PHP_SAPI === 'cli')
{
break;
}
}
@ -357,7 +370,7 @@ abstract class base implements search_backend_interface
$this->tidy();
$this->config['num_posts'] = $num_posts;
if ($post_counter < $max_post_id)
if ($post_counter < $max_post_id) // If there are still post to index
{
$totaltime = microtime(true) - $starttime;
$rows_per_second = $row_count / $totaltime;
@ -382,7 +395,8 @@ abstract class base implements search_backend_interface
$starttime = microtime(true);
$row_count = 0;
while (still_on_time() && $post_counter <= $max_post_id)
while (still_on_time() && $post_counter < $max_post_id)
{
$rows = $this->get_posts_batch_after($post_counter);
$ids = $posters = $forum_ids = array();
@ -399,9 +413,15 @@ abstract class base implements search_backend_interface
$this->index_remove($ids, $posters, $forum_ids);
$post_counter = $ids[count($ids) - 1];
}
// With cli process only one batch each time to be able to track progress
if (PHP_SAPI === 'cli')
{
break;
}
}
if ($post_counter < $max_post_id)
if ($post_counter < $max_post_id) // If there are still post delete from index
{
$totaltime = microtime(true) - $starttime;
$rows_per_second = $row_count / $totaltime;

View file

@ -17,8 +17,8 @@ use phpbb\config\config;
use phpbb\db\driver\driver_interface;
use phpbb\event\dispatcher_interface;
use phpbb\language\language;
use phpbb\search\exception\search_exception;
use phpbb\user;
use RuntimeException;
/**
* Fulltext search for MySQL
@ -72,19 +72,20 @@ class fulltext_mysql extends base implements search_backend_interface
* Constructor
* Creates a new \phpbb\search\backend\fulltext_mysql, which is used as a search backend
*
* @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 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 $search_results_table
* @param string $phpbb_root_path Relative path to phpBB root
* @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)
public function __construct(config $config, driver_interface $db, dispatcher_interface $phpbb_dispatcher, language $language, user $user, string $search_results_table, string $phpbb_root_path, string $phpEx)
{
global $cache;
parent::__construct($cache, $config, $db, $user);
parent::__construct($cache, $config, $db, $user, $search_results_table);
$this->phpbb_dispatcher = $phpbb_dispatcher;
$this->language = $language;
@ -916,7 +917,7 @@ class fulltext_mysql extends base implements search_backend_interface
// Make sure we can actually use MySQL with fulltext indexes
if ($error = $this->init())
{
throw new RuntimeException($error);
throw new search_exception($error);
}
if (empty($this->stats))
@ -975,7 +976,7 @@ class fulltext_mysql extends base implements search_backend_interface
$this->db->sql_query($sql_query);
}
$this->db->sql_query('TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE);
$this->db->sql_query('TRUNCATE TABLE ' . $this->search_results_table);
return null;
}
@ -988,7 +989,7 @@ class fulltext_mysql extends base implements search_backend_interface
// Make sure we can actually use MySQL with fulltext indexes
if ($error = $this->init())
{
throw new RuntimeException($error);
throw new search_exception($error);
}
if (empty($this->stats))
@ -1041,7 +1042,7 @@ class fulltext_mysql extends base implements search_backend_interface
$this->db->sql_query($sql_query);
}
$this->db->sql_query('TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE);
$this->db->sql_query('TRUNCATE TABLE ' . $this->search_results_table);
return null;
}

View file

@ -93,30 +93,42 @@ class fulltext_native extends base implements search_backend_interface
*/
protected $phpbb_dispatcher;
/**
* @var language
*/
/** @var language */
protected $language;
/** @var string */
protected $search_wordlist_table;
/** @var string */
protected $search_wordmatch_table;
/**
* 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
* @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 $search_results_table
* @param string $search_wordlist_table
* @param string $search_wordmatch_table
* @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)
public function __construct(config $config, driver_interface $db, dispatcher_interface $phpbb_dispatcher,
language $language, user $user, string $search_results_table, string $search_wordlist_table,
string $search_wordmatch_table, string $phpbb_root_path, string $phpEx)
{
global $cache;
parent::__construct($cache, $config, $db, $user);
parent::__construct($cache, $config, $db, $user, $search_results_table);
$this->phpbb_dispatcher = $phpbb_dispatcher;
$this->language = $language;
$this->search_wordlist_table = $search_wordlist_table;
$this->search_wordmatch_table = $search_wordmatch_table;
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $phpEx;
@ -335,7 +347,7 @@ class fulltext_native extends base implements search_backend_interface
if (count($exact_words))
{
$sql = 'SELECT word_id, word_text, word_common
FROM ' . SEARCH_WORDLIST_TABLE . '
FROM ' . $this->search_wordlist_table . '
WHERE ' . $this->db->sql_in_set('word_text', $exact_words) . '
ORDER BY word_count ASC';
$result = $this->db->sql_query($sql);
@ -607,8 +619,8 @@ class fulltext_native extends base implements search_backend_interface
$sql_array = array(
'SELECT' => ($type == 'posts') ? 'DISTINCT p.post_id' : 'DISTINCT p.topic_id',
'FROM' => array(
SEARCH_WORDMATCH_TABLE => array(),
SEARCH_WORDLIST_TABLE => array(),
$this->search_wordmatch_table => array(),
$this->search_wordlist_table => array(),
),
'LEFT_JOIN' => array(array(
'FROM' => array(POSTS_TABLE => 'p'),
@ -660,7 +672,7 @@ class fulltext_native extends base implements search_backend_interface
if (is_string($id))
{
$sql_array['LEFT_JOIN'][] = array(
'FROM' => array(SEARCH_WORDLIST_TABLE => 'w' . $w_num),
'FROM' => array($this->search_wordlist_table => 'w' . $w_num),
'ON' => "w$w_num.word_text LIKE $id"
);
$word_ids[] = "w$w_num.word_id";
@ -680,7 +692,7 @@ class fulltext_native extends base implements search_backend_interface
}
else if (is_string($subquery))
{
$sql_array['FROM'][SEARCH_WORDLIST_TABLE][] = 'w' . $w_num;
$sql_array['FROM'][$this->search_wordlist_table][] = 'w' . $w_num;
$sql_where[] = "w$w_num.word_text LIKE $subquery";
$sql_where[] = "m$m_num.word_id = w$w_num.word_id";
@ -693,7 +705,7 @@ class fulltext_native extends base implements search_backend_interface
$sql_where[] = "m$m_num.word_id = $subquery";
}
$sql_array['FROM'][SEARCH_WORDMATCH_TABLE][] = 'm' . $m_num;
$sql_array['FROM'][$this->search_wordmatch_table][] = 'm' . $m_num;
if ($title_match)
{
@ -712,7 +724,7 @@ class fulltext_native extends base implements search_backend_interface
if (is_string($subquery))
{
$sql_array['LEFT_JOIN'][] = array(
'FROM' => array(SEARCH_WORDLIST_TABLE => 'w' . $w_num),
'FROM' => array($this->search_wordlist_table => 'w' . $w_num),
'ON' => "w$w_num.word_text LIKE $subquery"
);
@ -726,7 +738,7 @@ class fulltext_native extends base implements search_backend_interface
if (count($this->must_not_contain_ids))
{
$sql_array['LEFT_JOIN'][] = array(
'FROM' => array(SEARCH_WORDMATCH_TABLE => 'm' . $m_num),
'FROM' => array($this->search_wordmatch_table => 'm' . $m_num),
'ON' => $this->db->sql_in_set("m$m_num.word_id", $this->must_not_contain_ids) . (($title_match) ? " AND m$m_num.$title_match" : '') . " AND m$m_num.post_id = m0.post_id"
);
@ -742,7 +754,7 @@ class fulltext_native extends base implements search_backend_interface
if (is_string($id))
{
$sql_array['LEFT_JOIN'][] = array(
'FROM' => array(SEARCH_WORDLIST_TABLE => 'w' . $w_num),
'FROM' => array($this->search_wordlist_table => 'w' . $w_num),
'ON' => "w$w_num.word_text LIKE $id"
);
$id = "w$w_num.word_id";
@ -752,7 +764,7 @@ class fulltext_native extends base implements search_backend_interface
}
$sql_array['LEFT_JOIN'][] = array(
'FROM' => array(SEARCH_WORDMATCH_TABLE => 'm' . $m_num),
'FROM' => array($this->search_wordmatch_table => 'm' . $m_num),
'ON' => "m$m_num.word_id = $id AND m$m_num.post_id = m0.post_id" . (($title_match) ? " AND m$m_num.$title_match" : '')
);
$is_null_joins[] = "m$m_num.word_id IS NULL";
@ -1310,7 +1322,7 @@ class fulltext_native extends base implements search_backend_interface
$words['del']['title'] = array();
$sql = 'SELECT w.word_id, w.word_text, m.title_match
FROM ' . SEARCH_WORDLIST_TABLE . ' w, ' . SEARCH_WORDMATCH_TABLE . " m
FROM ' . $this->search_wordlist_table . ' w, ' . $this->search_wordmatch_table . " m
WHERE m.post_id = $post_id
AND w.word_id = m.word_id";
$result = $this->db->sql_query($sql);
@ -1379,7 +1391,7 @@ class fulltext_native extends base implements search_backend_interface
if (count($unique_add_words))
{
$sql = 'SELECT word_id, word_text
FROM ' . SEARCH_WORDLIST_TABLE . '
FROM ' . $this->search_wordlist_table . '
WHERE ' . $this->db->sql_in_set('word_text', $unique_add_words);
$result = $this->db->sql_query($sql);
@ -1401,7 +1413,7 @@ class fulltext_native extends base implements search_backend_interface
$sql_ary[] = array('word_text' => (string) $word, 'word_count' => 0);
}
$this->db->sql_return_on_error(true);
$this->db->sql_multi_insert(SEARCH_WORDLIST_TABLE, $sql_ary);
$this->db->sql_multi_insert($this->search_wordlist_table, $sql_ary);
$this->db->sql_return_on_error(false);
}
unset($new_words, $sql_ary);
@ -1424,13 +1436,13 @@ class fulltext_native extends base implements search_backend_interface
$sql_in[] = $cur_words[$word_in][$word];
}
$sql = 'DELETE FROM ' . SEARCH_WORDMATCH_TABLE . '
$sql = 'DELETE FROM ' . $this->search_wordmatch_table . '
WHERE ' . $this->db->sql_in_set('word_id', $sql_in) . '
AND post_id = ' . intval($post_id) . "
AND title_match = $title_match";
$this->db->sql_query($sql);
$sql = 'UPDATE ' . SEARCH_WORDLIST_TABLE . '
$sql = 'UPDATE ' . $this->search_wordlist_table . '
SET word_count = word_count - 1
WHERE ' . $this->db->sql_in_set('word_id', $sql_in) . '
AND word_count > 0';
@ -1447,13 +1459,13 @@ class fulltext_native extends base implements search_backend_interface
if (count($word_ary))
{
$sql = 'INSERT INTO ' . SEARCH_WORDMATCH_TABLE . ' (post_id, word_id, title_match)
$sql = 'INSERT INTO ' . $this->search_wordmatch_table . ' (post_id, word_id, title_match)
SELECT ' . (int) $post_id . ', word_id, ' . (int) $title_match . '
FROM ' . SEARCH_WORDLIST_TABLE . '
FROM ' . $this->search_wordlist_table . '
WHERE ' . $this->db->sql_in_set('word_text', $word_ary);
$this->db->sql_query($sql);
$sql = 'UPDATE ' . SEARCH_WORDLIST_TABLE . '
$sql = 'UPDATE ' . $this->search_wordlist_table . '
SET word_count = word_count + 1
WHERE ' . $this->db->sql_in_set('word_text', $word_ary);
$this->db->sql_query($sql);
@ -1479,7 +1491,7 @@ class fulltext_native extends base implements search_backend_interface
if (count($post_ids))
{
$sql = 'SELECT w.word_id, w.word_text, m.title_match
FROM ' . SEARCH_WORDMATCH_TABLE . ' m, ' . SEARCH_WORDLIST_TABLE . ' w
FROM ' . $this->search_wordmatch_table . ' m, ' . $this->search_wordlist_table . ' w
WHERE ' . $this->db->sql_in_set('m.post_id', $post_ids) . '
AND w.word_id = m.word_id';
$result = $this->db->sql_query($sql);
@ -1501,7 +1513,7 @@ class fulltext_native extends base implements search_backend_interface
if (count($title_word_ids))
{
$sql = 'UPDATE ' . SEARCH_WORDLIST_TABLE . '
$sql = 'UPDATE ' . $this->search_wordlist_table . '
SET word_count = word_count - 1
WHERE ' . $this->db->sql_in_set('word_id', $title_word_ids) . '
AND word_count > 0';
@ -1510,7 +1522,7 @@ class fulltext_native extends base implements search_backend_interface
if (count($message_word_ids))
{
$sql = 'UPDATE ' . SEARCH_WORDLIST_TABLE . '
$sql = 'UPDATE ' . $this->search_wordlist_table . '
SET word_count = word_count - 1
WHERE ' . $this->db->sql_in_set('word_id', $message_word_ids) . '
AND word_count > 0';
@ -1520,7 +1532,7 @@ class fulltext_native extends base implements search_backend_interface
unset($title_word_ids);
unset($message_word_ids);
$sql = 'DELETE FROM ' . SEARCH_WORDMATCH_TABLE . '
$sql = 'DELETE FROM ' . $this->search_wordmatch_table . '
WHERE ' . $this->db->sql_in_set('post_id', $post_ids);
$this->db->sql_query($sql);
}
@ -1549,7 +1561,7 @@ class fulltext_native extends base implements search_backend_interface
$common_threshold = ((double) $this->config['fulltext_native_common_thres']) / 100.0;
// First, get the IDs of common words
$sql = 'SELECT word_id, word_text
FROM ' . SEARCH_WORDLIST_TABLE . '
FROM ' . $this->search_wordlist_table . '
WHERE word_count > ' . floor($this->config['num_posts'] * $common_threshold) . '
OR word_common = 1';
$result = $this->db->sql_query($sql);
@ -1565,7 +1577,7 @@ class fulltext_native extends base implements search_backend_interface
if (count($sql_in))
{
// Flag the words
$sql = 'UPDATE ' . SEARCH_WORDLIST_TABLE . '
$sql = 'UPDATE ' . $this->search_wordlist_table . '
SET word_common = 1
WHERE ' . $this->db->sql_in_set('word_id', $sql_in);
$this->db->sql_query($sql);
@ -1575,7 +1587,7 @@ class fulltext_native extends base implements search_backend_interface
$this->config->set('search_last_gc', time(), false);
// Delete the matches
$sql = 'DELETE FROM ' . SEARCH_WORDMATCH_TABLE . '
$sql = 'DELETE FROM ' . $this->search_wordmatch_table . '
WHERE ' . $this->db->sql_in_set('word_id', $sql_in);
$this->db->sql_query($sql);
}
@ -1603,15 +1615,15 @@ class fulltext_native extends base implements search_backend_interface
switch ($this->db->get_sql_layer())
{
case 'sqlite3':
$sql_queries[] = 'DELETE FROM ' . SEARCH_WORDLIST_TABLE;
$sql_queries[] = 'DELETE FROM ' . SEARCH_WORDMATCH_TABLE;
$sql_queries[] = 'DELETE FROM ' . SEARCH_RESULTS_TABLE;
$sql_queries[] = 'DELETE FROM ' . $this->search_wordlist_table;
$sql_queries[] = 'DELETE FROM ' . $this->search_wordmatch_table;
$sql_queries[] = 'DELETE FROM ' . $this->search_results_table;
break;
default:
$sql_queries[] = 'TRUNCATE TABLE ' . SEARCH_WORDLIST_TABLE;
$sql_queries[] = 'TRUNCATE TABLE ' . SEARCH_WORDMATCH_TABLE;
$sql_queries[] = 'TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE;
$sql_queries[] = 'TRUNCATE TABLE ' . $this->search_wordlist_table;
$sql_queries[] = 'TRUNCATE TABLE ' . $this->search_wordmatch_table;
$sql_queries[] = 'TRUNCATE TABLE ' . $this->search_results_table;
break;
}
@ -1672,8 +1684,8 @@ class fulltext_native extends base implements search_backend_interface
*/
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);
$this->stats['total_words'] = $this->db->get_estimated_row_count($this->search_wordlist_table);
$this->stats['total_matches'] = $this->db->get_estimated_row_count($this->search_wordmatch_table);
}
/**

View file

@ -17,8 +17,8 @@ use phpbb\config\config;
use phpbb\db\driver\driver_interface;
use phpbb\event\dispatcher_interface;
use phpbb\language\language;
use phpbb\search\exception\search_exception;
use phpbb\user;
use RuntimeException;
/**
* Fulltext search for PostgreSQL
@ -84,19 +84,20 @@ class fulltext_postgres extends base implements search_backend_interface
* Constructor
* Creates a new \phpbb\search\backend\fulltext_postgres, which is used as a search backend
*
* @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 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 $search_results_table
* @param string $phpbb_root_path Relative path to phpBB root
* @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)
public function __construct(config $config, driver_interface $db, dispatcher_interface $phpbb_dispatcher, language $language, user $user, string $search_results_table, string $phpbb_root_path, string $phpEx)
{
global $cache;
parent::__construct($cache, $config, $db, $user);
parent::__construct($cache, $config, $db, $user, $search_results_table);
$this->phpbb_dispatcher = $phpbb_dispatcher;
$this->language = $language;
@ -871,7 +872,7 @@ class fulltext_postgres extends base implements search_backend_interface
// Make sure we can actually use PostgreSQL with fulltext indexes
if ($error = $this->init())
{
throw new RuntimeException($error);
throw new search_exception($error);
}
if (empty($this->stats))
@ -917,7 +918,7 @@ class fulltext_postgres extends base implements search_backend_interface
$this->db->sql_query($sql_query);
}
$this->db->sql_query('TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE);
$this->db->sql_query('TRUNCATE TABLE ' . $this->search_results_table);
return null;
}
@ -930,7 +931,7 @@ class fulltext_postgres extends base implements search_backend_interface
// Make sure we can actually use PostgreSQL with fulltext indexes
if ($error = $this->init())
{
throw new RuntimeException($error);
throw new search_exception($error);
}
if (empty($this->stats))
@ -976,7 +977,7 @@ class fulltext_postgres extends base implements search_backend_interface
$this->db->sql_query($sql_query);
}
$this->db->sql_query('TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE);
$this->db->sql_query('TRUNCATE TABLE ' . $this->search_results_table);
return null;
}

View file

@ -0,0 +1,18 @@
<?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\exception;
class action_in_progress_exception extends search_exception
{
}

View file

@ -0,0 +1,18 @@
<?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\exception;
class no_action_in_progress_exception extends search_exception
{
}

View file

@ -0,0 +1,18 @@
<?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\exception;
class no_search_backend_found_exception extends search_exception
{
}

View file

@ -0,0 +1,20 @@
<?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\exception;
use phpbb\exception\runtime_exception;
class search_exception extends runtime_exception
{
}

View file

@ -14,8 +14,10 @@
namespace phpbb\search;
use phpbb\config\config;
use phpbb\di\exception\service_not_found_exception;
use phpbb\di\service_collection;
use phpbb\search\backend\search_backend_interface;
use phpbb\search\exception\no_search_backend_found_exception;
class search_backend_factory
{
@ -46,16 +48,29 @@ class search_backend_factory
*
* @param string $class
*
* @throws no_search_backend_found_exception
*
* @return search_backend_interface
*/
public function get(string $class): search_backend_interface
{
return $this->search_backends->get_by_class($class);
try
{
$search = $this->search_backends->get_by_class($class);
}
catch (service_not_found_exception $e)
{
throw new no_search_backend_found_exception('SEARCH_BACKEND_NOT_FOUND', [], $e);
}
return $search;
}
/**
* Obtains active search backend
*
* @throws no_search_backend_found_exception
*
* @return search_backend_interface
*/
public function get_active(): search_backend_interface

View file

@ -0,0 +1,181 @@
<?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\search\exception\action_in_progress_exception;
use phpbb\search\exception\no_action_in_progress_exception;
use phpbb\search\exception\no_search_backend_found_exception;
use phpbb\search\exception\search_exception;
class state_helper
{
protected const STATE_SEARCH_TYPE = 0;
protected const STATE_ACTION = 1;
protected const STATE_POST_COUNTER = 2;
/** @var config */
protected $config;
/** @var search_backend_factory */
protected $search_backend_factory;
/**
* Constructor.
*
* @param config $config
* @param search_backend_factory $search_backend_factory
*/
public function __construct(config $config, search_backend_factory $search_backend_factory)
{
$this->config = $config;
$this->search_backend_factory = $search_backend_factory;
}
/**
* Returns whether there is an action in progress
*
* @return bool
*/
public function is_action_in_progress(): bool
{
return !empty($this->config['search_indexing_state']);
}
/**
* @return string The class name of the search backend
*
* @throws no_action_in_progress_exception If there is no action in progress
*/
public function type(): string
{
$state = $this->load_state();
return $state[self::STATE_SEARCH_TYPE];
}
/**
* @return string The action that is being executed, can be 'create' or 'delete'
*
* @throws no_action_in_progress_exception If there is no action in progress
*/
public function action(): string
{
$state = $this->load_state();
return $state[self::STATE_ACTION];
}
/**
* @return int The post counter
*
* @throws no_action_in_progress_exception If there is no action in progress
*/
public function counter(): int
{
$state = $this->load_state();
return $state[self::STATE_POST_COUNTER];
}
/**
* Start a indexing or delete process.
*
* @param string $search_type
* @param string $action
*
* @throws action_in_progress_exception If there is an action in progress
* @throws no_search_backend_found_exception If search backend don't exist
* @throws search_exception If action isn't valid
*/
public function init(string $search_type, string $action): void
{
// It's not possible to start a new process when there is one already running
if ($this->is_action_in_progress())
{
throw new action_in_progress_exception();
}
// Make sure the search type exists (if not, throw an exception)
$this->search_backend_factory->get($search_type);
// Make sure the action is correct (just in case)
if (!in_array($action, ['create', 'delete']))
{
throw new search_exception('INVALID_ACTION');
}
$state = [
self::STATE_SEARCH_TYPE => $search_type,
self::STATE_ACTION => $action,
self::STATE_POST_COUNTER => 0
];
$this->save_state($state);
}
/**
* Set the post counter
*
* @param int $counter
*
* @throws no_action_in_progress_exception If there is no action in progress
*/
public function update_counter(int $counter): void
{
$state = $this->load_state();
$state[self::STATE_POST_COUNTER] = $counter;
$this->save_state($state);
}
/**
* Clear the state
*/
public function clear_state(): void
{
$this->save_state();
}
/**
* Load the state from the database
*
* @return array
*
* @throws no_action_in_progress_exception If there is no action in progress
*/
private function load_state(): array
{
// Is not possible to execute an action over state if it's empty
if (!$this->is_action_in_progress())
{
throw new no_action_in_progress_exception();
}
return explode(',', $this->config['search_indexing_state']);
}
/**
* Save the specified state in the database
*
* @param array $state
*/
private function save_state(array $state = []): void
{
ksort($state);
$this->config->set('search_indexing_state', implode(',', $state));
}
}

View file

@ -299,16 +299,9 @@ if ($keywords || $author || $author_id || $search_id || $submit)
$search_backend_factory = $phpbb_container->get('search.backend_factory');
$search = $search_backend_factory->get_active();
}
catch (RuntimeException $e)
catch (\phpbb\search\exception\no_search_backend_found_exception $e)
{
if (strpos($e->getMessage(), 'No service found') === 0)
{
trigger_error('NO_SUCH_SEARCH_MODULE');
}
else
{
throw $e;
}
trigger_error('NO_SUCH_SEARCH_MODULE');
}
// let the search module split up the keywords

View file

@ -0,0 +1,92 @@
<?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.
*
*/
use phpbb\console\command\searchindex\create;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;
require_once __DIR__ . '/phpbb_console_searchindex_base.php';
require_once __DIR__ . '/../../mock/search_backend_mock.php';
class phpbb_console_searchindex_create_test extends phpbb_console_searchindex_base
{
public function get_command_tester()
{
$application = new Application();
$application->add(new create(
$this->language,
$this->log,
$this->post_helper,
$this->search_backend_factory,
$this->state_helper,
$this->user
));
$command = $application->find('searchindex:create');
return new CommandTester($command);
}
public function test_create()
{
$command_tester = $this->get_command_tester();
$command_tester->execute([
'search-backend' => 'search_backend_mock',
]);
$this->assertEquals(Command::SUCCESS, $command_tester->getStatusCode());
$this->assertStringContainsString('CLI_SEARCHINDEX_CREATE_SUCCESS', $command_tester->getDisplay());
}
public function test_create_when_search_backend_dont_exist()
{
$command_tester = $this->get_command_tester();
$command_tester->execute([
'search-backend' => 'missing',
]);
$this->assertEquals(Command::FAILURE, $command_tester->getStatusCode());
$this->assertStringContainsString('CLI_SEARCHINDEX_BACKEND_NOT_FOUND', $command_tester->getDisplay());
}
public function test_create_when_action_in_progress()
{
$this->config['search_indexing_state'] = ['not', 'empty'];
$command_tester = $this->get_command_tester();
$command_tester->execute([
'search-backend' => 'search_backend_mock',
]);
$this->assertEquals(Command::FAILURE, $command_tester->getStatusCode());
$this->assertStringContainsString('CLI_SEARCHINDEX_ACTION_IN_PROGRESS', $command_tester->getDisplay());
$this->config['search_indexing_state'] = [];
}
public function test_create_when_search_backend_not_available()
{
$command_tester = $this->get_command_tester();
$command_tester->execute([
'search-backend' => 'search_backend_mock_not_available',
]);
$this->assertEquals(Command::FAILURE, $command_tester->getStatusCode());
$this->assertStringContainsString('CLI_SEARCHINDEX_BACKEND_NOT_AVAILABLE', $command_tester->getDisplay());
}
}

View file

@ -0,0 +1,92 @@
<?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.
*
*/
use phpbb\console\command\searchindex\delete;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;
require_once __DIR__ . '/phpbb_console_searchindex_base.php';
require_once __DIR__ . '/../../mock/search_backend_mock.php';
class phpbb_console_searchindex_delete_test extends phpbb_console_searchindex_base
{
public function get_command_tester()
{
$application = new Application();
$application->add(new delete(
$this->language,
$this->log,
$this->post_helper,
$this->search_backend_factory,
$this->state_helper,
$this->user
));
$command = $application->find('searchindex:delete');
return new CommandTester($command);
}
public function test_delete()
{
$command_tester = $this->get_command_tester();
$command_tester->execute([
'search-backend' => 'search_backend_mock',
]);
$this->assertEquals(Command::SUCCESS, $command_tester->getStatusCode());
$this->assertStringContainsString('CLI_SEARCHINDEX_DELETE_SUCCESS', $command_tester->getDisplay());
}
public function test_delete_when_search_backend_dont_exist()
{
$command_tester = $this->get_command_tester();
$command_tester->execute([
'search-backend' => 'missing',
]);
$this->assertEquals(Command::FAILURE, $command_tester->getStatusCode());
$this->assertStringContainsString('CLI_SEARCHINDEX_BACKEND_NOT_FOUND', $command_tester->getDisplay());
}
public function test_delete_when_action_in_progress()
{
$this->config['search_indexing_state'] = ['not', 'empty'];
$command_tester = $this->get_command_tester();
$command_tester->execute([
'search-backend' => 'search_backend_mock',
]);
$this->assertEquals(Command::FAILURE, $command_tester->getStatusCode());
$this->assertStringContainsString('CLI_SEARCHINDEX_ACTION_IN_PROGRESS', $command_tester->getDisplay());
$this->config['search_indexing_state'] = [];
}
public function test_delete_when_search_backend_not_available()
{
$command_tester = $this->get_command_tester();
$command_tester->execute([
'search-backend' => 'search_backend_mock_not_available',
]);
$this->assertEquals(Command::FAILURE, $command_tester->getStatusCode());
$this->assertStringContainsString('CLI_SEARCHINDEX_BACKEND_NOT_AVAILABLE', $command_tester->getDisplay());
}
}

View file

@ -0,0 +1,49 @@
<?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.
*
*/
use phpbb\console\command\searchindex\list_all;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;
require_once __DIR__ . '/phpbb_console_searchindex_base.php';
require_once __DIR__ . '/../../mock/search_backend_mock.php';
class phpbb_console_searchindex_list_all_test extends phpbb_console_searchindex_base
{
public function get_command_tester()
{
$application = new Application();
$application->add(new list_all(
$this->config,
$this->language,
$this->search_backend_collection,
$this->user
));
$command = $application->find('searchindex:list');
return new CommandTester($command);
}
public function test_list()
{
$command_tester = $this->get_command_tester();
$command_tester->execute([]);
$this->assertEquals(Command::SUCCESS, $command_tester->getStatusCode());
$this->assertStringContainsString('Mock search backend', $command_tester->getDisplay());
$this->assertStringContainsString('ACTIVE', $command_tester->getDisplay());
}
}

View file

@ -0,0 +1,93 @@
<?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.
*
*/
use phpbb\config\config;
use phpbb\di\service_collection;
use phpbb\language\language;
use phpbb\log\log;
use phpbb\posting\post_helper;
use phpbb\search\search_backend_factory;
use phpbb\search\state_helper;
use phpbb\user;
require_once __DIR__ . '/../../mock/search_backend_mock.php';
require_once __DIR__ . '/../../mock/search_backend_mock_not_available.php';
class phpbb_console_searchindex_base extends phpbb_test_case
{
/** @var config */
protected $config;
/** @var language */
protected $language;
/** @var log */
protected $log;
/** @var post_helper */
protected $post_helper;
/** @var user */
protected $user;
/** @var search_backend_factory */
protected $search_backend_factory;
/** @var state_helper */
protected $state_helper;
/** @var service_collection */
protected $search_backend_collection;
protected function setUp(): void
{
global $phpbb_root_path, $phpEx;
$this->config = new \phpbb\config\config([
'search_indexing_state' => [],
'search_type' => 'search_backend_mock'
]);
$lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx);
$this->language = new \phpbb\language\language($lang_loader);
$this->log = $this->createMock('\phpbb\log\log');
$this->post_helper = $this->createMock('\phpbb\posting\post_helper');
$this->post_helper
->method('get_max_post_id')
->willReturn(1000);
$this->user = $this->createMock('\phpbb\user');
$phpbb_container = new phpbb_mock_container_builder();
$this->search_backend_collection = new \phpbb\di\service_collection($phpbb_container);
$search_backend_mock = new search_backend_mock();
$this->search_backend_collection->add('search_backend_mock');
$this->search_backend_collection->add_service_class('search_backend_mock', 'search_backend_mock');
$phpbb_container->set('search_backend_mock', $search_backend_mock);
$search_backend_mock_not_available = new search_backend_mock_not_available();
$this->search_backend_collection->add('search_backend_mock_not_available');
$this->search_backend_collection->add_service_class('search_backend_mock_not_available', 'search_backend_mock_not_available');
$phpbb_container->set('search_backend_mock_not_available', $search_backend_mock_not_available);
$this->search_backend_factory = new search_backend_factory($this->config, $this->search_backend_collection);
$this->state_helper = new state_helper($this->config, $this->search_backend_factory);
parent::setUp();
}
}

View file

@ -57,7 +57,7 @@ class phpbb_service_collection_test extends \phpbb_test_case
public function test_get_by_class_many_services_exception()
{
$this->expectException('RuntimeException');
$this->expectExceptionMessage('More than one service definitions found for class "bar_class" in collection.');
$this->expectExceptionMessage('DI_MULTIPLE_SERVICE_DEFINITIONS');
$this->service_collection->get_by_class('bar_class');
}
@ -65,7 +65,7 @@ class phpbb_service_collection_test extends \phpbb_test_case
public function test_get_by_class_no_service_exception()
{
$this->expectException('RuntimeException');
$this->expectExceptionMessage('No service found for class "baz_class" in collection.');
$this->expectExceptionMessage('DI_SERVICE_NOT_FOUND');
$this->service_collection->get_by_class('baz_class');
}

View file

@ -0,0 +1,117 @@
<?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.
*
*/
use phpbb\search\backend\search_backend_interface;
class search_backend_mock implements search_backend_interface
{
public $index_created = false;
public function get_name(): string
{
return 'Mock search backend';
}
public function is_available(): bool
{
return true;
}
public function init()
{
return false;
}
public function get_search_query(): string
{
return '';
}
public function get_common_words(): array
{
return [];
}
public function get_word_length()
{
return false;
}
public function split_keywords(string &$keywords, string $terms): bool
{
return false;
}
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)
{
return 0;
}
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)
{
return 0;
}
public function supports_phrase_search(): bool
{
return false;
}
public function index(string $mode, int $post_id, string &$message, string &$subject, int $poster_id, int $forum_id)
{
// Nothing
}
public function index_remove(array $post_ids, array $author_ids, array $forum_ids): void
{
// Nothing
}
public function tidy(): void
{
// Nothing
}
public function create_index(int &$post_counter = 0): ?array
{
$this->index_created = true;
return null;
}
public function delete_index(int &$post_counter = 0): ?array
{
$this->index_created = true;
return null;
}
public function index_created(): bool
{
return $this->index_created;
}
public function index_stats()
{
return [];
}
public function get_acp_options(): array
{
return [];
}
public function get_type(): string
{
return static::class;
}
}

View file

@ -0,0 +1,25 @@
<?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 search_backend_mock_not_available extends search_backend_mock
{
public function get_name(): string
{
return 'Mock unavailable search backend';
}
public function is_available(): bool
{
return false;
}
}

View file

@ -40,6 +40,6 @@ class phpbb_search_mysql_test extends phpbb_search_common_test_case
$this->db = $this->new_dbal();
$phpbb_dispatcher = new phpbb_mock_event_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);
$this->search = new $class($config, $this->db, $phpbb_dispatcher, $language, $user, SEARCH_RESULTS_TABLE, $phpbb_root_path, $phpEx);
}
}

View file

@ -38,7 +38,7 @@ class phpbb_search_native_test extends phpbb_search_test_case
$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($config, $this->db, $phpbb_dispatcher, $language, $user, $phpbb_root_path, $phpEx);
$this->search = new $class($config, $this->db, $phpbb_dispatcher, $language, $user, SEARCH_RESULTS_TABLE, SEARCH_WORDLIST_TABLE, SEARCH_WORDMATCH_TABLE, $phpbb_root_path, $phpEx);
}
public function keywords()

View file

@ -40,6 +40,6 @@ class phpbb_search_postgres_test extends phpbb_search_common_test_case
$this->db = $this->new_dbal();
$phpbb_dispatcher = new phpbb_mock_event_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);
$this->search = new $class($config, $this->db, $phpbb_dispatcher, $language, $user, SEARCH_RESULTS_TABLE, $phpbb_root_path, $phpEx);
}
}