Passed
Pull Request — 4.4 (#9098)
by Sam
07:26
created

MySQLStatement::rewind()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\ORM\Connect;
4
5
use mysqli_result;
6
use mysqli_stmt;
7
8
/**
9
 * Provides a record-view for mysqli prepared statements
10
 *
11
 * By default streams unbuffered data, but seek(), rewind(), or numRecords() will force the statement to
12
 * buffer itself and sacrifice any potential performance benefit.
13
 */
14
class MySQLStatement extends Query
15
{
16
17
    /**
18
     * The related mysqli statement object if generated using a prepared query
19
     *
20
     * @var mysqli_stmt
21
     */
22
    protected $statement;
23
24
    /**
25
     * Metadata result for this statement
26
     *
27
     * @var mysqli_result
28
     */
29
    protected $metadata;
30
31
    /**
32
     * Is the statement bound to the current resultset?
33
     *
34
     * @var bool
35
     */
36
    protected $bound = false;
37
38
    /**
39
     * List of column names
40
     *
41
     * @var array
42
     */
43
    protected $columns = array();
44
45
    /**
46
     * Map of column types, keyed by column name
47
     *
48
     * @var array
49
     */
50
    protected $types = array();
51
52
    /**
53
     * List of bound variables in the current row
54
     *
55
     * @var array
56
     */
57
    protected $boundValues = array();
58
59
    /**
60
     * Binds this statement to the variables
61
     */
62
    protected function bind()
63
    {
64
        $variables = array();
65
66
        // Bind each field
67
        while ($field = $this->metadata->fetch_field()) {
68
            $this->columns[] = $field->name;
69
            $this->types[$field->name] = $field->type;
70
            // Note that while boundValues isn't initialised at this point,
71
            // later calls to $this->statement->fetch() Will populate
72
            // $this->boundValues later with the next result.
73
            $variables[] = &$this->boundValues[$field->name];
74
        }
75
76
        $this->bound = true;
77
        $this->metadata->free();
78
79
        // Buffer all results
80
        $this->statement->store_result();
81
82
        call_user_func_array(array($this->statement, 'bind_result'), $variables);
83
    }
84
85
    /**
86
     * Hook the result-set given into a Query class, suitable for use by SilverStripe.
87
     * @param mysqli_stmt $statement The related statement, if present
88
     * @param mysqli_result $metadata The metadata for this statement
89
     */
90
    public function __construct($statement, $metadata)
91
    {
92
        $this->statement = $statement;
93
        $this->metadata = $metadata;
94
95
        // Immediately bind and buffer
96
        $this->bind();
97
    }
98
99
    public function __destruct()
100
    {
101
        $this->statement->close();
102
        $this->currentRecord = false;
0 ignored issues
show
Documentation Bug introduced by
It seems like false of type false is incompatible with the declared type array of property $currentRecord.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
103
    }
104
105
    public function seek($row)
106
    {
107
        $this->rowNum = $row - 1;
108
109
        // Fix for https://github.com/silverstripe/silverstripe-framework/issues/9097 without breaking the seek() API
110
        $this->statement->data_seek($row);
111
        $result = $this->next();
112
        $this->statement->data_seek($row);
113
        return $result;
114
    }
115
116
    public function numRecords()
117
    {
118
        return $this->statement->num_rows();
0 ignored issues
show
Bug introduced by
The call to mysqli_stmt::num_rows() has too few arguments starting with stmt. ( Ignorable by Annotation )

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

118
        return $this->statement->/** @scrutinizer ignore-call */ num_rows();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
119
    }
120
121
    public function nextRecord()
122
    {
123
        // Skip data if out of data
124
        if (!$this->statement->fetch()) {
125
            return false;
126
        }
127
128
        // Dereferenced row
129
        $row = array();
130
        foreach ($this->boundValues as $key => $value) {
131
            $floatTypes = [MYSQLI_TYPE_FLOAT, MYSQLI_TYPE_DOUBLE, MYSQLI_TYPE_DECIMAL, MYSQLI_TYPE_NEWDECIMAL];
132
            if (in_array($this->types[$key], $floatTypes)) {
133
                $value = (float)$value;
134
            }
135
            $row[$key] = $value;
136
        }
137
        return $row;
138
    }
139
}
140