Failed Conditions
Pull Request — 3.0.x (#3980)
by Guilherme
06:55
created

DB2Statement   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 312
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 99
dl 0
loc 312
ccs 0
cts 167
cp 0
rs 9.0399
c 0
b 0
f 0
wmc 42

18 Methods

Rating   Name   Duplication   Size   Complexity  
A errorCode() 0 3 1
A errorInfo() 0 5 1
A __construct() 0 3 1
A bindValue() 0 3 1
B execute() 0 37 7
A columnCount() 0 9 2
A bind() 0 6 2
A setFetchMode() 0 5 1
A fetchColumn() 0 9 2
A bindParam() 0 29 4
A rowCount() 0 3 1
A closeCursor() 0 11 2
A writeStringToStream() 0 4 2
A createTemporaryFile() 0 9 2
A getIterator() 0 3 1
A fetch() 0 24 6
A fetchAll() 0 17 4
A copyStreamToStream() 0 4 2

How to fix   Complexity   

Complex Class

Complex classes like DB2Statement often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DB2Statement, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Doctrine\DBAL\Driver\IBMDB2;
4
5
use Doctrine\DBAL\Driver\Statement;
6
use Doctrine\DBAL\Driver\StatementIterator;
7
use Doctrine\DBAL\FetchMode;
8
use Doctrine\DBAL\ParameterType;
9
use IteratorAggregate;
10
use const DB2_BINARY;
11
use const DB2_CHAR;
12
use const DB2_LONG;
13
use const DB2_PARAM_FILE;
14
use const DB2_PARAM_IN;
15
use function assert;
16
use function db2_bind_param;
17
use function db2_execute;
18
use function db2_fetch_array;
19
use function db2_fetch_assoc;
20
use function db2_fetch_both;
21
use function db2_free_result;
22
use function db2_num_fields;
23
use function db2_num_rows;
24
use function db2_stmt_error;
25
use function db2_stmt_errormsg;
26
use function error_get_last;
27
use function fclose;
28
use function fwrite;
29
use function is_int;
30
use function is_resource;
31
use function ksort;
32
use function stream_copy_to_stream;
33
use function stream_get_meta_data;
34
use function tmpfile;
35
36
class DB2Statement implements IteratorAggregate, Statement
37
{
38
    /** @var resource */
39
    private $stmt;
40
41
    /** @var mixed[] */
42
    private $bindParam = [];
43
44
    /**
45
     * Map of LOB parameter positions to the tuples containing reference to the variable bound to the driver statement
46
     * and the temporary file handle bound to the underlying statement
47
     *
48
     * @var mixed[][]
49
     */
50
    private $lobs = [];
51
52
    /** @var int */
53
    private $defaultFetchMode = FetchMode::MIXED;
54
55
    /**
56
     * Indicates whether the statement is in the state when fetching results is possible
57
     *
58
     * @var bool
59
     */
60
    private $result = false;
61
62
    /**
63
     * @param resource $stmt
64
     */
65
    public function __construct($stmt)
66
    {
67
        $this->stmt = $stmt;
68
    }
69
70
    /**
71
     * {@inheritdoc}
72
     */
73
    public function bindValue($param, $value, $type = ParameterType::STRING)
74
    {
75
        return $this->bindParam($param, $value, $type);
76
    }
77
78
    /**
79
     * {@inheritdoc}
80
     */
81
    public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null)
82
    {
83
        assert(is_int($column));
84
85
        switch ($type) {
86
            case ParameterType::INTEGER:
87
                $this->bind($column, $variable, DB2_PARAM_IN, DB2_LONG);
88
                break;
89
90
            case ParameterType::LARGE_OBJECT:
91
                if (isset($this->lobs[$column])) {
92
                    [, $handle] = $this->lobs[$column];
93
                    fclose($handle);
94
                }
95
96
                $handle = $this->createTemporaryFile();
97
                $path   = stream_get_meta_data($handle)['uri'];
98
99
                $this->bind($column, $path, DB2_PARAM_FILE, DB2_BINARY);
100
101
                $this->lobs[$column] = [&$variable, $handle];
102
                break;
103
104
            default:
105
                $this->bind($column, $variable, DB2_PARAM_IN, DB2_CHAR);
106
                break;
107
        }
108
109
        return true;
110
    }
111
112
    /**
113
     * @param int   $position Parameter position
114
     * @param mixed $variable
115
     *
116
     * @throws DB2Exception
117
     */
118
    private function bind($position, &$variable, int $parameterType, int $dataType) : void
119
    {
120
        $this->bindParam[$position] =& $variable;
121
122
        if (! db2_bind_param($this->stmt, $position, 'variable', $parameterType, $dataType)) {
123
            throw new DB2Exception(db2_stmt_errormsg());
124
        }
125
    }
126
127
    /**
128
     * {@inheritdoc}
129
     */
130
    public function closeCursor()
131
    {
132
        $this->bindParam = [];
133
134
        if (! db2_free_result($this->stmt)) {
135
            return false;
136
        }
137
138
        $this->result = false;
139
140
        return true;
141
    }
142
143
    /**
144
     * {@inheritdoc}
145
     */
146
    public function columnCount()
147
    {
148
        $count = db2_num_fields($this->stmt);
149
150
        if ($count !== false) {
151
            return $count;
152
        }
153
154
        return 0;
155
    }
156
157
    /**
158
     * {@inheritdoc}
159
     */
160
    public function errorCode()
161
    {
162
        return db2_stmt_error();
163
    }
164
165
    /**
166
     * {@inheritdoc}
167
     */
168
    public function errorInfo()
169
    {
170
        return [
171
            db2_stmt_errormsg(),
172
            db2_stmt_error(),
173
        ];
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179
    public function execute($params = null)
180
    {
181
        if ($params === null) {
182
            ksort($this->bindParam);
183
184
            $params = [];
185
186
            foreach ($this->bindParam as $column => $value) {
187
                $params[] = $value;
188
            }
189
        }
190
191
        foreach ($this->lobs as [$source, $target]) {
192
            if (is_resource($source)) {
193
                $this->copyStreamToStream($source, $target);
194
195
                continue;
196
            }
197
198
            $this->writeStringToStream($source, $target);
199
        }
200
201
        $retval = db2_execute($this->stmt, $params);
202
203
        foreach ($this->lobs as [, $handle]) {
204
            fclose($handle);
205
        }
206
207
        $this->lobs = [];
208
209
        if ($retval === false) {
210
            throw new DB2Exception(db2_stmt_errormsg());
211
        }
212
213
        $this->result = true;
214
215
        return $retval;
216
    }
217
218
    /**
219
     * {@inheritdoc}
220
     */
221
    public function setFetchMode($fetchMode)
222
    {
223
        $this->defaultFetchMode = $fetchMode;
224
225
        return true;
226
    }
227
228
    /**
229
     * {@inheritdoc}
230
     */
231
    public function getIterator()
232
    {
233
        return new StatementIterator($this);
234
    }
235
236
    /**
237
     * {@inheritdoc}
238
     */
239
    public function fetch($fetchMode = null)
240
    {
241
        // do not try fetching from the statement if it's not expected to contain result
242
        // in order to prevent exceptional situation
243
        if (! $this->result) {
244
            return false;
245
        }
246
247
        $fetchMode = $fetchMode ?? $this->defaultFetchMode;
248
        switch ($fetchMode) {
249
            case FetchMode::COLUMN:
250
                return $this->fetchColumn();
251
252
            case FetchMode::MIXED:
253
                return db2_fetch_both($this->stmt);
254
255
            case FetchMode::ASSOCIATIVE:
256
                return db2_fetch_assoc($this->stmt);
257
258
            case FetchMode::NUMERIC:
259
                return db2_fetch_array($this->stmt);
260
261
            default:
262
                throw new DB2Exception('Given Fetch-Style ' . $fetchMode . ' is not supported.');
263
        }
264
    }
265
266
    /**
267
     * {@inheritdoc}
268
     */
269
    public function fetchAll($fetchMode = null)
270
    {
271
        $rows = [];
272
273
        switch ($fetchMode) {
274
            case FetchMode::COLUMN:
275
                while (($row = $this->fetchColumn()) !== false) {
276
                    $rows[] = $row;
277
                }
278
                break;
279
            default:
280
                while (($row = $this->fetch($fetchMode)) !== false) {
281
                    $rows[] = $row;
282
                }
283
        }
284
285
        return $rows;
286
    }
287
288
    /**
289
     * {@inheritdoc}
290
     */
291
    public function fetchColumn()
292
    {
293
        $row = $this->fetch(FetchMode::NUMERIC);
294
295
        if ($row === false) {
296
            return false;
297
        }
298
299
        return $row[0] ?? null;
300
    }
301
302
    /**
303
     * {@inheritdoc}
304
     */
305
    public function rowCount() : int
306
    {
307
        return @db2_num_rows($this->stmt);
308
    }
309
310
    /**
311
     * @return resource
312
     *
313
     * @throws DB2Exception
314
     */
315
    private function createTemporaryFile()
316
    {
317
        $handle = @tmpfile();
318
319
        if ($handle === false) {
320
            throw new DB2Exception('Could not create temporary file: ' . error_get_last()['message']);
321
        }
322
323
        return $handle;
324
    }
325
326
    /**
327
     * @param resource $source
328
     * @param resource $target
329
     *
330
     * @throws DB2Exception
331
     */
332
    private function copyStreamToStream($source, $target) : void
333
    {
334
        if (@stream_copy_to_stream($source, $target) === false) {
335
            throw new DB2Exception('Could not copy source stream to temporary file: ' . error_get_last()['message']);
336
        }
337
    }
338
339
    /**
340
     * @param resource $target
341
     *
342
     * @throws DB2Exception
343
     */
344
    private function writeStringToStream(string $string, $target) : void
345
    {
346
        if (@fwrite($target, $string) === false) {
347
            throw new DB2Exception('Could not write string to temporary file: ' . error_get_last()['message']);
348
        }
349
    }
350
}
351