Completed
Pull Request — master (#25)
by Matt
02:01
created

mysqli::is_fulltext()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 5
nc 3
nop 2
1
<?php
2
/**
3
 *
4
 * Precise Similar Topics
5
 *
6
 * @copyright (c) 2018 Matt Friedman
7
 * @license GNU General Public License, version 2 (GPL-2.0)
8
 *
9
 */
10
11
namespace vse\similartopics\driver;
12
13
/**
14
 * This class handles similar topics queries for MySQLi dbms
15
 */
16
class mysqli implements driver_interface
17
{
18
	/** @var \phpbb\db\driver\driver_interface */
19
	protected $db;
20
21
	/** @var string */
22
	protected $engine;
23
24
	/**
25
	 * Constructor
26
	 *
27
	 * @param \phpbb\db\driver\driver_interface $db
28
	 */
29
	public function __construct(\phpbb\db\driver\driver_interface $db)
30
	{
31
		$this->db = $db;
32
	}
33
34
	/**
35
	 * {@inheritdoc}
36
	 */
37
	public function get_name()
38
	{
39
		return 'mysqli';
40
	}
41
42
	/**
43
	 * {@inheritdoc}
44
	 */
45
	public function get_type()
46
	{
47
		return 'mysql';
48
	}
49
50
	/**
51
	 * {@inheritdoc}
52
	 */
53
	public function get_query($topic_id, $topic_title, $length, $sensitivity)
54
	{
55
		return array(
56
			'SELECT'	=> "f.forum_id, f.forum_name, t.*,
57
				MATCH (t.topic_title) AGAINST ('" . $this->db->sql_escape($topic_title) . "') AS score",
58
59
			'FROM'		=> array(
60
				TOPICS_TABLE	=> 't',
61
			),
62
			'LEFT_JOIN'	=> array(
63
				array(
64
					'FROM'	=>	array(FORUMS_TABLE	=> 'f'),
65
					'ON'	=> 'f.forum_id = t.forum_id',
66
				),
67
			),
68
			'WHERE'		=> "MATCH (t.topic_title) AGAINST ('" . $this->db->sql_escape($topic_title) . "') >= " . (float) $sensitivity . '
69
				AND t.topic_status <> ' . ITEM_MOVED . '
70
				AND t.topic_visibility = ' . ITEM_APPROVED . '
71
				AND t.topic_time > (UNIX_TIMESTAMP() - ' . (int) $length . ')
72
				AND t.topic_id <> ' . (int) $topic_id,
73
		);
74
	}
75
76
	/**
77
	 * {@inheritdoc}
78
	 */
79
	public function is_supported()
80
	{
81
		// FULLTEXT is supported on InnoDB since MySQL 5.6.4 according to
82
		// http://dev.mysql.com/doc/refman/5.6/en/innodb-storage-engine.html
83
		return $this->is_mysql() && ($this->get_engine() === 'myisam' || ($this->get_engine() === 'innodb' && phpbb_version_compare($this->db->sql_server_info(true), '5.6.4', '>=')));
84
	}
85
86
	/**
87
	 * {@inheritdoc}
88
	 */
89
	public function is_fulltext($column = 'topic_title', $table = TOPICS_TABLE)
90
	{
91
		foreach ($this->get_fulltext_indexes($column, $table) as $index)
92
		{
93
			if ($index === $column)
94
			{
95
				return true;
96
			}
97
		}
98
99
		return false;
100
	}
101
102
	/**
103
	 * {@inheritdoc}
104
	 */
105
	public function get_fulltext_indexes($column = 'topic_title', $table = TOPICS_TABLE)
106
	{
107
		$indexes = array();
108
109
		if (!$this->is_supported())
110
		{
111
			return $indexes;
112
		}
113
114
		$sql = 'SHOW INDEX
115
			FROM ' . $this->db->sql_escape($table);
116
		$result = $this->db->sql_query($sql);
117
118
		while ($row = $this->db->sql_fetchrow($result))
119
		{
120
			// Older MySQL versions didn't use Index_type, so fallback to Comment
121
			$index_type = isset($row['Index_type']) ? $row['Index_type'] : $row['Comment'];
122
123
			if ($index_type === 'FULLTEXT' && $row['Key_name'] === $column)
124
			{
125
				$indexes[] = $row['Key_name'];
126
			}
127
		}
128
129
		$this->db->sql_freeresult($result);
130
131
		return $indexes;
132
	}
133
134
	/**
135
	 * {@inheritdoc}
136
	 */
137
	public function create_fulltext_index($column = 'topic_title', $table = TOPICS_TABLE)
138
	{
139
		if (!$this->is_fulltext($column, $table))
140
		{
141
			// First see if we need to update the table engine to support fulltext indexes
142
			if (!$this->is_supported())
143
			{
144
				$sql = 'ALTER TABLE ' . $this->db->sql_escape($table) . ' ENGINE = MYISAM';
145
				$this->db->sql_query($sql);
146
				$this->set_engine();
147
			}
148
149
			$sql = 'ALTER TABLE ' . $this->db->sql_escape($table) . ' 
150
				ADD FULLTEXT (' . $this->db->sql_escape($column) . ')';
151
			$this->db->sql_query($sql);
152
		}
153
	}
154
155
	/**
156
	 * {@inheritdoc}
157
	 */
158
	public function get_engine()
159
	{
160
		return $this->engine !== null ? $this->engine : $this->set_engine();
161
	}
162
163
	/**
164
	 * Set the database storage engine name
165
	 *
166
	 * @access protected
167
	 * @return string The storage engine name
168
	 */
169
	protected function set_engine()
170
	{
171
		$this->engine = '';
172
173
		if ($this->is_mysql())
174
		{
175
			$info = $this->get_table_info();
176
177
			// Modern MySQL uses 'Engine', but older may still use 'Type'
178
			foreach (array('Engine', 'Type') as $name)
179
			{
180
				if (isset($info[$name]))
181
				{
182
					$this->engine = strtolower($info[$name]);
183
					break;
184
				}
185
			}
186
		}
187
188
		return $this->engine;
189
	}
190
191
	/**
192
	 * Get topics table information
193
	 *
194
	 * @access protected
195
	 * @param string $table Name of the table
196
	 * @return mixed Array with the table info, false if the table does not exist
197
	 */
198
	protected function get_table_info($table = TOPICS_TABLE)
199
	{
200
		$result = $this->db->sql_query('SHOW TABLE STATUS LIKE \'' . $this->db->sql_escape($table) . '\'');
201
		$info = $this->db->sql_fetchrow($result);
202
		$this->db->sql_freeresult($result);
203
204
		return $info;
205
	}
206
207
	/**
208
	 * Check if the database is using MySQL
209
	 *
210
	 * @access public
211
	 * @return bool True if is mysql, false otherwise
212
	 */
213
	protected function is_mysql()
214
	{
215
		return ($this->db->get_sql_layer() === 'mysql4' || $this->db->get_sql_layer() === 'mysqli');
216
	}
217
}
218