Test Failed
Pull Request — master (#2765)
by Marco
04:16
created

MysqliConnection::ping()   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;
23
use Doctrine\DBAL\Driver\LastInsertId;
24
use Doctrine\DBAL\Driver\PingableConnection;
25
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
26
use Doctrine\DBAL\Driver\Statement;
27
28
/**
29
 * @author Kim Hemsø Rasmussen <[email protected]>
30
 * @author Till Klampaeckel <[email protected]>
31
 */
32
class MysqliConnection implements Connection, PingableConnection, ServerInfoAwareConnection
33
{
34
    /**
35
     * Name of the option to set connection flags
36
     */
37
    const OPTION_FLAGS = 'flags';
38
39
    /**
40
     * @var \mysqli
41
     */
42
    private $_conn;
43
44
    /**
45
     * @var LastInsertId
46
     */
47
    private $lastInsertId;
48
49
    /**
50
     * @param array  $params
51
     * @param string $username
52
     * @param string $password
53
     * @param array  $driverOptions
54
     *
55
     * @throws \Doctrine\DBAL\Driver\Mysqli\MysqliException
56
     */
57 5
    public function __construct(array $params, $username, $password, array $driverOptions = [])
58
    {
59 5
        $port = isset($params['port']) ? $params['port'] : ini_get('mysqli.default_port');
60
61
        // Fallback to default MySQL port if not given.
62 5
        if ( ! $port) {
63
            $port = 3306;
64
        }
65
66 5
        $socket = isset($params['unix_socket']) ? $params['unix_socket'] : ini_get('mysqli.default_socket');
67 5
        $dbname = isset($params['dbname']) ? $params['dbname'] : null;
68
69 5
        $flags = isset($driverOptions[static::OPTION_FLAGS]) ? $driverOptions[static::OPTION_FLAGS] : null;
70
71 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...
72
73 5
        $this->setSecureConnection($params);
74 1
        $this->setDriverOptions($driverOptions);
75
76
        set_error_handler(function () {});
77
        try {
78 1
            if ( ! $this->_conn->real_connect($params['host'], $username, $password, $dbname, $port, $socket, $flags)) {
79 1
                throw new MysqliException($this->_conn->connect_error, $this->_conn->sqlstate ?? 'HY000', $this->_conn->connect_errno);
80
            }
81
        } finally {
82 1
            restore_error_handler();
83
        }
84
85
        if (isset($params['charset'])) {
86
            $this->_conn->set_charset($params['charset']);
87
        }
88
89
        $this->lastInsertId = new LastInsertId();
90
    }
91
92
    /**
93
     * Retrieves mysqli native resource handle.
94
     *
95
     * Could be used if part of your application is not using DBAL.
96
     *
97
     * @return \mysqli
98
     */
99
    public function getWrappedResourceHandle()
100
    {
101
        return $this->_conn;
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     */
107
    public function getServerVersion()
108
    {
109
        $majorVersion = floor($this->_conn->server_version / 10000);
110
        $minorVersion = floor(($this->_conn->server_version - $majorVersion * 10000) / 100);
111
        $patchVersion = floor($this->_conn->server_version - $majorVersion * 10000 - $minorVersion * 100);
112
113
        return $majorVersion . '.' . $minorVersion . '.' . $patchVersion;
114
    }
115
116
    /**
117
     * {@inheritdoc}
118
     */
119 1
    public function requiresQueryForServerVersion()
120
    {
121 1
        return false;
122
    }
123
124
    /**
125
     * {@inheritdoc}
126
     *
127
     * @throws \Doctrine\DBAL\Driver\Mysqli\MysqliException
128
     */
129
    public function prepare($prepareString) : Statement
130
    {
131
        return new MysqliStatement($this->_conn, $prepareString, $this->lastInsertId);
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137 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...
138
    {
139
        $args = func_get_args();
140
        $sql = $args[0];
141
        $stmt = $this->prepare($sql);
142
        $stmt->execute();
143
144
        return $stmt;
145
    }
146
147
    /**
148
     * {@inheritdoc}
149
     */
150
    public function quote($input, $type=\PDO::PARAM_STR)
151
    {
152
        return "'". $this->_conn->escape_string($input) ."'";
153
    }
154
155
    /**
156
     * {@inheritdoc}
157
     *
158
     * @throws \Doctrine\DBAL\Driver\Mysqli\MysqliException
159
     */
160
    public function exec($statement)
161
    {
162
        if (false === $this->_conn->query($statement)) {
163
            throw new MysqliException($this->_conn->error, $this->_conn->sqlstate, $this->_conn->errno);
164
        }
165
166
        $this->lastInsertId->set((string) $this->_conn->insert_id);
167
168
        return $this->_conn->affected_rows;
169
    }
170
171
    /**
172
     * {@inheritdoc}
173
     */
174
    public function lastInsertId($name = null) : string
175
    {
176
        return $this->lastInsertId->get();
177
    }
178
179
    /**
180
     * {@inheritdoc}
181
     */
182
    public function beginTransaction()
183
    {
184
        $this->_conn->query('START TRANSACTION');
185
186
        return true;
187
    }
188
189
    /**
190
     * {@inheritdoc}
191
     */
192
    public function commit()
193
    {
194
        return $this->_conn->commit();
195
    }
196
197
    /**
198
     * {@inheritdoc}non-PHPdoc)
199
     */
200
    public function rollBack()
201
    {
202
        return $this->_conn->rollback();
203
    }
204
205
    /**
206
     * {@inheritdoc}
207
     */
208
    public function errorCode()
209
    {
210
        return $this->_conn->errno;
211
    }
212
213
    /**
214
     * {@inheritdoc}
215
     */
216
    public function errorInfo()
217
    {
218
        return $this->_conn->error;
219
    }
220
221
    /**
222
     * Apply the driver options to the connection.
223
     *
224
     * @param array $driverOptions
225
     *
226
     * @throws MysqliException When one of of the options is not supported.
227
     * @throws MysqliException When applying doesn't work - e.g. due to incorrect value.
228
     */
229 1
    private function setDriverOptions(array $driverOptions = [])
230
    {
231
        $supportedDriverOptions = [
232 1
            \MYSQLI_OPT_CONNECT_TIMEOUT,
233
            \MYSQLI_OPT_LOCAL_INFILE,
234
            \MYSQLI_INIT_COMMAND,
235
            \MYSQLI_READ_DEFAULT_FILE,
236
            \MYSQLI_READ_DEFAULT_GROUP,
237
        ];
238
239 1
        if (defined('MYSQLI_SERVER_PUBLIC_KEY')) {
240 1
            $supportedDriverOptions[] = \MYSQLI_SERVER_PUBLIC_KEY;
241
        }
242
243 1
        $exceptionMsg = "%s option '%s' with value '%s'";
244
245 1
        foreach ($driverOptions as $option => $value) {
246
247
            if ($option === static::OPTION_FLAGS) {
248
                continue;
249
            }
250
251
            if (!in_array($option, $supportedDriverOptions, true)) {
252
                throw new MysqliException(
253
                    sprintf($exceptionMsg, 'Unsupported', $option, $value)
254
                );
255
            }
256
257
            if (@mysqli_options($this->_conn, $option, $value)) {
258
                continue;
259
            }
260
261
            $msg  = sprintf($exceptionMsg, 'Failed to set', $option, $value);
262
            $msg .= sprintf(', error: %s (%d)', mysqli_error($this->_conn), mysqli_errno($this->_conn));
263
264
            throw new MysqliException(
265
                $msg,
266
                $this->_conn->sqlstate,
267
                $this->_conn->errno
268
            );
269
        }
270 1
    }
271
272
    /**
273
     * Pings the server and re-connects when `mysqli.reconnect = 1`
274
     *
275
     * @return bool
276
     */
277
    public function ping()
278
    {
279
        return $this->_conn->ping();
280
    }
281
282
    /**
283
     * Establish a secure connection
284
     *
285
     * @param array $params
286
     * @throws MysqliException
287
     */
288 5
    private function setSecureConnection(array $params)
289
    {
290 5
        if (! isset($params['ssl_key']) &&
291 3
            ! isset($params['ssl_cert']) &&
292 2
            ! isset($params['ssl_ca']) &&
293 1
            ! isset($params['ssl_capath']) &&
294 1
            ! isset($params['ssl_cipher'])
295
        ) {
296 1
            return;
297
        }
298
299 4
        if (! isset($params['ssl_key']) || ! isset($params['ssl_cert'])) {
300 4
            $msg = '"ssl_key" and "ssl_cert" parameters are mandatory when using secure connection parameters.';
301 4
            throw new MysqliException($msg);
302
        }
303
304
        $this->_conn->ssl_set(
305
            $params['ssl_key'],
306
            $params['ssl_cert'],
307
            $params['ssl_ca']     ?? null,
308
            $params['ssl_capath'] ?? null,
309
            $params['ssl_cipher'] ?? null
310
        );
311
    }
312
}
313