Failed Conditions
Push — master ( ea968d...edfbda )
by Luís
18s
created

MysqliConnection   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 260
Duplicated Lines 3.46 %

Test Coverage

Coverage 31.82%

Importance

Changes 0
Metric Value
wmc 35
dl 9
loc 260
ccs 28
cts 88
cp 0.3182
rs 9
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A requiresQueryForServerVersion() 0 3 1
B setDriverOptions() 0 39 6
A rollBack() 0 3 1
A getWrappedResourceHandle() 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
A lastInsertId() 0 3 1
A getServerVersion() 0 7 1
A errorCode() 0 3 1
A quote() 0 3 1
A beginTransaction() 0 5 1
D __construct() 0 30 8
A commit() 0 3 1
B setSecureConnection() 0 17 6

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