forked from Samuel-BF/atk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMySqliStatement.php
232 lines (199 loc) · 5.9 KB
/
MySqliStatement.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
<?php namespace Sintattica\Atk\Db\Statement;
use Sintattica\Atk\Core\Tools;
use Sintattica\Atk\Utils\Debugger;
use Sintattica\Atk\Core\Config;
use Sintattica\Atk\Db\Db;
/**
* MySQLi statement implementation.
*
* @author Peter C. Verhage <[email protected]>
*
* @package atk
* @subpackage db.statement
*/
class MySQLiStatement extends Statement
{
/**
* MySQLi statement.
*
* @var MySQLiStatement
*/
private $m_stmt;
/**
* Column names.
*
* @var array
*/
private $m_columnNames = null;
/**
* Row value bindings.
*
* @var array
*/
private $m_values = null;
/**
* Prepares the statement for execution.
*/
protected function _prepare()
{
if ($this->getDb()->connect() != Db::DB_SUCCESS) {
throw new StatementException("Cannot connect to database.", StatementException::NO_DATABASE_CONNECTION);
}
$conn = $this->getDb()->link_id();
Tools::atkdebug("Prepare query: " . $this->_getParsedQuery());
$this->m_stmt = $conn->prepare($this->_getParsedQuery());
if (!$this->m_stmt || $conn->errno) {
throw new StatementException("Cannot prepare statement (ERROR: {$conn->errno} - {$conn->error}).",
StatementException::PREPARE_STATEMENT_ERROR);
}
}
/**
* Moves the cursor back to the beginning of the result set.
*
* NOTE:
* Depending on the database driver, using this method might result in the
* query to be executed again.
*/
public function rewind()
{
if ($this->_getLatestParams() === null) {
throw new StatementException("Statement has not been executed yet.",
StatementException::STATEMENT_NOT_EXECUTED);
}
$this->m_stmt->data_seek(0);
}
/**
* Bind statement parameters.
*
* @param array $params parameters
*/
private function _bindParams($params)
{
if (count($params) == 0) {
return;
}
$i = 0;
$args = array();
$args[] = str_repeat('s', count($this->_getBindPositions()));
foreach ($this->_getBindPositions() as $param) {
Tools::atkdebug("Bind param {$i}: " . ($params[$param] === null ? 'NULL' : $params[$param]));
$args[] = &$params[$param];
$i++;
}
call_user_func_array(array($this->m_stmt, 'bind_param'), $args);
}
/**
* Store the column names from this statement's metadata.
*/
private function _storeColumnNames()
{
if ($this->m_columnNames !== null) {
// already stored on previous execution
return;
}
$metadata = $this->m_stmt->result_metadata();
if ($this->m_stmt->errno) {
throw new StatementException("Cannot retrieve metadata (ERROR: {$this->m_stmt->errno} - {$this->m_stmt->error}).",
StatementException::OTHER_ERROR);
}
if (!$metadata) {
// no result set (INSERT, UPDATE, DELETE, ... queries)
return;
}
$this->m_columnNames = array();
foreach ($metadata->fetch_fields() as $column) {
$this->m_columnNames[] = $column->name;
}
}
/**
* Bind result columns to values array so we can read the result when
* fetching rows.
*/
private function _bindResult()
{
$this->_storeColumnNames();
if ($this->m_columnNames === null) {
// no result set (INSERT, UPDATE, DELETE, ... queries)
return;
}
$this->m_values = array();
$refs = array();
for ($i = 0; $i < count($this->m_columnNames); $i++) {
$this->m_values[$i] = null;
$refs[$i] = &$this->m_values[$i];
}
$this->m_stmt->store_result();
call_user_func_array(array($this->m_stmt, 'bind_result'), $refs);
}
/**
* Executes the statement using the given bind parameters.
*
* @param array $params bind parameters
*/
protected function _execute($params)
{
if (Config::getGlobal("debug") >= 0) {
Debugger::addQuery($this->_getParsedQuery(), false);
}
$this->_bindParams($params);
if (!$this->m_stmt->execute()) {
throw new StatementException("Cannot execute statement: {$this->m_stmt->error}",
StatementException::STATEMENT_ERROR);
}
$this->m_insertId = $this->getDb()->link_id()->insert_id;
$this->_bindResult();
if ($this->m_columnNames === null) {
$this->getDb()->debugWarnings();
}
}
/**
* Fetches the next row from the result set.
*
* @return array next row from the result set (false if no other rows exist)
*/
protected function _fetch()
{
if (!$this->m_stmt->fetch()) {
return false;
}
$values = array();
foreach ($this->m_values as $value) {
$values[] = $value;
}
$row = array_combine($this->m_columnNames, $values);
return $row;
}
/**
* Resets the statement so that it can be re-used again.
*/
protected function _reset()
{
@$this->m_stmt->free_result();
@$this->m_stmt->reset();
}
/**
* Frees up all resources for this statement. The statement cannot be
* re-used anymore.
*/
public function _close()
{
@$this->m_stmt->close();
}
/**
* Returns the number of affected rows in case of an INSERT, UPDATE
* or DELETE query. Called immediatly after atkStatement::_execute().
*/
protected function _getAffectedRowCount()
{
return $this->m_stmt->affected_rows;
}
/**
* Returns the auto-generated id used in the last query.
*
* @return int auto-generated id
*/
public function getInsertId()
{
return $this->m_insertId;
}
}