0a5450cc5e8d7662e747a261eaa5b1db6d9433e6
[lhc/web/wiklou.git] / includes / libs / rdbms / database / DatabaseMysqli.php
1 <?php
2 /**
3 * This is the MySQLi database abstraction layer.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup Database
22 */
23 namespace Wikimedia\Rdbms;
24
25 use mysqli;
26 use mysqli_result;
27 use IP;
28 use stdClass;
29
30 /**
31 * Database abstraction object for PHP extension mysqli.
32 *
33 * @ingroup Database
34 * @since 1.22
35 * @see Database
36 */
37 class DatabaseMysqli extends DatabaseMysqlBase {
38 /**
39 * @param string $sql
40 * @return mysqli_result
41 */
42 protected function doQuery( $sql ) {
43 $conn = $this->getBindingHandle();
44
45 if ( $this->bufferResults() ) {
46 $ret = $conn->query( $sql );
47 } else {
48 $ret = $conn->query( $sql, MYSQLI_USE_RESULT );
49 }
50
51 return $ret;
52 }
53
54 /**
55 * @param string $realServer
56 * @return bool|mysqli
57 * @throws DBConnectionError
58 */
59 protected function mysqlConnect( $realServer ) {
60 # Avoid suppressed fatal error, which is very hard to track down
61 if ( !function_exists( 'mysqli_init' ) ) {
62 throw new DBConnectionError( $this, "MySQLi functions missing,"
63 . " have you compiled PHP with the --with-mysqli option?\n" );
64 }
65
66 // Other than mysql_connect, mysqli_real_connect expects an explicit port
67 // and socket parameters. So we need to parse the port and socket out of
68 // $realServer
69 $port = null;
70 $socket = null;
71 $hostAndPort = IP::splitHostAndPort( $realServer );
72 if ( $hostAndPort ) {
73 $realServer = $hostAndPort[0];
74 if ( $hostAndPort[1] ) {
75 $port = $hostAndPort[1];
76 }
77 } elseif ( substr_count( $realServer, ':' ) == 1 ) {
78 // If we have a colon and something that's not a port number
79 // inside the hostname, assume it's the socket location
80 $hostAndSocket = explode( ':', $realServer );
81 $realServer = $hostAndSocket[0];
82 $socket = $hostAndSocket[1];
83 }
84
85 $mysqli = mysqli_init();
86
87 $connFlags = 0;
88 if ( $this->flags & self::DBO_SSL ) {
89 $connFlags |= MYSQLI_CLIENT_SSL;
90 $mysqli->ssl_set(
91 $this->sslKeyPath,
92 $this->sslCertPath,
93 $this->sslCAFile,
94 $this->sslCAPath,
95 $this->sslCiphers
96 );
97 }
98 if ( $this->flags & self::DBO_COMPRESS ) {
99 $connFlags |= MYSQLI_CLIENT_COMPRESS;
100 }
101 if ( $this->flags & self::DBO_PERSISTENT ) {
102 $realServer = 'p:' . $realServer;
103 }
104
105 if ( $this->utf8Mode ) {
106 // Tell the server we're communicating with it in UTF-8.
107 // This may engage various charset conversions.
108 $mysqli->options( MYSQLI_SET_CHARSET_NAME, 'utf8' );
109 } else {
110 $mysqli->options( MYSQLI_SET_CHARSET_NAME, 'binary' );
111 }
112 $mysqli->options( MYSQLI_OPT_CONNECT_TIMEOUT, 3 );
113
114 if ( $mysqli->real_connect( $realServer, $this->user,
115 $this->password, $this->dbName, $port, $socket, $connFlags )
116 ) {
117 return $mysqli;
118 }
119
120 return false;
121 }
122
123 protected function connectInitCharset() {
124 // already done in mysqlConnect()
125 return true;
126 }
127
128 /**
129 * @param string $charset
130 * @return bool
131 */
132 protected function mysqlSetCharset( $charset ) {
133 $conn = $this->getBindingHandle();
134
135 if ( method_exists( $conn, 'set_charset' ) ) {
136 return $conn->set_charset( $charset );
137 } else {
138 return $this->query( 'SET NAMES ' . $charset, __METHOD__ );
139 }
140 }
141
142 /**
143 * @return bool
144 */
145 protected function closeConnection() {
146 $conn = $this->getBindingHandle();
147
148 return $conn->close();
149 }
150
151 /**
152 * @return int
153 */
154 function insertId() {
155 $conn = $this->getBindingHandle();
156
157 return (int)$conn->insert_id;
158 }
159
160 /**
161 * @return int
162 */
163 function lastErrno() {
164 if ( $this->conn instanceof mysqli ) {
165 return $this->conn->errno;
166 } else {
167 return mysqli_connect_errno();
168 }
169 }
170
171 /**
172 * @return int
173 */
174 protected function fetchAffectedRowCount() {
175 $conn = $this->getBindingHandle();
176
177 return $conn->affected_rows;
178 }
179
180 /**
181 * @param string $db
182 * @return bool
183 */
184 function selectDB( $db ) {
185 $conn = $this->getBindingHandle();
186
187 $this->dbName = $db;
188
189 return $conn->select_db( $db );
190 }
191
192 /**
193 * @param mysqli_result $res
194 * @return bool
195 */
196 protected function mysqlFreeResult( $res ) {
197 $res->free_result();
198
199 return true;
200 }
201
202 /**
203 * @param mysqli_result $res
204 * @return stdClass|bool
205 */
206 protected function mysqlFetchObject( $res ) {
207 $object = $res->fetch_object();
208 if ( $object === null ) {
209 return false;
210 }
211
212 return $object;
213 }
214
215 /**
216 * @param mysqli_result $res
217 * @return bool
218 */
219 protected function mysqlFetchArray( $res ) {
220 $array = $res->fetch_array();
221 if ( $array === null ) {
222 return false;
223 }
224
225 return $array;
226 }
227
228 /**
229 * @param mysqli_result $res
230 * @return mixed
231 */
232 protected function mysqlNumRows( $res ) {
233 return $res->num_rows;
234 }
235
236 /**
237 * @param mysqli_result $res
238 * @return mixed
239 */
240 protected function mysqlNumFields( $res ) {
241 return $res->field_count;
242 }
243
244 /**
245 * @param mysqli_result $res
246 * @param int $n
247 * @return mixed
248 */
249 protected function mysqlFetchField( $res, $n ) {
250 $field = $res->fetch_field_direct( $n );
251
252 // Add missing properties to result (using flags property)
253 // which will be part of function mysql-fetch-field for backward compatibility
254 $field->not_null = $field->flags & MYSQLI_NOT_NULL_FLAG;
255 $field->primary_key = $field->flags & MYSQLI_PRI_KEY_FLAG;
256 $field->unique_key = $field->flags & MYSQLI_UNIQUE_KEY_FLAG;
257 $field->multiple_key = $field->flags & MYSQLI_MULTIPLE_KEY_FLAG;
258 $field->binary = $field->flags & MYSQLI_BINARY_FLAG;
259 $field->numeric = $field->flags & MYSQLI_NUM_FLAG;
260 $field->blob = $field->flags & MYSQLI_BLOB_FLAG;
261 $field->unsigned = $field->flags & MYSQLI_UNSIGNED_FLAG;
262 $field->zerofill = $field->flags & MYSQLI_ZEROFILL_FLAG;
263
264 return $field;
265 }
266
267 /**
268 * @param mysqli_result $res
269 * @param int $n
270 * @return mixed
271 */
272 protected function mysqlFieldName( $res, $n ) {
273 $field = $res->fetch_field_direct( $n );
274
275 return $field->name;
276 }
277
278 /**
279 * @param mysqli_result $res
280 * @param int $n
281 * @return mixed
282 */
283 protected function mysqlFieldType( $res, $n ) {
284 $field = $res->fetch_field_direct( $n );
285
286 return $field->type;
287 }
288
289 /**
290 * @param mysqli_result $res
291 * @param int $row
292 * @return mixed
293 */
294 protected function mysqlDataSeek( $res, $row ) {
295 return $res->data_seek( $row );
296 }
297
298 /**
299 * @param mysqli $conn Optional connection object
300 * @return string
301 */
302 protected function mysqlError( $conn = null ) {
303 if ( $conn === null ) {
304 return mysqli_connect_error();
305 } else {
306 return $conn->error;
307 }
308 }
309
310 /**
311 * Escapes special characters in a string for use in an SQL statement
312 * @param string $s
313 * @return string
314 */
315 protected function mysqlRealEscapeString( $s ) {
316 $conn = $this->getBindingHandle();
317
318 return $conn->real_escape_string( (string)$s );
319 }
320
321 /**
322 * Give an id for the connection
323 *
324 * mysql driver used resource id, but mysqli objects cannot be cast to string.
325 * @return string
326 */
327 public function __toString() {
328 if ( $this->conn instanceof mysqli ) {
329 return (string)$this->conn->thread_id;
330 } else {
331 // mConn might be false or something.
332 return (string)$this->conn;
333 }
334 }
335
336 /**
337 * @return mysqli
338 */
339 protected function getBindingHandle() {
340 return parent::getBindingHandle();
341 }
342 }
343
344 class_alias( DatabaseMysqli::class, 'DatabaseMysqli' );