|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* Result wrapper for grabbing data queried from an IDatabase object |
|
4
|
|
|
* |
|
5
|
|
|
* Note that using the Iterator methods in combination with the non-Iterator |
|
6
|
|
|
* DB result iteration functions may cause rows to be skipped or repeated. |
|
7
|
|
|
* |
|
8
|
|
|
* By default, this will use the iteration methods of the IDatabase handle if provided. |
|
9
|
|
|
* Subclasses can override methods to make it solely work on the result resource instead. |
|
10
|
|
|
* If no database is provided, and the subclass does not override the DB iteration methods, |
|
11
|
|
|
* then a RuntimeException will be thrown when iteration is attempted. |
|
12
|
|
|
* |
|
13
|
|
|
* The result resource field should not be accessed from non-Database related classes. |
|
14
|
|
|
* It is database class specific and is stored here to associate iterators with queries. |
|
15
|
|
|
* |
|
16
|
|
|
* @ingroup Database |
|
17
|
|
|
*/ |
|
18
|
|
|
class ResultWrapper implements Iterator { |
|
19
|
|
|
/** @var resource|array|null Optional underlying result handle for subclass usage */ |
|
20
|
|
|
public $result; |
|
21
|
|
|
|
|
22
|
|
|
/** @var IDatabase|null */ |
|
23
|
|
|
protected $db; |
|
24
|
|
|
|
|
25
|
|
|
/** @var int */ |
|
26
|
|
|
protected $pos = 0; |
|
27
|
|
|
/** @var stdClass|null */ |
|
28
|
|
|
protected $currentRow = null; |
|
29
|
|
|
|
|
30
|
|
|
/** |
|
31
|
|
|
* Create a row iterator from a result resource and an optional Database object |
|
32
|
|
|
* |
|
33
|
|
|
* Only Database-related classes should construct ResultWrapper. Other code may |
|
34
|
|
|
* use the FakeResultWrapper subclass for convenience or compatibility shims, however. |
|
35
|
|
|
* |
|
36
|
|
|
* @param IDatabase|null $db Optional database handle |
|
37
|
|
|
* @param ResultWrapper|array|resource $result Optional underlying result handle |
|
38
|
|
|
*/ |
|
39
|
|
|
public function __construct( IDatabase $db = null, $result ) { |
|
40
|
|
|
$this->db = $db; |
|
41
|
|
|
if ( $result instanceof ResultWrapper ) { |
|
42
|
|
|
$this->result = $result->result; |
|
43
|
|
|
} else { |
|
44
|
|
|
$this->result = $result; |
|
45
|
|
|
} |
|
46
|
|
|
} |
|
47
|
|
|
|
|
48
|
|
|
/** |
|
49
|
|
|
* Get the number of rows in a result object |
|
50
|
|
|
* |
|
51
|
|
|
* @return int |
|
52
|
|
|
*/ |
|
53
|
|
|
public function numRows() { |
|
54
|
|
|
return $this->getDB()->numRows( $this ); |
|
55
|
|
|
} |
|
56
|
|
|
|
|
57
|
|
|
/** |
|
58
|
|
|
* Fetch the next row from the given result object, in object form. Fields can be retrieved with |
|
59
|
|
|
* $row->fieldname, with fields acting like member variables. If no more rows are available, |
|
60
|
|
|
* false is returned. |
|
61
|
|
|
* |
|
62
|
|
|
* @return stdClass|bool |
|
63
|
|
|
* @throws DBUnexpectedError Thrown if the database returns an error |
|
64
|
|
|
*/ |
|
65
|
|
|
public function fetchObject() { |
|
66
|
|
|
return $this->getDB()->fetchObject( $this ); |
|
67
|
|
|
} |
|
68
|
|
|
|
|
69
|
|
|
/** |
|
70
|
|
|
* Fetch the next row from the given result object, in associative array form. Fields are |
|
71
|
|
|
* retrieved with $row['fieldname']. If no more rows are available, false is returned. |
|
72
|
|
|
* |
|
73
|
|
|
* @return array|bool |
|
74
|
|
|
* @throws DBUnexpectedError Thrown if the database returns an error |
|
75
|
|
|
*/ |
|
76
|
|
|
public function fetchRow() { |
|
77
|
|
|
return $this->getDB()->fetchRow( $this ); |
|
78
|
|
|
} |
|
79
|
|
|
|
|
80
|
|
|
/** |
|
81
|
|
|
* Change the position of the cursor in a result object. |
|
82
|
|
|
* See mysql_data_seek() |
|
83
|
|
|
* |
|
84
|
|
|
* @param int $row |
|
85
|
|
|
*/ |
|
86
|
|
|
public function seek( $row ) { |
|
87
|
|
|
$this->getDB()->dataSeek( $this, $row ); |
|
88
|
|
|
} |
|
89
|
|
|
|
|
90
|
|
|
/** |
|
91
|
|
|
* Free a result object |
|
92
|
|
|
* |
|
93
|
|
|
* This either saves memory in PHP (buffered queries) or on the server (unbuffered queries). |
|
94
|
|
|
* In general, queries are not large enough in result sets for this to be worth calling. |
|
95
|
|
|
*/ |
|
96
|
|
|
public function free() { |
|
97
|
|
|
if ( $this->db ) { |
|
98
|
|
|
$this->db->freeResult( $this ); |
|
99
|
|
|
$this->db = null; |
|
100
|
|
|
} |
|
101
|
|
|
$this->result = null; |
|
102
|
|
|
} |
|
103
|
|
|
|
|
104
|
|
|
/** |
|
105
|
|
|
* @return IDatabase |
|
106
|
|
|
* @throws RuntimeException |
|
107
|
|
|
*/ |
|
108
|
|
|
private function getDB() { |
|
109
|
|
|
if ( !$this->db ) { |
|
110
|
|
|
throw new RuntimeException( get_class( $this ) . ' needs a DB handle for iteration.' ); |
|
111
|
|
|
} |
|
112
|
|
|
|
|
113
|
|
|
return $this->db; |
|
114
|
|
|
} |
|
115
|
|
|
|
|
116
|
|
|
function rewind() { |
|
117
|
|
|
if ( $this->numRows() ) { |
|
118
|
|
|
$this->getDB()->dataSeek( $this, 0 ); |
|
119
|
|
|
} |
|
120
|
|
|
$this->pos = 0; |
|
121
|
|
|
$this->currentRow = null; |
|
122
|
|
|
} |
|
123
|
|
|
|
|
124
|
|
|
/** |
|
125
|
|
|
* @return stdClass|array|bool |
|
126
|
|
|
*/ |
|
127
|
|
|
function current() { |
|
128
|
|
|
if ( is_null( $this->currentRow ) ) { |
|
129
|
|
|
$this->next(); |
|
130
|
|
|
} |
|
131
|
|
|
|
|
132
|
|
|
return $this->currentRow; |
|
133
|
|
|
} |
|
134
|
|
|
|
|
135
|
|
|
/** |
|
136
|
|
|
* @return int |
|
137
|
|
|
*/ |
|
138
|
|
|
function key() { |
|
139
|
|
|
return $this->pos; |
|
140
|
|
|
} |
|
141
|
|
|
|
|
142
|
|
|
/** |
|
143
|
|
|
* @return stdClass |
|
144
|
|
|
*/ |
|
145
|
|
|
function next() { |
|
146
|
|
|
$this->pos++; |
|
147
|
|
|
$this->currentRow = $this->fetchObject(); |
|
|
|
|
|
|
148
|
|
|
|
|
149
|
|
|
return $this->currentRow; |
|
150
|
|
|
} |
|
151
|
|
|
|
|
152
|
|
|
function valid() { |
|
153
|
|
|
return $this->current() !== false; |
|
154
|
|
|
} |
|
155
|
|
|
} |
|
156
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountIdthat can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theidproperty of an instance of theAccountclass. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.