Failed Conditions
Push — master ( edfbda...298c91 )
by Luís
16s
created

MysqliConnection   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 269
Duplicated Lines 3.35 %

Test Coverage

Coverage 30.76%

Importance

Changes 0
Metric Value
wmc 36
dl 9
loc 269
rs 8.8
c 0
b 0
f 0
ccs 28
cts 91
cp 0.3076

17 Methods

Rating   Name   Duplication   Size   Complexity  
A getWrappedResourceHandle() 0 3 1
D __construct() 0 30 8
A requiresQueryForServerVersion() 0 3 1
B setDriverOptions() 0 39 6
A rollBack() 0 3 1
A exec() 0 7 2
A prepare() 0 3 1
A query() 8 8 1
A errorInfo() 0 3 1
A ping() 0 3 1
B setSecureConnection() 0 17 6
A lastInsertId() 0 3 1
A getServerVersion() 0 12 2
A errorCode() 0 3 1
A quote() 0 3 1
A beginTransaction() 0 5 1
A commit() 0 3 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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 1
    public function __construct(array $params, $username, $password, array $driverOptions = [])
51
    {
52 1
        $port = isset($params['port']) ? $params['port'] : ini_get('mysqli.default_port');
53
54
        // Fallback to default MySQL port if not given.
55 1
        if ( ! $port) {
56
            $port = 3306;
57
        }
58
59 1
        $socket = isset($params['unix_socket']) ? $params['unix_socket'] : ini_get('mysqli.default_socket');
60 1
        $dbname = isset($params['dbname']) ? $params['dbname'] : null;
61
62 1
        $flags = isset($driverOptions[static::OPTION_FLAGS]) ? $driverOptions[static::OPTION_FLAGS] : null;
63
64 1
        $this->_conn = mysqli_init();
65
66 1
        $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)) {
0 ignored issues
show
Bug introduced by
It seems like $port can also be of type string; however, parameter $port of mysqli::real_connect() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

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