MediaWiki REL1_39
DatabaseMysqli.php
Go to the documentation of this file.
1<?php
20namespace Wikimedia\Rdbms;
21
22use mysqli;
23use mysqli_result;
24use Wikimedia\AtEase\AtEase;
25use Wikimedia\IPUtils;
26
40 protected function doSingleStatementQuery( string $sql ): QueryStatus {
41 AtEase::suppressWarnings();
42 $res = $this->getBindingHandle()->query( $sql );
43 AtEase::restoreWarnings();
44
45 return new QueryStatus(
46 $res instanceof mysqli_result ? new MysqliResultWrapper( $this, $res ) : $res,
47 $this->affectedRows(),
48 $this->lastError(),
49 $this->lastErrno()
50 );
51 }
52
53 protected function doMultiStatementQuery( array $sqls ): array {
54 $qsByStatementId = [];
55
56 $conn = $this->getBindingHandle();
57 // Clear any previously left over result
58 while ( $conn->more_results() && $conn->next_result() ) {
59 $mysqliResult = $conn->store_result();
60 $mysqliResult->free();
61 }
62
63 $combinedSql = implode( ";\n", $sqls );
64 $conn->multi_query( $combinedSql );
65
66 reset( $sqls );
67 $done = false;
68 do {
69 $mysqliResult = $conn->store_result();
70 $statementId = key( $sqls );
71 if ( $statementId !== null ) {
72 // Database uses "true" for successful queries without result sets
73 if ( $mysqliResult === false ) {
74 $res = ( $conn->errno === 0 );
75 } elseif ( $mysqliResult instanceof mysqli_result ) {
76 $res = new MysqliResultWrapper( $this, $mysqliResult );
77 } else {
78 $res = $mysqliResult;
79 }
80 $qsByStatementId[$statementId] = new QueryStatus(
81 $res,
82 $conn->affected_rows,
83 $conn->error,
84 $conn->errno
85 );
86 next( $sqls );
87 }
88 if ( $conn->more_results() ) {
89 $conn->next_result();
90 } else {
91 $done = true;
92 }
93 } while ( !$done );
94 // Fill in status for statements aborted due to prior statement failure
95 while ( ( $statementId = key( $sqls ) ) !== null ) {
96 $qsByStatementId[$statementId] = new QueryStatus( false, 0, 'Query aborted', 0 );
97 next( $sqls );
98 }
99
100 return $qsByStatementId;
101 }
102
111 protected function mysqlConnect( $server, $user, $password, $db ) {
112 if ( !function_exists( 'mysqli_init' ) ) {
113 throw $this->newExceptionAfterConnectError(
114 "MySQLi functions missing, have you compiled PHP with the --with-mysqli option?"
115 );
116 }
117
118 // PHP 8.1.0+ throws exceptions by default. Turn that off for consistency.
119 mysqli_report( MYSQLI_REPORT_OFF );
120
121 // Other than mysql_connect, mysqli_real_connect expects an explicit port number
122 // e.g. "localhost:1234" or "127.0.0.1:1234"
123 // or Unix domain socket path
124 // e.g. "localhost:/socket_path" or "localhost:/foo/bar:bar:bar"
125 // colons are known to be used by Google AppEngine,
126 // see <https://cloud.google.com/sql/docs/mysql/connect-app-engine>
127 //
128 // We need to parse the port or socket path out of $realServer
129 $port = null;
130 $socket = null;
131 $hostAndPort = IPUtils::splitHostAndPort( $server );
132 if ( $hostAndPort ) {
133 $realServer = $hostAndPort[0];
134 if ( $hostAndPort[1] ) {
135 $port = $hostAndPort[1];
136 }
137 } elseif ( substr_count( $server, ':/' ) == 1 ) {
138 // If we have a colon slash instead of a colon and a port number
139 // after the ip or hostname, assume it's the Unix domain socket path
140 list( $realServer, $socket ) = explode( ':', $server, 2 );
141 } else {
142 $realServer = $server;
143 }
144
145 $mysqli = mysqli_init();
146 // Make affectedRows() for UPDATE reflect the number of matching rows, regardless
147 // of whether any column values changed. This is what callers want to know and is
148 // consistent with what Postgres and SQLite return.
149 $flags = MYSQLI_CLIENT_FOUND_ROWS;
150 if ( $this->ssl ) {
151 $flags |= MYSQLI_CLIENT_SSL;
152 $mysqli->ssl_set(
153 $this->sslKeyPath,
154 $this->sslCertPath,
155 $this->sslCAFile,
156 $this->sslCAPath,
157 $this->sslCiphers
158 );
159 }
160 if ( $this->getFlag( self::DBO_COMPRESS ) ) {
161 $flags |= MYSQLI_CLIENT_COMPRESS;
162 }
163 if ( $this->getFlag( self::DBO_PERSISTENT ) ) {
164 $realServer = 'p:' . $realServer;
165 }
166
167 if ( $this->utf8Mode ) {
168 // Tell the server we're communicating with it in UTF-8.
169 // This may engage various charset conversions.
170 $mysqli->options( MYSQLI_SET_CHARSET_NAME, 'utf8' );
171 } else {
172 $mysqli->options( MYSQLI_SET_CHARSET_NAME, 'binary' );
173 }
174 $mysqli->options( MYSQLI_OPT_CONNECT_TIMEOUT, 3 );
175
176 // @phan-suppress-next-line PhanTypeMismatchArgumentNullableInternal socket seems set when used
177 $ok = $mysqli->real_connect( $realServer, $user, $password, $db, $port, $socket, $flags );
178
179 return $ok ? $mysqli : null;
180 }
181
182 protected function closeConnection() {
183 $conn = $this->getBindingHandle();
184
185 return $conn->close();
186 }
187
188 public function insertId() {
189 $conn = $this->getBindingHandle();
190
191 return (int)$conn->insert_id;
192 }
193
197 public function lastErrno() {
198 if ( $this->conn instanceof mysqli ) {
199 return $this->conn->errno;
200 } else {
201 return mysqli_connect_errno();
202 }
203 }
204
205 protected function fetchAffectedRowCount() {
206 $conn = $this->getBindingHandle();
207
208 return $conn->affected_rows;
209 }
210
215 protected function mysqlError( $conn = null ) {
216 if ( $conn === null ) {
217 return (string)mysqli_connect_error();
218 } else {
219 return $conn->error;
220 }
221 }
222
223 protected function mysqlRealEscapeString( $s ) {
224 $conn = $this->getBindingHandle();
225
226 return $conn->real_escape_string( (string)$s );
227 }
228
232 protected function getBindingHandle() {
233 return parent::getBindingHandle();
234 }
235}
236
240class_alias( DatabaseMysqli::class, 'DatabaseMysqli' );
MySQL database abstraction layer.
Database abstraction object for PHP extension mysqli.
closeConnection()
Closes underlying database connection.
doMultiStatementQuery(array $sqls)
Execute a batch of query statements, aborting remaining statements if one fails.
mysqlRealEscapeString( $s)
Escape special characters in a string for use in an SQL statement.
mysqlConnect( $server, $user, $password, $db)
doSingleStatementQuery(string $sql)
Run a query and return a QueryStatus instance with the query result information.
insertId()
Get the inserted value of an auto-increment row.
affectedRows()
Get the number of rows affected by the last write query.
foreach( $mmfl['setupFiles'] as $fileName) if($queue) if(empty( $mmfl['quiet'])) $s