ManageSearch   F
last analyzed

Complexity

Total Complexity 81

Size/Duplication

Total Lines 672
Duplicated Lines 0 %

Test Coverage

Coverage 4.33%

Importance

Changes 0
Metric Value
eloc 291
c 0
b 0
f 0
dl 0
loc 672
ccs 12
cts 277
cp 0.0433
rs 2
wmc 81

12 Methods

Rating   Name   Duplication   Size   Complexity  
A action_index() 0 39 1
A connectSphinxApi() 0 34 6
B action_managesphinx() 0 53 10
A _saveSphinxConfig() 0 12 1
B loadSearchAPIs() 0 36 9
F action_edit() 0 123 21
A settings_search() 0 3 1
A _settings() 0 26 2
A action_weight() 0 48 5
C action_searchSettings_display() 0 81 12
A connectSphinxQL() 0 32 6
B action_create() 0 68 7

How to fix   Complexity   

Complex Class

Complex classes like ManageSearch often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ManageSearch, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * The admin screen to change the search settings.  Allows for the creation \
5
 * of search indexes and search weights
6
 *
7
 * @package   ElkArte Forum
8
 * @copyright ElkArte Forum contributors
9
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
10
 *
11
 * This file contains code covered by:
12
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
13
 *
14
 * @version 2.0 dev
15
 *
16
 */
17
18
namespace ElkArte\AdminController;
19
20
use ElkArte\AbstractController;
21
use ElkArte\Action;
22
use ElkArte\Helper\FileFunctions;
23
use ElkArte\Helper\Util;
24
use ElkArte\Languages\Txt;
25
use ElkArte\Search\SearchApiWrapper;
26
use ElkArte\SettingsForm\SettingsForm;
27
use Exception;
28
use FilesystemIterator;
29
use GlobIterator;
30
use SphinxClient;
0 ignored issues
show
Bug introduced by
The type SphinxClient was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
31
use UnexpectedValueException;
32
33
/**
34
 * ManageSearch controller admin class.
35
 *
36
 * @package Search
37
 */
38
class ManageSearch extends AbstractController
39
{
40
	/**
41
	 * Main entry point for the admin search settings screen.
42
	 *
43
	 * What it does:
44
	 *
45
	 * - It checks permissions, and it forwards to the appropriate function based on
46
	 * the given sub-action.
47
	 * - Defaults to sub-action 'settings'.
48
	 * - Called by ?action=admin;area=managesearch.
49
	 * - Requires the admin_forum permission.
50
	 *
51
	 * @event integrate_sa_manage_search add new search actions
52
	 * @uses ManageSearch template.
53
	 * @uses Search language file.
54
	 * @see  AbstractController::action_index()
55
	 */
56
	public function action_index()
57
	{
58
		global $context, $txt;
59
60
		Txt::load('Search');
61
		theme()->getTemplates()->load('ManageSearch');
62
63
		$subActions = array(
64
			'settings' => array($this, 'action_searchSettings_display', 'permission' => 'admin_forum'),
65
			'weights' => array($this, 'action_weight', 'permission' => 'admin_forum'),
66
			'method' => array($this, 'action_edit', 'permission' => 'admin_forum'),
67
			'createfulltext' => array($this, 'action_edit', 'permission' => 'admin_forum'),
68
			'removecustom' => array($this, 'action_edit', 'permission' => 'admin_forum'),
69
			'removefulltext' => array($this, 'action_edit', 'permission' => 'admin_forum'),
70
			'createmsgindex' => array($this, 'action_create', 'permission' => 'admin_forum'),
71
			'managesphinx' => array($this, 'action_managesphinx', 'permission' => 'admin_forum'),
72
			'managesphinxql' => array($this, 'action_managesphinx', 'permission' => 'admin_forum'),
73
		);
74
75
		// Control for actions
76
		$action = new Action('manage_search');
77
78
		// Default the sub-action to 'edit search method'.  Call integrate_sa_manage_search
79
		$subAction = $action->initialize($subActions, 'method');
80
81
		// Final bits
82
		$context['sub_action'] = $subAction;
83
		$context['page_title'] = $txt['search_settings_title'];
84
85
		// Create the tabs
86
		$context[$context['admin_menu_name']]['object']->prepareTabData([
87
			'title' => 'manage_search',
88
			'description' => 'search_settings_desc',
89
			'prefix' => 'search',
90
			'help' => 'search']
91
		);
92
93
		// Call the right function for this sub-action.
94
		$action->dispatch($subAction);
95
	}
96
97
	/**
98
	 * Edit some general settings related to the search function.
99
	 *
100
	 * - Called by ?action=admin;area=managesearch;sa=settings.
101
	 * - Requires the admin_forum permission.
102
	 *
103
	 * @event integrate_save_search_settings
104
	 * @uses ManageSearch template, 'modify_settings' sub-template.
105
	 */
106
	public function action_searchSettings_display()
107
	{
108
		global $txt, $context, $modSettings;
109
110
		// Initialize the form
111
		$settingsForm = new SettingsForm(SettingsForm::DB_ADAPTER);
112
113
		// Initialize it with our settings
114
		$settingsForm->setConfigVars($this->_settings());
115
116
		$context['page_title'] = $txt['search_settings_title'];
117
		$context['sub_template'] = 'show_settings';
118
119
		$context['search_engines'] = array();
120
		if (!empty($modSettings['additional_search_engines']))
121
		{
122
			$context['search_engines'] = Util::unserialize($modSettings['additional_search_engines']);
123
		}
124
125
		for ($count = 0; $count < 3; $count++)
126
		{
127
			$context['search_engines'][] = array(
128
				'name' => '',
129
				'url' => '',
130
				'separator' => '',
131
			);
132
		}
133
134
		// A form was submitted.
135
		if (isset($this->_req->query->save))
136
		{
137
			checkSession();
138
139
			call_integration_hook('integrate_save_search_settings');
140
141
			if (empty($this->_req->post->search_results_per_page))
142
			{
143
				$this->_req->post->search_results_per_page = empty($modSettings['search_results_per_page']) ? $modSettings['defaultMaxMessages'] : $modSettings['search_results_per_page'];
144
			}
145
146
			$new_engines = array();
147
			foreach ($this->_req->post->engine_name as $id => $searchengine)
148
			{
149
				$url = trim(str_replace(array('"', '<', '>'), array('&quot;', '&lt;', '&gt;'), $this->_req->post->engine_url[$id]));
150
				// If no url, forget it
151
				if (empty($searchengine))
152
				{
153
					continue;
154
				}
155
156
				if (empty($url))
157
				{
158
					continue;
159
				}
160
161
				if (!filter_var($url, FILTER_VALIDATE_URL))
162
				{
163
					continue;
164
				}
165
166
				$new_engines[] = array(
167
					'name' => trim(Util::htmlspecialchars($searchengine, ENT_COMPAT)),
168
					'url' => $url,
169
					'separator' => trim(Util::htmlspecialchars(empty($this->_req->post->engine_separator[$id]) ? '+' : $this->_req->post->engine_separator[$id], ENT_COMPAT)),
170
				);
171
			}
172
173
			updateSettings(array(
174
				'additional_search_engines' => $new_engines === [] ? '' : serialize($new_engines)
175
			));
176
177
			$settingsForm->setConfigValues((array) $this->_req->post);
178
			$settingsForm->save();
179
			redirectexit('action=admin;area=managesearch;sa=settings;' . $context['session_var'] . '=' . $context['session_id']);
180
		}
181
182
		// Prep the template!
183
		$context['post_url'] = getUrl('admin', ['action' => 'admin', 'area' => 'managesearch', 'save', 'sa' => 'settings']);
184 2
		$context['settings_title'] = $txt['search_settings_title'];
185
186 2
		$settingsForm->prepare();
187
	}
188
189
	/**
190
	 * Retrieve admin search settings
191 2
	 *
192
	 * @event integrate_modify_search_settings
193
	 */
194
	private function _settings()
195 2
	{
196 2
		global $txt, $modSettings;
197
198 2
		// What are we editing anyway?
199
		$config_vars = array(
200
			// Permission...
201
			array('permissions', 'search_posts', 'collapsed' => 'true'),
202
			// Some simple settings.
203
			array('check', 'search_dropdown'),
204 2
			array('int', 'search_results_per_page'),
205 2
			array('int', 'search_max_results', 'subtext' => $txt['search_max_results_disable']),
206
			// Some limitations.
207
			array('int', 'search_floodcontrol_time', 'subtext' => $txt['search_floodcontrol_time_desc'], 6, 'postinput' => $txt['seconds']),
208 2
			array('title', 'additional_search_engines'),
209
			array('callback', 'external_search_engines'),
210 2
		);
211
212
		// Perhaps the search method wants to add some settings?
213
		$searchAPI = new SearchApiWrapper(empty($modSettings['search_index']) ? '' : $modSettings['search_index']);
214
		$searchAPI->searchSettings($config_vars);
215
216 2
		// Add new settings with a nice hook, makes them available for admin settings search as well
217
		call_integration_hook('integrate_modify_search_settings', array(&$config_vars));
218 2
219
		return $config_vars;
220
	}
221
222
	/**
223
	 * Return the search settings for use in admin search
224
	 */
225
	public function settings_search()
226
	{
227
		return $this->_settings();
228
	}
229
230
	/**
231
	 * Edit the relative weight of the search factors.
232
	 *
233
	 * - Called by ?action=admin;area=managesearch;sa=weights.
234
	 * - Requires the admin_forum permission.
235
	 *
236
	 * @event integrate_modify_search_weights
237
	 * @event integrate_save_search_weights
238
	 * @uses ManageSearch template, 'modify_weights' sub-template.
239
	 */
240
	public function action_weight()
241
	{
242
		global $txt, $context, $modSettings;
243
244
		$context['page_title'] = $txt['search_weights_title'];
245
		$context['sub_template'] = 'modify_weights';
246
247
		$factors = array(
248
			'search_weight_frequency',
249
			'search_weight_age',
250
			'search_weight_length',
251
			'search_weight_subject',
252
			'search_weight_first_message',
253
			'search_weight_sticky',
254
			'search_weight_likes',
255
		);
256
257
		call_integration_hook('integrate_modify_search_weights', array(&$factors));
258
259
		// A form was submitted.
260
		if (isset($this->_req->post->save))
261
		{
262
			checkSession();
263
			validateToken('admin-msw');
264
265
			call_integration_hook('integrate_save_search_weights');
266
267
			$changes = array();
268
			foreach ($factors as $factor)
269
			{
270
				$changes[$factor] = (int) $this->_req->post->{$factor};
271
			}
272
273
			updateSettings($changes);
274
		}
275
276
		$context['relative_weights'] = array('total' => 0);
277
		foreach ($factors as $factor)
278
		{
279
			$context['relative_weights']['total'] += $modSettings[$factor] ?? 0;
280
		}
281
282
		foreach ($factors as $factor)
283
		{
284
			$context['relative_weights'][$factor] = round(100 * ($modSettings[$factor] ?? 0) / $context['relative_weights']['total'], 1);
285
		}
286
287
		createToken('admin-msw');
288
	}
289
290
	/**
291
	 * Edit the search method and search index used.
292
	 *
293
	 * What it does:
294
	 *
295
	 * - Calculates the size of the current search indexes in use.
296
	 * - Allows to create and delete a fulltext index on the messages table.
297
	 * - Allows to delete a custom index (that action_create() created).
298
	 * - Called by ?action=admin;area=managesearch;sa=method.
299
	 * - Requires the admin_forum permission.
300
	 *
301
	 * @uses ManageSearch template, 'select_search_method' sub-template.
302
	 */
303
	public function action_edit()
304
	{
305
		global $txt, $context, $modSettings;
306
307
		// Need to work with some db search stuffs
308
		$db_search = db_search();
309
		require_once(SUBSDIR . '/ManageSearch.subs.php');
310
311
		$context[$context['admin_menu_name']]['current_subsection'] = 'method';
312
		$context['page_title'] = $txt['search_method_title'];
313
		$context['sub_template'] = 'select_search_method';
314
		$context['supports_fulltext'] = $db_search->search_support('fulltext');
315
		$context['fulltext_index'] = false;
316
317
		// Load any apis.
318
		$context['search_apis'] = $this->loadSearchAPIs();
319
320
		// Detect whether a fulltext index is set.
321
		if ($context['supports_fulltext'])
322
		{
323
			$fulltext_index = detectFulltextIndex();
0 ignored issues
show
Bug introduced by
The function detectFulltextIndex was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

323
			$fulltext_index = /** @scrutinizer ignore-call */ detectFulltextIndex();
Loading history...
324
		}
325
326
		// Creating index, removing or simply changing the one in use?
327
		$sa = $this->_req->getQuery('sa', 'trim', '');
328
		if ($sa === 'createfulltext')
329
		{
330
			checkSession('get');
331
			validateToken('admin-msm', 'get');
332
333
			alterFullTextIndex('{db_prefix}messages', array('body', 'subject', 'body,subject'), true);
0 ignored issues
show
Bug introduced by
The function alterFullTextIndex was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

333
			/** @scrutinizer ignore-call */ 
334
   alterFullTextIndex('{db_prefix}messages', array('body', 'subject', 'body,subject'), true);
Loading history...
334
			$fulltext_index = true;
335
		}
336
		elseif ($sa === 'removefulltext' && !empty($fulltext_index))
337
		{
338
			checkSession('get');
339
			validateToken('admin-msm', 'get');
340
341
			alterFullTextIndex('{db_prefix}messages', $fulltext_index);
342
			$fulltext_index = false;
343
344
			// Go back to the default search method.
345
			if (!empty($modSettings['search_index']) && $modSettings['search_index'] === 'fulltext')
346
			{
347
				updateSettings(array(
348
					'search_index' => '',
349
				));
350
			}
351
		}
352
		elseif ($sa === 'removecustom')
353
		{
354
			checkSession('get');
355
			validateToken('admin-msm', 'get');
356
357
			drop_log_search_words();
0 ignored issues
show
Bug introduced by
The function drop_log_search_words was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

357
			/** @scrutinizer ignore-call */ 
358
   drop_log_search_words();
Loading history...
358
359
			updateSettings(array(
360
				'search_custom_index_config' => '',
361
				'search_custom_index_resume' => '',
362
			));
363
364
			// Go back to the default search method.
365
			if (!empty($modSettings['search_index']) && $modSettings['search_index'] === 'custom')
366
			{
367
				updateSettings(array(
368
					'search_index' => '',
369
				));
370
			}
371
		}
372
		elseif (isset($this->_req->post->save))
373
		{
374
			checkSession();
375
			validateToken('admin-msmpost');
376
377
			updateSettings(array(
378
				'search_index' => empty($this->_req->post->search_index) || (!in_array($this->_req->post->search_index, array('fulltext', 'custom')) && !isset($context['search_apis'][$this->_req->post->search_index])) ? '' : $this->_req->post->search_index,
379
				'search_force_index' => isset($this->_req->post->search_force_index) ? '1' : '0',
380
				'search_match_words' => isset($this->_req->post->search_match_words) ? '1' : '0',
381
			));
382
		}
383
384
		$table_info_defaults = array(
385
			'data_length' => 0,
386
			'index_length' => 0,
387
			'fulltext_length' => 0,
388
			'custom_index_length' => 0,
389
		);
390
391
		// Get some info about the messages table, to show its size and index size.
392
		if (method_exists($db_search, 'membersTableInfo'))
393
		{
394
			$context['table_info'] = array_merge($table_info_defaults, $db_search->membersTableInfo());
395
		}
396
		else
397
		{
398
			// Here may be wolves.
399
			$context['table_info'] = array(
400
				'data_length' => $txt['not_applicable'],
401
				'index_length' => $txt['not_applicable'],
402
				'fulltext_length' => $txt['not_applicable'],
403
				'custom_index_length' => $txt['not_applicable'],
404
			);
405
		}
406
407
		// Format the data and index length in human readable form.
408
		foreach ($context['table_info'] as $type => $size)
409
		{
410
			// If it's not numeric then just break.  This database engine doesn't support size.
411
			if (!is_numeric($size))
412
			{
413
				break;
414
			}
415
416
			$context['table_info'][$type] = byte_format($context['table_info'][$type]);
417
		}
418
419
		$context['custom_index'] = !empty($modSettings['search_custom_index_config']);
420
		$context['partial_custom_index'] = !empty($modSettings['search_custom_index_resume']) && empty($modSettings['search_custom_index_config']);
421
		$context['double_index'] = !empty($context['fulltext_index']) && $context['custom_index'];
422
		$context['fulltext_index'] = !empty($fulltext_index);
423
424
		createToken('admin-msmpost');
425
		createToken('admin-msm', 'get');
426
	}
427
428
	/**
429
	 * Get the installed Search API implementations.
430
	 *
431
	 * - This function checks for patterns in comments on top of the Search-API files!
432
	 * - It loads the search API classes if identified.
433
	 * - This function is used by action_edit to list all installed API implementations.
434
	 */
435
	private function loadSearchAPIs()
436
	{
437
		global $txt;
438
439
		$apis = array();
440
		try
441
		{
442
			$files = new GlobIterator(SOURCEDIR . '/ElkArte/Search/API/*.php', FilesystemIterator::SKIP_DOTS);
443
			foreach ($files as $file)
444
			{
445
				if ($file->isFile())
446
				{
447
					$index_name = $file->getBasename('.php');
448
					$common_name = strtolower($index_name);
449
450
					if ($common_name === 'searchapi')
451
					{
452
						continue;
453
					}
454
455
					$apis[$index_name] = array(
456
						'filename' => $file->getFilename(),
457
						'setting_index' => $index_name,
458
						'has_template' => in_array($common_name, array('custom', 'fulltext', 'standard')),
459
						'label' => $index_name && isset($txt['search_index_' . $common_name]) ? str_replace('{managesearch_url}', getUrl('admin', ['action' => 'admin', 'area' => 'managesearch', 'sa' => 'manage' . $common_name]), $txt['search_index_' . $common_name]) : '',
460
						'desc' => $index_name && isset($txt['search_index_' . $common_name . '_desc']) ? str_replace('{managesearch_url}', getUrl('admin', ['action' => 'admin', 'area' => 'managesearch', 'sa' => 'manage' . $common_name]), $txt['search_index_' . $common_name . '_desc']) : '',
461
					);
462
				}
463
			}
464
		}
465
		catch (UnexpectedValueException)
466
		{
467
			// @todo for now just passthrough
468
		}
469
470
		return $apis;
471
	}
472
473
	/**
474
	 * Create a custom search index for the messages table.
475
	 *
476
	 * What it does:
477
	 *
478
	 * - Called by ?action=admin;area=managesearch;sa=createmsgindex.
479
	 * - Linked from the action_edit screen.
480
	 * - Requires the admin_forum permission.
481
	 * - Depending on the size of the message table, the process is divided in steps.
482
	 *
483
	 * @uses ManageSearch template, 'create_index_progress', and 'create_index_done'
484
	 * sub-templates.
485
	 */
486
	public function action_create()
487
	{
488
		global $modSettings, $context, $txt, $db_show_debug;
489
490
		// Scotty, we need more time...
491
		detectServer()->setTimeLimit(600);
492
493
		$context[$context['admin_menu_name']]['current_subsection'] = 'method';
494
		$context['page_title'] = $txt['search_index_custom'];
495
		$context['sub_template'] = 'create_index_progress';
496
497
		$messages_per_batch = 75;
498
499
		// Resume building an index that was not completed
500
		if (isset($this->_req->query->resume) && !empty($modSettings['search_custom_index_resume']))
501
		{
502
			$context['index_settings'] = Util::unserialize($modSettings['search_custom_index_resume']);
503
			$context['start'] = (int) $context['index_settings']['resume_at'];
504
			unset($context['index_settings']['resume_at']);
505
			$context['step'] = 1;
506
		}
507
		else
508
		{
509
			$context['index_settings'] = array();
510
			$context['start'] = $this->_req->getPost('start', 'intval', 0);
511
			$context['step'] = $this->_req->getPost('step', 'intval', 1);
512
		}
513
514
		checkSession('request');
515
516
		// Admin timeouts are painful when building these long indexes
517
		$_SESSION['admin_time'] = time();
518
519
		require_once(SUBSDIR . '/ManageSearch.subs.php');
520
521
		// Logging may cause session issues with many queries
522
		$old_db_show_debug = $db_show_debug;
523
		$db_show_debug = false;
524
525
		// Step 1: insert all the words.
526
		if ($context['step'] === 1)
527
		{
528
			$context['sub_template'] = 'create_index_progress';
529
			[$context['start'], $context['step'], $context['percentage']] = createSearchIndex($context['start'], $messages_per_batch);
0 ignored issues
show
Bug introduced by
The function createSearchIndex was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

529
			[$context['start'], $context['step'], $context['percentage']] = /** @scrutinizer ignore-call */ createSearchIndex($context['start'], $messages_per_batch);
Loading history...
530
		}
531
532
		// Step 2: removing the words that occur too often and are of no use.
533
		if ($context['step'] === 2)
534
		{
535
			[$context['start'], $complete, $context['percentage']] = removeCommonWordsFromIndex($context['start']);
0 ignored issues
show
Bug introduced by
The function removeCommonWordsFromIndex was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

535
			[$context['start'], $complete, $context['percentage']] = /** @scrutinizer ignore-call */ removeCommonWordsFromIndex($context['start']);
Loading history...
536
			if ($complete)
537
			{
538
				$context['step'] = 3;
539
			}
540
541
			$context['sub_template'] = 'create_index_progress';
542
		}
543
544
		// Restore previous debug state
545
		$db_show_debug = $old_db_show_debug;
546
547
		// Step 3: everything done.
548
		if ($context['step'] === 3)
549
		{
550
			$context['sub_template'] = 'create_index_done';
551
552
			updateSettings(array('search_index' => 'custom', 'search_custom_index_config' => serialize($context['index_settings'])));
553
			removeSettings('search_custom_index_resume');
554
		}
555
	}
556
557
	/**
558
	 * Edit settings related to the sphinx or sphinxQL search function.
559
	 *
560
	 * - Called by ?action=admin;area=managesearch;sa=sphinx.
561
	 * - Checks if connection to search daemon is possible
562
	 */
563
	public function action_managesphinx()
564
	{
565
		global $txt, $context, $modSettings;
566
567
		// Saving the settings
568
		if (isset($this->_req->post->save))
569
		{
570
			checkSession();
571
			validateToken('admin-mssphinx');
572
			$this->_saveSphinxConfig();
573
		}
574
		// Checking if we can connect?
575
		elseif (isset($this->_req->post->checkconnect))
576
		{
577
			checkSession();
578
			validateToken('admin-mssphinx');
579
580
			// If they have not picked sphinx yet, let them know, but we can still check connections
581
			if (empty($modSettings['search_index']) || stripos($modSettings['search_index'], 'sphinx') === false)
582
			{
583
				$context['settings_message'][] = $txt['sphinx_test_not_selected'];
584
				$context['error_type'] = 'notice';
585
			}
586
587
			// Try to connect via Sphinx API?
588
			if (empty($modSettings['search_index']) || $modSettings['search_index'] === 'Sphinx')
589
			{
590
				$this->connectSphinxApi();
591
			}
592
593
			// Try to connect via SphinxQL
594
			if (empty($modSettings['search_index']) || $modSettings['search_index'] === 'Sphinxql')
595
			{
596
				$this->connectSphinxQL();
597
			}
598
		}
599
		elseif (isset($this->_req->post->createconfig))
600
		{
601
			checkSession();
602
			validateToken('admin-mssphinx');
603
			$this->_saveSphinxConfig();
604
605
			require_once(SUBSDIR . '/ManageSearch.subs.php');
606
607
			createSphinxConfig();
0 ignored issues
show
Bug introduced by
The function createSphinxConfig was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

607
			/** @scrutinizer ignore-call */ 
608
   createSphinxConfig();
Loading history...
608
		}
609
610
		// Setup for the template
611
		$context[$context['admin_menu_name']]['current_subsection'] = 'managesphinx';
612
		$context['page_title'] = $txt['search_sphinx'];
613
		$context['page_description'] = $txt['sphinx_create_config'];
614
		$context['sub_template'] = 'manage_sphinx';
615
		createToken('admin-mssphinx');
616
	}
617
618
	/**
619
	 * Save the form values in modsettings
620
	 */
621
	private function _saveSphinxConfig()
622
	{
623
		updateSettings(array(
624
			'sphinx_index_prefix' => rtrim($this->_req->post->sphinx_index_prefix, '/'),
625
			'sphinx_data_path' => rtrim($this->_req->post->sphinx_data_path, '/'),
626
			'sphinx_log_path' => rtrim($this->_req->post->sphinx_log_path, '/'),
627
			'sphinx_stopword_path' => $this->_req->getPost('sphinx_stopword_path', 'trim', ''),
628
			'sphinx_indexer_mem' => $this->_req->getPost('sphinx_indexer_mem', 'intval', 128),
629
			'sphinx_searchd_server' => $this->_req->getPost('sphinx_searchd_server', 'trim', 'localhost'),
630
			'sphinx_searchd_port' => $this->_req->getPost('sphinx_searchd_port', 'intval', 0),
631
			'sphinxql_searchd_port' => $this->_req->getPost('sphinxql_searchd_port', 'intval', 0),
632
			'sphinx_max_results' => $this->_req->getPost('sphinx_max_results', 'intval', 0)
633
		));
634
	}
635
636
	/**
637
	 * Attempt to connect to sphinx using the API methods
638
	 */
639
	public function connectSphinxApi()
640
	{
641
		global $txt, $modSettings, $context;
642
643
		// This is included with sphinx and not distrubuted with ElkArte
644
		if (FileFunctions::instance()->fileExists(SOURCEDIR . '/sphinxapi.php'))
645
		{
646
			include_once(SOURCEDIR . '/sphinxapi.php');
647
			$server = empty($modSettings['sphinx_searchd_server']) ? 'localhost' : $modSettings['sphinx_searchd_server'];
648
			$port = empty($modSettings['sphinx_searchd_port']) ? 9312 : $modSettings['sphinx_searchd_port'];
649
650
			$mySphinx = new SphinxClient();
651
			$mySphinx->SetServer($server, (int) $port);
652
			$mySphinx->SetLimits(0, 25, 1);
653
654
			$index = (empty($modSettings['sphinx_index_prefix']) ? 'elkarte' : $modSettings['sphinx_index_prefix']) . '_index';
655
			$request = $mySphinx->Query('ElkArte', $index);
656
657
			if ($request === false)
658
			{
659
				$context['settings_message'][] = $txt['sphinx_test_connect_failed'];
660
				$context['error_type'] = 'serious';
661
			}
662
			else
663
			{
664
				updateSettings(array('sphinx_searchd_server' => $server, 'sphinx_searchd_port' => $port));
665
				$context['settings_message'][] = $txt['sphinx_test_passed'];
666
			}
667
668
			return;
669
		}
670
671
		$context['settings_message'][] = $txt['sphinx_test_api_missing'];
672
		$context['error_type'] = 'serious';
673
	}
674
675
	/**
676
	 * Attempt to connect to Sphinx using the preferred QL way
677
	 */
678
	public function connectSphinxQL()
679
	{
680
		global $txt, $modSettings, $context;
681
682
		$server = empty($modSettings['sphinx_searchd_server']) ? 'localhost' : $modSettings['sphinx_searchd_server'];
683
		$server = $server === 'localhost' ? '127.0.0.1' : $server;
684
		$port = empty($modSettings['sphinxql_searchd_port']) ? '9306' : $modSettings['sphinxql_searchd_port'];
685
686
		set_error_handler(static function () { /* ignore errors */ });
687
		try
688
		{
689
			$result = mysqli_connect($server, '', '', '', (int) $port);
0 ignored issues
show
Bug introduced by
The call to mysqli_connect() has too few arguments starting with socket. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

689
			$result = /** @scrutinizer ignore-call */ mysqli_connect($server, '', '', '', (int) $port);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
690
		}
691
		catch (Exception)
692
		{
693
			$result = false;
694
		}
695
		finally
696
		{
697
			restore_error_handler();
698
		}
699
700
		if ($result === false)
701
		{
702
			$context['settings_message'][] = $txt['sphinxql_test_connect_failed'];
703
			$context['error_type'] = 'serious';
704
705
			return;
706
		}
707
708
		updateSettings(array('sphinx_searchd_server' => $server, 'sphinxql_searchd_port' => $port));
709
		$context['settings_message'][] = $txt['sphinxql_test_passed'];
710
	}
711
}
712