Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
54 / 54
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
Mysqli
100.00% covered (success)
100.00%
54 / 54
100.00% covered (success)
100.00%
3 / 3
11
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 search
100.00% covered (success)
100.00%
47 / 47
100.00% covered (success)
100.00%
1 / 1
9
 setRelevanceRanking
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3/**
4 * phpMyFAQ MySQL (ext/mysqli) search classes.
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * @package   phpMyFAQ
11 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
12 * @copyright 2010-2026 phpMyFAQ Team
13 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link      https://www.phpmyfaq.de
15 * @since     2010-06-06
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Search\Database;
21
22use Exception;
23use Override;
24use phpMyFAQ\Configuration;
25use phpMyFAQ\Search\SearchDatabase;
26
27/**
28 * Class Mysqli
29 *
30 * @package phpMyFAQ\Search\Database
31 * @deprecated Use PDO instead. Will be removed in the v5.0 release.
32 */
33class Mysqli extends SearchDatabase implements DatabaseInterface
34{
35    /**
36     * Constructor.
37     */
38    public function __construct(Configuration $configuration)
39    {
40        parent::__construct($configuration);
41        $this->relevanceSupport = true;
42    }
43
44    /**
45     * Prepares the search and executes it.
46     *
47     * @param  string $searchTerm Search term
48     * @throws Exception
49     */
50    #[Override]
51    public function search(string $searchTerm): mixed
52    {
53        if (is_numeric($searchTerm) && (bool) $this->configuration->get(item: 'search.searchForSolutionId')) {
54            return parent::search($searchTerm);
55        }
56
57        $relevance = (bool) $this->configuration->get(item: 'search.enableRelevance');
58        $orderBy = '';
59        $columns = $this->getResultColumns();
60        if ($this->relevanceSupport && $relevance) {
61            $columns .= ', ' . $this->setRelevanceRanking($searchTerm);
62            $orderBy = 'ORDER BY score DESC';
63        }
64        if (!$this->relevanceSupport || !$relevance) {
65            $orderBy = '';
66        }
67
68        $chars = [
69            "\xe2\x80\x98",
70            "\xe2\x80\x99",
71            "\xe2\x80\x9c",
72            "\xe2\x80\x9d",
73            "\xe2\x80\x93",
74            "\xe2\x80\x94",
75            "\xe2\x80\xa6",
76        ];
77        $replace = ["'", "'", '"', '"', '-', '--', '...'];
78        $searchTerm = str_replace($chars, $replace, $searchTerm);
79        $query = sprintf(
80            "
81                SELECT
82                    %s
83                FROM 
84                    %s %s %s
85                WHERE
86                    MATCH (%s) AGAINST ('%s' IN BOOLEAN MODE)
87                    %s
88                    %s",
89            $columns,
90            $this->getTable(),
91            $this->getJoinedTable(),
92            $this->getJoinedColumns(),
93            $this->getMatchingColumns(),
94            $this->configuration->getDb()->escape($searchTerm),
95            $this->getConditions(),
96            $orderBy,
97        );
98        $this->resultSet = $this->configuration->getDb()->query($query);
99        // Fallback for searches with less than three characters
100        if (false !== $this->resultSet && 0 === $this->configuration->getDb()->numRows($this->resultSet)) {
101            $query = sprintf(
102                '
103                    SELECT
104                        %s
105                    FROM 
106                        %s %s %s
107                    WHERE
108                        %s
109                        %s',
110                $this->getResultColumns(),
111                $this->getTable(),
112                $this->getJoinedTable(),
113                $this->getJoinedColumns(),
114                $this->getMatchClause($searchTerm),
115                $this->getConditions(),
116            );
117
118            $this->resultSet = $this->configuration->getDb()->query($query);
119        }
120
121        return $this->resultSet;
122    }
123
124    /**
125     * Add the matching columns into the columns for the relevance ranking
126     */
127    public function setRelevanceRanking(string $searchTerm): string
128    {
129        return sprintf(
130            "MATCH (%s) AGAINST ('%s' IN BOOLEAN MODE) as score",
131            $this->getMatchingColumns(),
132            $this->configuration->getDb()->escape($searchTerm),
133        );
134    }
135}