Failed Conditions
Pull Request — master (#2617)
by Walt
34:21
created

MysqliConnection::__construct()   B

Complexity

Conditions 4
Paths 0

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 30
ccs 0
cts 21
cp 0
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 17
nc 0
nop 4
crap 20
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
use Doctrine\DBAL\ParameterType;
26
27
/**
28
 * @author Kim Hemsø Rasmussen <[email protected]>
29
 * @author Till Klampaeckel <[email protected]>
30
 */
31
class MysqliConnection implements Connection, PingableConnection, ServerInfoAwareConnection
32
{
33
    /**
34
     * Name of the option to set connection flags
35
     */
36
    const OPTION_FLAGS = 'flags';
37
38
    /**
39
     * @var \mysqli
40
     */
41
    private $_conn;
42
43
    /**
44
     * @param array  $params
45
     * @param string $username
46
     * @param string $password
47
     * @param array  $driverOptions
48
     *
49
     * @throws \Doctrine\DBAL\Driver\Mysqli\MysqliException
50
     */
51
    public function __construct(array $params, $username, $password, array $driverOptions = [])
52
    {
53
        $port = $params['port'] ?? ini_get('mysqli.default_port');
54
55
        // Fallback to default MySQL port if not given.
56
        if ( ! $port) {
57
            $port = 3306;
58
        }
59
60
        $socket = $params['unix_socket'] ?? ini_get('mysqli.default_socket');
61
        $dbname = $params['dbname'] ?? null;
62
63
        $flags = $driverOptions[static::OPTION_FLAGS] ?? null;
64
65
        $this->_conn = mysqli_init();
66
67
        $this->setSecureConnection($params);
68
        $this->setDriverOptions($driverOptions);
69
70
        set_error_handler(function () {});
71
        try {
72
            if ( ! $this->_conn->real_connect($params['host'], $username, $password, $dbname, $port, $socket, $flags)) {
73
                throw new MysqliException($this->_conn->connect_error, $this->_conn->sqlstate ?? 'HY000', $this->_conn->connect_errno);
74
            }
75
        } finally {
76
            restore_error_handler();
77
        }
78
79
        if (isset($params['charset'])) {
0 ignored issues
show
Unused Code introduced by
IfNode is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
80
            $this->_conn->set_charset($params['charset']);
81
        }
82
    }
83
84
    /**
85
     * Retrieves mysqli native resource handle.
86
     *
87
     * Could be used if part of your application is not using DBAL.
88
     *
89
     * @return \mysqli
90
     */
91
    public function getWrappedResourceHandle()
92
    {
93
        return $this->_conn;
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     *
99
     * The server version detection includes a special case for MariaDB
100
     * to support '5.5.5-' prefixed versions introduced in Maria 10+
101
     * @link https://jira.mariadb.org/browse/MDEV-4088
102
     */
103
    public function getServerVersion()
104
    {
105
        $serverInfos = $this->_conn->get_server_info();
106
        if (false !== stripos($serverInfos, 'mariadb')) {
107
            return $serverInfos;
108
        }
109
110
        $majorVersion = floor($this->_conn->server_version / 10000);
111
        $minorVersion = floor(($this->_conn->server_version - $majorVersion * 10000) / 100);
112
        $patchVersion = floor($this->_conn->server_version - $majorVersion * 10000 - $minorVersion * 100);
113
114
        return $majorVersion . '.' . $minorVersion . '.' . $patchVersion;
115
    }
116
117
    /**
118
     * {@inheritdoc}
119
     */
120
    public function requiresQueryForServerVersion()
121
    {
122
        return false;
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     */
128
    public function prepare($prepareString)
129
    {
130
        return new MysqliStatement($this->_conn, $prepareString);
131
    }
132
133
    /**
134
     * {@inheritdoc}
135
     */
136
    public function query()
137
    {
138
        $args = func_get_args();
139
        $sql = $args[0];
140
        $stmt = $this->prepare($sql);
141
        $stmt->execute();
142
143
        return $stmt;
144
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149
    public function quote($input, $type = ParameterType::STRING)
150
    {
151
        return "'". $this->_conn->escape_string($input) ."'";
152
    }
153
154
    /**
155
     * {@inheritdoc}
156
     */
157
    public function exec($statement)
158
    {
159
        if (false === $this->_conn->query($statement)) {
160
            throw new MysqliException($this->_conn->error, $this->_conn->sqlstate, $this->_conn->errno);
161
        }
162
163
        return $this->_conn->affected_rows;
164
    }
165
166
    /**
167
     * {@inheritdoc}
168
     */
169
    public function lastInsertId($name = null)
170
    {
171
        return $this->_conn->insert_id;
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177
    public function beginTransaction()
178
    {
179
        $this->_conn->query('START TRANSACTION');
180
181
        return true;
182
    }
183
184
    /**
185
     * {@inheritdoc}
186
     */
187
    public function commit()
188
    {
189
        return $this->_conn->commit();
190
    }
191
192
    /**
193
     * {@inheritdoc}non-PHPdoc)
194
     */
195
    public function rollBack()
196
    {
197
        return $this->_conn->rollback();
198
    }
199
200
    /**
201
     * {@inheritdoc}
202
     */
203
    public function errorCode()
204
    {
205
        return $this->_conn->errno;
206
    }
207
208
    /**
209
     * {@inheritdoc}
210
     */
211
    public function errorInfo()
212
    {
213
        return $this->_conn->error;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_conn->error returns the type string which is incompatible with the return type mandated by Doctrine\DBAL\Driver\Connection::errorInfo() of array.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
214
    }
215
216
    /**
217
     * Apply the driver options to the connection.
218
     *
219
     * @param array $driverOptions
220
     *
221
     * @throws MysqliException When one of of the options is not supported.
222
     * @throws MysqliException When applying doesn't work - e.g. due to incorrect value.
223
     */
224
    private function setDriverOptions(array $driverOptions = [])
225
    {
226
        $supportedDriverOptions = [
227
            \MYSQLI_OPT_CONNECT_TIMEOUT,
228
            \MYSQLI_OPT_LOCAL_INFILE,
229
            \MYSQLI_INIT_COMMAND,
230
            \MYSQLI_READ_DEFAULT_FILE,
231
            \MYSQLI_READ_DEFAULT_GROUP,
232
        ];
233
234
        if (defined('MYSQLI_SERVER_PUBLIC_KEY')) {
235
            $supportedDriverOptions[] = \MYSQLI_SERVER_PUBLIC_KEY;
236
        }
237
238
        $exceptionMsg = "%s option '%s' with value '%s'";
239
240
        foreach ($driverOptions as $option => $value) {
241
242
            if ($option === static::OPTION_FLAGS) {
243
                continue;
244
            }
245
246
            if (!in_array($option, $supportedDriverOptions, true)) {
247
                throw new MysqliException(
248
                    sprintf($exceptionMsg, 'Unsupported', $option, $value)
249
                );
250
            }
251
252
            if (@mysqli_options($this->_conn, $option, $value)) {
253
                continue;
254
            }
255
256
            $msg  = sprintf($exceptionMsg, 'Failed to set', $option, $value);
257
            $msg .= sprintf(', error: %s (%d)', mysqli_error($this->_conn), mysqli_errno($this->_conn));
258
259
            throw new MysqliException(
260
                $msg,
261
                $this->_conn->sqlstate,
262
                $this->_conn->errno
263
            );
264
        }
265
    }
266
267
    /**
268
     * Pings the server and re-connects when `mysqli.reconnect = 1`
269
     *
270
     * @return bool
271
     */
272
    public function ping()
273
    {
274
        return $this->_conn->ping();
275
    }
276
277
    /**
278
     * Establish a secure connection
279
     *
280
     * @param array $params
281
     * @throws MysqliException
282
     */
283
    private function setSecureConnection(array $params)
284
    {
285
        if (! isset($params['ssl_key']) &&
286
            ! isset($params['ssl_cert']) &&
287
            ! isset($params['ssl_ca']) &&
288
            ! isset($params['ssl_capath']) &&
289
            ! isset($params['ssl_cipher'])
290
        ) {
291
            return;
292
        }
293
294
        $this->_conn->ssl_set(
295
            $params['ssl_key']    ?? null,
296
            $params['ssl_cert']   ?? null,
297
            $params['ssl_ca']     ?? null,
298
            $params['ssl_capath'] ?? null,
299
            $params['ssl_cipher'] ?? null
300
        );
301
    }
302
}
303