Failed Conditions
Pull Request — master (#2825)
by Sébastien
06:58
created

MysqliConnection::errorInfo()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL\Driver\Mysqli;
21
22
use Doctrine\DBAL\Driver\Connection as Connection;
23
use Doctrine\DBAL\Driver\PingableConnection;
24
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
25
26
/**
27
 * @author Kim Hemsø Rasmussen <[email protected]>
28
 * @author Till Klampaeckel <[email protected]>
29
 */
30
class MysqliConnection implements Connection, PingableConnection, ServerInfoAwareConnection
31
{
32
    /**
33
     * Name of the option to set connection flags
34
     */
35
    const OPTION_FLAGS = 'flags';
36
37
    /**
38
     * @var \mysqli
39
     */
40
    private $_conn;
41
42
    /**
43
     * @param array  $params
44
     * @param string $username
45
     * @param string $password
46
     * @param array  $driverOptions
47
     *
48
     * @throws \Doctrine\DBAL\Driver\Mysqli\MysqliException
49
     */
50 5
    public function __construct(array $params, $username, $password, array $driverOptions = [])
51
    {
52 5
        $port = isset($params['port']) ? $params['port'] : ini_get('mysqli.default_port');
53
54
        // Fallback to default MySQL port if not given.
55 5
        if ( ! $port) {
56
            $port = 3306;
57
        }
58
59 5
        $socket = isset($params['unix_socket']) ? $params['unix_socket'] : ini_get('mysqli.default_socket');
60 5
        $dbname = isset($params['dbname']) ? $params['dbname'] : null;
61
62 5
        $flags = isset($driverOptions[static::OPTION_FLAGS]) ? $driverOptions[static::OPTION_FLAGS] : null;
63
64 5
        $this->_conn = mysqli_init();
0 ignored issues
show
Documentation Bug introduced by
It seems like mysqli_init() of type object<mysql> is incompatible with the declared type object<mysqli> of property $_conn.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
65
66 5
        $this->setSecureConnection($params);
67 1
        $this->setDriverOptions($driverOptions);
68
69
        set_error_handler(function () {});
70
        try {
71 1
            if ( ! $this->_conn->real_connect($params['host'], $username, $password, $dbname, $port, $socket, $flags)) {
72 1
                throw new MysqliException($this->_conn->connect_error, $this->_conn->sqlstate ?? 'HY000', $this->_conn->connect_errno);
73
            }
74
        } finally {
75 1
            restore_error_handler();
76
        }
77
78
        if (isset($params['charset'])) {
79
            $this->_conn->set_charset($params['charset']);
80
        }
81
    }
82
83
    /**
84
     * Retrieves mysqli native resource handle.
85
     *
86
     * Could be used if part of your application is not using DBAL.
87
     *
88
     * @return \mysqli
89
     */
90
    public function getWrappedResourceHandle()
91
    {
92
        return $this->_conn;
93
    }
94
95
    /**
96
     * {@inheritdoc}
97
     */
98
    public function getServerVersion()
99
    {
100
        // Hack for mariadb
101
        $serverInfos = $this->_conn->get_server_info();
102
        if (false !== stripos($serverInfos, 'mariadb')) {
103
            return $serverInfos;
104
        }
105
106
        $majorVersion = floor($this->_conn->server_version / 10000);
107
        $minorVersion = floor(($this->_conn->server_version - $majorVersion * 10000) / 100);
108
        $patchVersion = floor($this->_conn->server_version - $majorVersion * 10000 - $minorVersion * 100);
109
110
        return $majorVersion . '.' . $minorVersion . '.' . $patchVersion;
111
    }
112
113
    /**
114
     * {@inheritdoc}
115
     */
116 1
    public function requiresQueryForServerVersion()
117
    {
118 1
        return false;
119
    }
120
121
    /**
122
     * {@inheritdoc}
123
     */
124
    public function prepare($prepareString)
125
    {
126
        return new MysqliStatement($this->_conn, $prepareString);
127
    }
128
129
    /**
130
     * {@inheritdoc}
131
     */
132 View Code Duplication
    public function query()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
133
    {
134
        $args = func_get_args();
135
        $sql = $args[0];
136
        $stmt = $this->prepare($sql);
137
        $stmt->execute();
138
139
        return $stmt;
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145
    public function quote($input, $type=\PDO::PARAM_STR)
146
    {
147
        return "'". $this->_conn->escape_string($input) ."'";
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153
    public function exec($statement)
154
    {
155
        if (false === $this->_conn->query($statement)) {
156
            throw new MysqliException($this->_conn->error, $this->_conn->sqlstate, $this->_conn->errno);
157
        }
158
159
        return $this->_conn->affected_rows;
160
    }
161
162
    /**
163
     * {@inheritdoc}
164
     */
165
    public function lastInsertId($name = null)
166
    {
167
        return $this->_conn->insert_id;
168
    }
169
170
    /**
171
     * {@inheritdoc}
172
     */
173
    public function beginTransaction()
174
    {
175
        $this->_conn->query('START TRANSACTION');
176
177
        return true;
178
    }
179
180
    /**
181
     * {@inheritdoc}
182
     */
183
    public function commit()
184
    {
185
        return $this->_conn->commit();
186
    }
187
188
    /**
189
     * {@inheritdoc}non-PHPdoc)
190
     */
191
    public function rollBack()
192
    {
193
        return $this->_conn->rollback();
194
    }
195
196
    /**
197
     * {@inheritdoc}
198
     */
199
    public function errorCode()
200
    {
201
        return $this->_conn->errno;
202
    }
203
204
    /**
205
     * {@inheritdoc}
206
     */
207
    public function errorInfo()
208
    {
209
        return $this->_conn->error;
210
    }
211
212
    /**
213
     * Apply the driver options to the connection.
214
     *
215
     * @param array $driverOptions
216
     *
217
     * @throws MysqliException When one of of the options is not supported.
218
     * @throws MysqliException When applying doesn't work - e.g. due to incorrect value.
219
     */
220 1
    private function setDriverOptions(array $driverOptions = [])
221
    {
222
        $supportedDriverOptions = [
223 1
            \MYSQLI_OPT_CONNECT_TIMEOUT,
224
            \MYSQLI_OPT_LOCAL_INFILE,
225
            \MYSQLI_INIT_COMMAND,
226
            \MYSQLI_READ_DEFAULT_FILE,
227
            \MYSQLI_READ_DEFAULT_GROUP,
228
        ];
229
230 1
        if (defined('MYSQLI_SERVER_PUBLIC_KEY')) {
231 1
            $supportedDriverOptions[] = \MYSQLI_SERVER_PUBLIC_KEY;
232
        }
233
234 1
        $exceptionMsg = "%s option '%s' with value '%s'";
235
236 1
        foreach ($driverOptions as $option => $value) {
237
238
            if ($option === static::OPTION_FLAGS) {
239
                continue;
240
            }
241
242
            if (!in_array($option, $supportedDriverOptions, true)) {
243
                throw new MysqliException(
244
                    sprintf($exceptionMsg, 'Unsupported', $option, $value)
245
                );
246
            }
247
248
            if (@mysqli_options($this->_conn, $option, $value)) {
249
                continue;
250
            }
251
252
            $msg  = sprintf($exceptionMsg, 'Failed to set', $option, $value);
253
            $msg .= sprintf(', error: %s (%d)', mysqli_error($this->_conn), mysqli_errno($this->_conn));
254
255
            throw new MysqliException(
256
                $msg,
257
                $this->_conn->sqlstate,
258
                $this->_conn->errno
259
            );
260
        }
261 1
    }
262
263
    /**
264
     * Pings the server and re-connects when `mysqli.reconnect = 1`
265
     *
266
     * @return bool
267
     */
268
    public function ping()
269
    {
270
        return $this->_conn->ping();
271
    }
272
273
    /**
274
     * Establish a secure connection
275
     *
276
     * @param array $params
277
     * @throws MysqliException
278
     */
279 5
    private function setSecureConnection(array $params)
280
    {
281 5
        if (! isset($params['ssl_key']) &&
282 5
            ! isset($params['ssl_cert']) &&
283 5
            ! isset($params['ssl_ca']) &&
284 5
            ! isset($params['ssl_capath']) &&
285 5
            ! isset($params['ssl_cipher'])
286
        ) {
287 1
            return;
288
        }
289
290 4
        if (! isset($params['ssl_key']) || ! isset($params['ssl_cert'])) {
291 4
            $msg = '"ssl_key" and "ssl_cert" parameters are mandatory when using secure connection parameters.';
292 4
            throw new MysqliException($msg);
293
        }
294
295
        $this->_conn->ssl_set(
296
            $params['ssl_key'],
297
            $params['ssl_cert'],
298
            $params['ssl_ca']     ?? null,
299
            $params['ssl_capath'] ?? null,
300
            $params['ssl_cipher'] ?? null
301
        );
302
    }
303
}
304