3 * This is the MySQLi database abstraction layer.
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.
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.
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
23 namespace Wikimedia\Rdbms
;
31 * Database abstraction object for PHP extension mysqli.
37 class DatabaseMysqli
extends DatabaseMysqlBase
{
40 * @return mysqli_result|bool
42 protected function doQuery( $sql ) {
43 $conn = $this->getBindingHandle();
45 if ( $this->getFlag( self
::DBO_NOBUFFER
) ) {
46 $ret = $conn->query( $sql, MYSQLI_USE_RESULT
);
48 $ret = $conn->query( $sql );
55 * @param string $realServer
56 * @param string|null $dbName
58 * @throws DBConnectionError
60 protected function mysqlConnect( $realServer, $dbName ) {
61 if ( !function_exists( 'mysqli_init' ) ) {
62 throw $this->newExceptionAfterConnectError(
63 "MySQLi functions missing, have you compiled PHP with the --with-mysqli option?"
67 // Other than mysql_connect, mysqli_real_connect expects an explicit port number
68 // e.g. "localhost:1234" or "127.0.0.1:1234"
69 // or Unix domain socket path
70 // e.g. "localhost:/socket_path" or "localhost:/foo/bar:bar:bar"
71 // colons are known to be used by Google AppEngine,
72 // see <https://cloud.google.com/sql/docs/mysql/connect-app-engine>
74 // We need to parse the port or socket path out of $realServer
77 $hostAndPort = IP
::splitHostAndPort( $realServer );
79 $realServer = $hostAndPort[0];
80 if ( $hostAndPort[1] ) {
81 $port = $hostAndPort[1];
83 } elseif ( substr_count( $realServer, ':/' ) == 1 ) {
84 // If we have a colon slash instead of a colon and a port number
85 // after the ip or hostname, assume it's the Unix domain socket path
86 list( $realServer, $socket ) = explode( ':', $realServer, 2 );
89 $mysqli = mysqli_init();
90 // Make affectedRows() for UPDATE reflect the number of matching rows, regardless
91 // of whether any column values changed. This is what callers want to know and is
92 // consistent with what Postgres, SQLite, and SQL Server return.
93 $connFlags = MYSQLI_CLIENT_FOUND_ROWS
;
94 if ( $this->getFlag( self
::DBO_SSL
) ) {
95 $connFlags |
= MYSQLI_CLIENT_SSL
;
104 if ( $this->getFlag( self
::DBO_COMPRESS
) ) {
105 $connFlags |
= MYSQLI_CLIENT_COMPRESS
;
107 if ( $this->getFlag( self
::DBO_PERSISTENT
) ) {
108 $realServer = 'p:' . $realServer;
111 if ( $this->utf8Mode
) {
112 // Tell the server we're communicating with it in UTF-8.
113 // This may engage various charset conversions.
114 $mysqli->options( MYSQLI_SET_CHARSET_NAME
, 'utf8' );
116 $mysqli->options( MYSQLI_SET_CHARSET_NAME
, 'binary' );
118 $mysqli->options( MYSQLI_OPT_CONNECT_TIMEOUT
, 3 );
120 if ( $mysqli->real_connect(
138 protected function closeConnection() {
139 $conn = $this->getBindingHandle();
141 return $conn->close();
147 function insertId() {
148 $conn = $this->getBindingHandle();
150 return (int)$conn->insert_id
;
156 function lastErrno() {
157 if ( $this->conn
instanceof mysqli
) {
158 return $this->conn
->errno
;
160 return mysqli_connect_errno();
167 protected function fetchAffectedRowCount() {
168 $conn = $this->getBindingHandle();
170 return $conn->affected_rows
;
174 * @param mysqli_result $res
177 protected function mysqlFreeResult( $res ) {
184 * @param mysqli_result $res
185 * @return stdClass|bool
187 protected function mysqlFetchObject( $res ) {
188 $object = $res->fetch_object();
189 if ( $object === null ) {
197 * @param mysqli_result $res
198 * @return array|false
200 protected function mysqlFetchArray( $res ) {
201 $array = $res->fetch_array();
202 if ( $array === null ) {
210 * @param mysqli_result $res
213 protected function mysqlNumRows( $res ) {
214 return $res->num_rows
;
218 * @param mysqli_result $res
221 protected function mysqlNumFields( $res ) {
222 return $res->field_count
;
226 * @param mysqli_result $res
230 protected function mysqlFetchField( $res, $n ) {
231 $field = $res->fetch_field_direct( $n );
233 // Add missing properties to result (using flags property)
234 // which will be part of function mysql-fetch-field for backward compatibility
235 $field->not_null
= $field->flags
& MYSQLI_NOT_NULL_FLAG
;
236 $field->primary_key
= $field->flags
& MYSQLI_PRI_KEY_FLAG
;
237 $field->unique_key
= $field->flags
& MYSQLI_UNIQUE_KEY_FLAG
;
238 $field->multiple_key
= $field->flags
& MYSQLI_MULTIPLE_KEY_FLAG
;
239 $field->binary
= $field->flags
& MYSQLI_BINARY_FLAG
;
240 $field->numeric = $field->flags
& MYSQLI_NUM_FLAG
;
241 $field->blob
= $field->flags
& MYSQLI_BLOB_FLAG
;
242 $field->unsigned
= $field->flags
& MYSQLI_UNSIGNED_FLAG
;
243 $field->zerofill
= $field->flags
& MYSQLI_ZEROFILL_FLAG
;
249 * @param mysqli_result $res
253 protected function mysqlFieldName( $res, $n ) {
254 $field = $res->fetch_field_direct( $n );
260 * @param mysqli_result $res
264 protected function mysqlFieldType( $res, $n ) {
265 $field = $res->fetch_field_direct( $n );
271 * @param mysqli_result $res
275 protected function mysqlDataSeek( $res, $row ) {
276 return $res->data_seek( $row );
280 * @param mysqli|null $conn Optional connection object
283 protected function mysqlError( $conn = null ) {
284 if ( $conn === null ) {
285 return mysqli_connect_error();
292 * Escapes special characters in a string for use in an SQL statement
296 protected function mysqlRealEscapeString( $s ) {
297 $conn = $this->getBindingHandle();
299 return $conn->real_escape_string( (string)$s );
305 protected function getBindingHandle() {
306 return parent
::getBindingHandle();
311 * @deprecated since 1.29
313 class_alias( DatabaseMysqli
::class, 'DatabaseMysqli' );