Issues (87)

src/Coroutine/PDOStatement.php (4 issues)

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 PDOStatement as BaseStatement;
13
use Swoole\Coroutine\MySQL\Statement;
14
15
class PDOStatement extends BaseStatement
16
{
17
    private $parent;
18
19
    public $statement;
20
21
    public $timeout;
22
23
    public $bindMap = [];
24
25
    public $cursor = -1;
26
27
    public $cursorOrientation = PDO::FETCH_ORI_NEXT;
28
29
    public $resultSet = [];
30
31
    public $fetchStyle = PDO::FETCH_BOTH;
32
33
    public function __construct(PDO $parent, Statement $statement, array $driverOptions = [])
34
    {
35
        $this->parent = $parent;
36
        $this->statement = $statement;
37
        $this->timeout = $driverOptions['timeout'] ?? -1;
38
    }
39
40
    public function errorCode()
41
    {
42
        return $this->statement->errno;
43
    }
44
45
    public function errorInfo()
46
    {
47
        return $this->statement->error;
48
    }
49
50
    public function rowCount()
51
    {
52
        return $this->statement->affected_rows;
53
    }
54
55
    public function bindParam($parameter, &$variable, $type = null, $maxlen = null, $driverdata = null)
56
    {
57
        if (! is_string($parameter) && ! is_int($parameter)) {
58
            return false;
59
        }
60
61
        $parameter = ltrim($parameter, ':');
62
        $this->bindMap[$parameter] = &$variable;
63
64
        return true;
65
    }
66
67
    public function bindValue($parameter, $variable, $type = null)
68
    {
69
        if (! is_string($parameter) && ! is_int($parameter)) {
70
            return false;
71
        }
72
73
        if (is_object($variable)) {
74
            if (! method_exists($variable, '__toString')) {
75
                return false;
76
            } else {
77
                $variable = (string) $variable;
78
            }
79
        }
80
81
        $parameter = ltrim($parameter, ':');
82
        $this->bindMap[$parameter] = $variable;
83
84
        return true;
85
    }
86
87
    private function afterExecute()
88
    {
89
        $this->cursor = -1;
90
        $this->bindMap = [];
91
    }
92
93
    public function execute($inputParameters = null)
94
    {
95
        if (! empty($inputParameters)) {
96
            foreach ($inputParameters as $key => $value) {
97
                $this->bindParam($key, $value);
98
            }
99
        }
100
101
        $inputParameters = [];
102
        if (! empty($this->statement->bindKeyMap)) {
103
            foreach ($this->statement->bindKeyMap as $nameKey => $numKey) {
0 ignored issues
show
The property bindKeyMap does not seem to exist on Swoole\Coroutine\Mysql\Statement.
Loading history...
104
                if (isset($this->bindMap[$nameKey])) {
105
                    $inputParameters[$numKey] = $this->bindMap[$nameKey];
106
                }
107
            }
108
        } else {
109
            $inputParameters = $this->bindMap;
110
        }
111
112
        $result = $this->statement->execute($inputParameters, $this->timeout);
113
        $this->resultSet = ($ok = $result !== false) ? $result : [];
114
        $this->afterExecute();
115
116
        if ($result === false) {
117
            throw new \PDOException($this->errorInfo(), $this->errorCode());
118
        }
119
120
        return $ok;
121
    }
122
123
    public function setFetchMode($fetchStyle, $params = null)
124
    {
125
        $this->fetchStyle = $fetchStyle;
0 ignored issues
show
Bug Best Practice introduced by
The expression ImplicitReturnNode returns the type null which is incompatible with the return type mandated by PDOStatement::setFetchMode() of boolean.

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...
126
    }
127
128
    private function __executeWhenStringQueryEmpty()
129
    {
130
        if (is_string($this->statement) && empty($this->resultSet)) {
0 ignored issues
show
The condition is_string($this->statement) is always false.
Loading history...
131
            $this->resultSet = $this->parent->client->query($this->statement);
132
            $this->afterExecute();
133
        }
134
    }
135
136
    private function transBoth($rawData)
137
    {
138
        $temp = [];
139
        foreach ($rawData as $row) {
140
            $rowSet = [];
141
            $i = 0;
142
            foreach ($row as $key => $value) {
143
                $rowSet[$key] = $value;
144
                $rowSet[$i++] = $value;
145
            }
146
            $temp[] = $rowSet;
147
        }
148
149
        return $temp;
150
    }
151
152
    private function transStyle(
153
        $rawData,
154
        $fetchStyle = null,
155
        $fetchArgument = null,
156
        $ctorArgs = null
157
    )
158
    {
159
        if (! is_array($rawData)) {
160
            return false;
161
        }
162
        if (empty($rawData)) {
163
            return $rawData;
164
        }
165
166
        $fetchStyle = is_null($fetchStyle) ? $this->fetchStyle : $fetchStyle;
167
        $ctorArgs = is_null($ctorArgs) ? [] : $ctorArgs;
0 ignored issues
show
The assignment to $ctorArgs is dead and can be removed.
Loading history...
168
169
        $resultSet = [];
170
        switch ($fetchStyle) {
171
            case PDO::FETCH_BOTH:
172
                $resultSet = $this->transBoth($rawData);
173
                break;
174
            case PDO::FETCH_COLUMN:
175
                $resultSet = array_column(
176
                    is_numeric($fetchArgument) ? $this->transBoth($rawData) : $rawData,
177
                    $fetchArgument
178
                );
179
                break;
180
            case PDO::FETCH_OBJ:
181
                foreach ($rawData as $row) {
182
                    $resultSet[] = (object) $row;
183
                }
184
                break;
185
            case PDO::FETCH_NUM:
186
                foreach ($rawData as $row) {
187
                    $resultSet[] = array_values($row);
188
                }
189
                break;
190
            case PDO::FETCH_ASSOC:
191
            default:
192
                return $rawData;
193
        }
194
195
        return $resultSet;
196
    }
197
198
    public function fetch(
199
        $fetchStyle = null,
200
        $cursorOrientation = null,
201
        $cursorOffset = null,
202
        $fetchArgument = null
203
    )
204
    {
205
        $this->__executeWhenStringQueryEmpty();
206
207
        $cursorOrientation = is_null($cursorOrientation) ? PDO::FETCH_ORI_NEXT : $cursorOrientation;
208
        $cursorOffset = is_null($cursorOffset) ? 0 : (int) $cursorOffset;
209
210
        switch ($cursorOrientation) {
211
            case PDO::FETCH_ORI_ABS:
212
                $this->cursor = $cursorOffset;
213
                break;
214
            case PDO::FETCH_ORI_REL:
215
                $this->cursor += $cursorOffset;
216
                break;
217
            case PDO::FETCH_ORI_NEXT:
218
            default:
219
                $this->cursor++;
220
        }
221
222
        if (isset($this->resultSet[$this->cursor])) {
223
            $result = $this->resultSet[$this->cursor];
224
            unset($this->resultSet[$this->cursor]);
225
        } else {
226
            $result = false;
227
        }
228
229
        if (empty($result)) {
230
            return $result;
231
        } else {
232
            return $this->transStyle([$result], $fetchStyle, $fetchArgument)[0];
233
        }
234
    }
235
236
    /**
237
     * Returns a single column from the next row of a result set or FALSE if there are no more rows.
238
     *
239
     * @param int|null $columnNumber
240
     *
241
     * 0-indexed number of the column you wish to retrieve from the row.
242
     * If no value is supplied, PDOStatement::fetchColumn() fetches the first column.
243
     *
244
     * @return bool|mixed
245
     */
246
    public function fetchColumn($columnNumber = null)
247
    {
248
        $columnNumber = is_null($columnNumber) ? 0 : $columnNumber;
249
        $this->__executeWhenStringQueryEmpty();
250
251
        return $this->fetch(PDO::FETCH_COLUMN, PDO::FETCH_ORI_NEXT, 0, $columnNumber);
252
    }
253
254
    public function fetchAll($fetchStyle = null, $fetchArgument = null, $ctorArgs = null)
255
    {
256
        $this->__executeWhenStringQueryEmpty();
257
        $resultSet = $this->transStyle($this->resultSet, $fetchStyle, $fetchArgument, $ctorArgs);
258
        $this->resultSet = [];
259
260
        return $resultSet;
261
    }
262
}
263