Failed Conditions
Pull Request — master (#2849)
by Luís
63:28
created

MysqliConnection::setSecureConnection()   B

Complexity

Conditions 8
Paths 3

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 10.9144

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 9
cts 14
cp 0.6429
rs 7.7777
c 0
b 0
f 0
cc 8
eloc 13
nc 3
nop 1
crap 10.9144
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
        });
71 1
72 1
        try {
73
            if ( ! $this->_conn->real_connect($params['host'], $username, $password, $dbname, $port, $socket, $flags)) {
74
                throw new MysqliException($this->_conn->connect_error, $this->_conn->sqlstate ?? 'HY000', $this->_conn->connect_errno);
75 1
            }
76
        } finally {
77
            restore_error_handler();
78
        }
79
80
        if (isset($params['charset'])) {
81
            $this->_conn->set_charset($params['charset']);
82
        }
83
    }
84
85
    /**
86
     * Retrieves mysqli native resource handle.
87
     *
88
     * Could be used if part of your application is not using DBAL.
89
     *
90
     * @return \mysqli
91
     */
92
    public function getWrappedResourceHandle()
93
    {
94
        return $this->_conn;
95
    }
96
97
    /**
98
     * {@inheritdoc}
99
     */
100
    public function getServerVersion()
101
    {
102
        $majorVersion = floor($this->_conn->server_version / 10000);
103
        $minorVersion = floor(($this->_conn->server_version - $majorVersion * 10000) / 100);
104
        $patchVersion = floor($this->_conn->server_version - $majorVersion * 10000 - $minorVersion * 100);
105
106
        return $majorVersion . '.' . $minorVersion . '.' . $patchVersion;
107
    }
108
109
    /**
110 1
     * {@inheritdoc}
111
     */
112 1
    public function requiresQueryForServerVersion()
113
    {
114
        return false;
115
    }
116
117
    /**
118
     * {@inheritdoc}
119
     */
120
    public function prepare($prepareString)
121
    {
122
        return new MysqliStatement($this->_conn, $prepareString);
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     */
128 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...
129
    {
130
        $args = func_get_args();
131
        $sql  = $args[0];
132
        $stmt = $this->prepare($sql);
133
        $stmt->execute();
134
135
        return $stmt;
136
    }
137
138
    /**
139
     * {@inheritdoc}
140
     */
141
    public function quote($input, $type = \PDO::PARAM_STR)
142
    {
143
        return "'" . $this->_conn->escape_string($input) . "'";
144
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149
    public function exec($statement)
150
    {
151
        if (false === $this->_conn->query($statement)) {
152
            throw new MysqliException($this->_conn->error, $this->_conn->sqlstate, $this->_conn->errno);
153
        }
154
155
        return $this->_conn->affected_rows;
156
    }
157
158
    /**
159
     * {@inheritdoc}
160
     */
161
    public function lastInsertId($name = null)
162
    {
163
        return $this->_conn->insert_id;
164
    }
165
166
    /**
167
     * {@inheritdoc}
168
     */
169
    public function beginTransaction()
170
    {
171
        $this->_conn->query('START TRANSACTION');
172
173
        return true;
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179
    public function commit()
180
    {
181
        return $this->_conn->commit();
182
    }
183
184
    /**
185
     * {@inheritdoc}non-PHPdoc)
186
     */
187
    public function rollBack()
188
    {
189
        return $this->_conn->rollback();
190
    }
191
192
    /**
193
     * {@inheritdoc}
194
     */
195
    public function errorCode()
196
    {
197
        return $this->_conn->errno;
198
    }
199
200
    /**
201
     * {@inheritdoc}
202
     */
203
    public function errorInfo()
204
    {
205
        return $this->_conn->error;
206
    }
207
208
    /**
209
     * Apply the driver options to the connection.
210
     *
211
     * @param array $driverOptions
212
     *
213
     * @throws MysqliException When one of of the options is not supported.
214 1
     * @throws MysqliException When applying doesn't work - e.g. due to incorrect value.
215
     */
216
    private function setDriverOptions(array $driverOptions = [])
217 1
    {
218
        $supportedDriverOptions = [
219
            \MYSQLI_OPT_CONNECT_TIMEOUT,
220
            \MYSQLI_OPT_LOCAL_INFILE,
221
            \MYSQLI_INIT_COMMAND,
222
            \MYSQLI_READ_DEFAULT_FILE,
223
            \MYSQLI_READ_DEFAULT_GROUP,
224 1
        ];
225 1
226
        if (defined('MYSQLI_SERVER_PUBLIC_KEY')) {
227
            $supportedDriverOptions[] = \MYSQLI_SERVER_PUBLIC_KEY;
228 1
        }
229
230 1
        $exceptionMsg = "%s option '%s' with value '%s'";
231
232
        foreach ($driverOptions as $option => $value) {
233
            if ($option === static::OPTION_FLAGS) {
234
                continue;
235
            }
236
237
            if ( ! in_array($option, $supportedDriverOptions, true)) {
238
                throw new MysqliException(
239
                    sprintf($exceptionMsg, 'Unsupported', $option, $value)
240
                );
241
            }
242
243
            if (@mysqli_options($this->_conn, $option, $value)) {
244
                continue;
245
            }
246
247
            $msg  = sprintf($exceptionMsg, 'Failed to set', $option, $value);
248
            $msg .= sprintf(', error: %s (%d)', mysqli_error($this->_conn), mysqli_errno($this->_conn));
249
250
            throw new MysqliException(
251
                $msg,
252
                $this->_conn->sqlstate,
253
                $this->_conn->errno
254
            );
255 1
        }
256
    }
257
258
    /**
259
     * Pings the server and re-connects when `mysqli.reconnect = 1`
260
     *
261
     * @return bool
262
     */
263
    public function ping()
264
    {
265
        return $this->_conn->ping();
266
    }
267
268
    /**
269
     * Establish a secure connection
270
     *
271
     * @param array $params
272
     * @throws MysqliException
273 5
     */
274
    private function setSecureConnection(array $params)
275 5
    {
276 5
        if ( ! isset($params['ssl_key']) && ! isset($params['ssl_cert'])
277 5
            && ! isset($params['ssl_ca']) && ! isset($params['ssl_capath']) && ! isset($params['ssl_cipher'])) {
278 5
            return;
279 5
        }
280
281 1
        if ( ! isset($params['ssl_key']) || ! isset($params['ssl_cert'])) {
282
            $msg = '"ssl_key" and "ssl_cert" parameters are mandatory when using secure connection parameters.';
283
            throw new MysqliException($msg);
284 4
        }
285 4
286 4
        $this->_conn->ssl_set(
287
            $params['ssl_key'],
288
            $params['ssl_cert'],
289
            $params['ssl_ca']     ?? null,
290
            $params['ssl_capath'] ?? null,
291
            $params['ssl_cipher'] ?? null
292
        );
293
    }
294
}
295