Completed
Branch master (9dff9d)
by Albert
05:30
created

PDO::getAttribute()   C

Complexity

Conditions 13
Paths 13

Size

Total Lines 22
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 20
nc 13
nop 1
dl 0
loc 22
rs 6.6166
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Copyright: Swlib
5
 * Author: Twosee <[email protected]>
6
 * Modifier: Albert Chen
7
 * License: Apache 2.0
8
 */
9
10
namespace SwooleTW\Http\Coroutine;
11
12
use Exception;
13
use PDO as BasePDO;
14
use Illuminate\Database\QueryException;
15
use SwooleTW\Http\Coroutine\PDOStatement;
16
use SwooleTW\Http\Coroutine\StatementException;
17
use SwooleTW\Http\Coroutine\ConnectionException;
18
19
class PDO extends BasePDO
20
{
21
    public static $keyMap = [
22
        'dbname' => 'database'
23
    ];
24
25
    private static $defaultOptions = [
26
        'host' => '',
27
        'port' => 3306,
28
        'user' => '',
29
        'password' => '',
30
        'database' => '',
31
        'charset' => 'utf8mb4',
32
        'strict_type' => true
33
    ];
34
35
    /** @var \Swoole\Coroutine\Mysql */
36
    public $client;
37
38
    public $inTransaction = false;
39
40
    public function __construct(
41
        string $dsn,
42
        string $username = '',
43
        string $password = '',
44
        array $driverOptions = []
45
    ) {
46
        $this->setClient();
47
        $this->connect($this->getOptions(...func_get_args()));
48
    }
49
50
    protected function setClient($client = null)
51
    {
52
        $this->client = $client ?: new \Swoole\Coroutine\Mysql();
53
    }
54
55
    protected function connect(array $options = [])
56
    {
57
        $this->client->connect($options);
58
59
        if (! $this->client->connected) {
60
            $message = $this->client->connect_error ?: $this->client->error;
61
            $errorCode = $this->client->connect_errno ?: $this->client->errno;
62
63
            throw new ConnectionException($message, $errorCode);
64
        }
65
66
        return $this;
67
    }
68
69
    protected function getOptions($dsn, $username, $password, $driverOptions)
70
    {
71
        $dsn = explode(':', $dsn);
72
        $driver = ucwords(array_shift($dsn));
73
        $dsn = explode(';', implode(':', $dsn));
74
        $options = [];
75
76
        static::checkDriver($driver);
77
78
        foreach ($dsn as $kv) {
79
            $kv = explode('=', $kv);
0 ignored issues
show
Bug introduced by
$kv of type string[] is incompatible with the type string expected by parameter $string of explode(). ( Ignorable by Annotation )

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

79
            $kv = explode('=', /** @scrutinizer ignore-type */ $kv);
Loading history...
80
            if ($kv) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $kv of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
81
                $options[$kv[0]] = $kv[1] ?? '';
82
            }
83
        }
84
85
        $authorization = [
86
            'user' => $username,
87
            'password' => $password,
88
        ];
89
90
        $options = $driverOptions + $authorization + $options;
91
92
        foreach (static::$keyMap as $pdoKey => $swpdoKey) {
93
            if (isset($options[$pdoKey])) {
94
                $options[$swpdoKey] = $options[$pdoKey];
95
                unset($options[$pdoKey]);
96
            }
97
        }
98
99
        return $options + static::$defaultOptions;
0 ignored issues
show
Bug introduced by
Since $defaultOptions is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $defaultOptions to at least protected.
Loading history...
100
    }
101
102
    public static function checkDriver(string $driver)
103
    {
104
        if (! in_array($driver, static::getAvailableDrivers())) {
105
            throw new \InvalidArgumentException("{$driver} driver is not supported yet.");
106
        }
107
    }
108
109
    public static function getAvailableDrivers()
110
    {
111
        return ['Mysql'];
112
    }
113
114
    public function beginTransaction()
115
    {
116
        $this->client->begin();
117
        $this->inTransaction = true;
118
    }
119
120
    public function rollBack()
121
    {
122
        $this->client->rollback();
123
        $this->inTransaction = false;
124
    }
125
126
    public function commit()
127
    {
128
        $this->client->commit();
129
        $this->inTransaction = true;
130
    }
131
132
    public function inTransaction()
133
    {
134
        return $this->inTransaction;
135
    }
136
137
    public function lastInsertId($seqname = null)
138
    {
139
        return $this->client->insert_id;
140
    }
141
142
    public function errorCode()
143
    {
144
        $this->client->errno;
145
    }
146
147
    public function errorInfo()
148
    {
149
        return [
150
            $this->client->errno,
151
            $this->client->errno,
152
            $this->client->error,
153
        ];
154
    }
155
156
    public function exec($statement): int
157
    {
158
        $this->query($statement);
159
160
        return $this->client->affected_rows;
161
    }
162
163
    public function query(string $statement, float $timeout = -1)
164
    {
165
        $result = $this->client->query($statement, $timeout);
166
167
        if ($result === false) {
168
            $exception = new Exception($this->client->error, $this->client->errno);
169
            throw new QueryException($statement, [], $exception);
170
        }
171
172
        return $result;
173
    }
174
175
    private function rewriteToPosition(string $statement)
0 ignored issues
show
Unused Code introduced by
The parameter $statement is not used and could be removed. ( Ignorable by Annotation )

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

175
    private function rewriteToPosition(/** @scrutinizer ignore-unused */ string $statement)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The method rewriteToPosition() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
176
    {
177
        //
178
    }
179
180
    public function prepare($statement, $driverOptions = null)
181
    {
182
        $driverOptions = is_null($driverOptions) ? [] : $driverOptions;
183
        if (strpos($statement, ':') !== false) {
184
            $i = 0;
185
            $bindKeyMap = [];
186
            $statement = preg_replace_callback(
187
                '/:(\w+)\b/',
188
                function ($matches) use (&$i, &$bindKeyMap) {
189
                    $bindKeyMap[$matches[1]] = $i++;
190
191
                    return '?';
192
                },
193
                $statement
194
            );
195
        }
196
197
        $stmtObj = $this->client->prepare($statement);
198
199
        if ($stmtObj) {
0 ignored issues
show
introduced by
$stmtObj is of type Swoole\Coroutine\Mysql\Statement, thus it always evaluated to true.
Loading history...
200
            $stmtObj->bindKeyMap = $bindKeyMap ?? [];
0 ignored issues
show
Bug introduced by
The property bindKeyMap does not seem to exist on Swoole\Coroutine\Mysql\Statement.
Loading history...
201
            return new PDOStatement($this, $stmtObj, $driverOptions);
202
        } else {
203
            $statementException = new StatementException($this->client->error, $this->client->errno);
204
            throw new QueryException($statement, [], $statementException);
205
        }
206
    }
207
208
    public function getAttribute($attribute)
209
    {
210
        switch ($attribute) {
211
            case \PDO::ATTR_AUTOCOMMIT:
212
                return true;
213
            case \PDO::ATTR_CASE:
214
            case \PDO::ATTR_CLIENT_VERSION:
215
            case \PDO::ATTR_CONNECTION_STATUS:
216
                return $this->client->connected;
217
            case \PDO::ATTR_DRIVER_NAME:
218
            case \PDO::ATTR_ERRMODE:
219
                return 'Swoole Style';
220
            case \PDO::ATTR_ORACLE_NULLS:
221
            case \PDO::ATTR_PERSISTENT:
222
            case \PDO::ATTR_PREFETCH:
223
            case \PDO::ATTR_SERVER_INFO:
224
                return $this->serverInfo['timeout'] ?? static::$defaultOptions['timeout'];
0 ignored issues
show
Bug introduced by
Since $defaultOptions is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $defaultOptions to at least protected.
Loading history...
Bug Best Practice introduced by
The property serverInfo does not exist on SwooleTW\Http\Coroutine\PDO. Did you maybe forget to declare it?
Loading history...
225
            case \PDO::ATTR_SERVER_VERSION:
226
                return 'Swoole Mysql';
227
            case \PDO::ATTR_TIMEOUT:
228
            default:
229
                throw new \InvalidArgumentException('Not implemented yet!');
230
        }
231
    }
232
233
    public function quote($string, $paramtype = null)
234
    {
235
        throw new \BadMethodCallException(<<<TXT
236
If you are using this function to build SQL statements,
237
you are strongly recommended to use PDO::prepare() to prepare SQL statements
238
with bound parameters instead of using PDO::quote() to interpolate user input into an SQL statement.
239
Prepared statements with bound parameters are not only more portable, more convenient,
240
immune to SQL injection, but are often much faster to execute than interpolated queries,
241
as both the server and client side can cache a compiled form of the query.
242
TXT
243
        );
244
    }
245
246
    public function __destruct()
247
    {
248
        $this->client->close();
249
    }
250
}
251