1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Database;
9:
10: use Nette,
11: PDO;
12:
13:
14: 15: 16: 17: 18: 19: 20: 21:
22: class ResultSet extends Nette\Object implements \Iterator, IRowContainer
23: {
24:
25: private $connection;
26:
27:
28: private $supplementalDriver;
29:
30:
31: private $pdoStatement;
32:
33:
34: private $result;
35:
36:
37: private $resultKey = -1;
38:
39:
40: private $results;
41:
42:
43: private $time;
44:
45:
46: private $queryString;
47:
48:
49: private $params;
50:
51:
52: private $types;
53:
54:
55: public function __construct(Connection $connection, $queryString, array $params)
56: {
57: $time = microtime(TRUE);
58: $this->connection = $connection;
59: $this->supplementalDriver = $connection->getSupplementalDriver();
60: $this->queryString = $queryString;
61: $this->params = $params;
62:
63: try {
64: if (substr($queryString, 0, 2) === '::') {
65: $connection->getPdo()->{substr($queryString, 2)}();
66: } elseif ($queryString !== NULL) {
67: $this->pdoStatement = $connection->getPdo()->prepare($queryString);
68: $this->pdoStatement->setFetchMode(PDO::FETCH_ASSOC);
69: $this->pdoStatement->execute($params);
70: }
71: } catch (\PDOException $e) {
72: $e = $this->supplementalDriver->convertException($e);
73: $e->queryString = $queryString;
74: throw $e;
75: }
76: $this->time = microtime(TRUE) - $time;
77: }
78:
79:
80: 81: 82:
83: public function getConnection()
84: {
85: return $this->connection;
86: }
87:
88:
89: 90: 91: 92:
93: public function getPdoStatement()
94: {
95: return $this->pdoStatement;
96: }
97:
98:
99: 100: 101:
102: public function getQueryString()
103: {
104: return $this->queryString;
105: }
106:
107:
108: 109: 110:
111: public function getParameters()
112: {
113: return $this->params;
114: }
115:
116:
117: 118: 119:
120: public function getColumnCount()
121: {
122: return $this->pdoStatement ? $this->pdoStatement->columnCount() : NULL;
123: }
124:
125:
126: 127: 128:
129: public function getRowCount()
130: {
131: return $this->pdoStatement ? $this->pdoStatement->rowCount() : NULL;
132: }
133:
134:
135: 136: 137:
138: public function getTime()
139: {
140: return $this->time;
141: }
142:
143:
144: 145: 146: 147: 148:
149: public function normalizeRow($row)
150: {
151: if ($this->types === NULL) {
152: $this->types = (array) $this->supplementalDriver->getColumnTypes($this->pdoStatement);
153: }
154:
155: foreach ($this->types as $key => $type) {
156: $value = $row[$key];
157: if ($value === NULL || $value === FALSE || $type === IStructure::FIELD_TEXT) {
158:
159: } elseif ($type === IStructure::FIELD_INTEGER) {
160: $row[$key] = is_float($tmp = $value * 1) ? $value : $tmp;
161:
162: } elseif ($type === IStructure::FIELD_FLOAT) {
163: if (($pos = strpos($value, '.')) !== FALSE) {
164: $value = rtrim(rtrim($pos === 0 ? "0$value" : $value, '0'), '.');
165: }
166: $float = (float) $value;
167: $row[$key] = (string) $float === $value ? $float : $value;
168:
169: } elseif ($type === IStructure::FIELD_BOOL) {
170: $row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F';
171:
172: } elseif ($type === IStructure::FIELD_DATETIME || $type === IStructure::FIELD_DATE || $type === IStructure::FIELD_TIME) {
173: $row[$key] = new Nette\Utils\DateTime($value);
174:
175: } elseif ($type === IStructure::FIELD_TIME_INTERVAL) {
176: preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)\z#', $value, $m);
177: $row[$key] = new \DateInterval("PT$m[2]H$m[3]M$m[4]S");
178: $row[$key]->invert = (int) (bool) $m[1];
179:
180: } elseif ($type === IStructure::FIELD_UNIX_TIMESTAMP) {
181: $row[$key] = Nette\Utils\DateTime::from($value);
182: }
183: }
184:
185: return $this->supplementalDriver->normalizeRow($row);
186: }
187:
188:
189:
190:
191:
192: 193: 194: 195:
196: public function dump()
197: {
198: Helpers::dumpResult($this);
199: }
200:
201:
202:
203:
204:
205: public function rewind()
206: {
207: if ($this->result === FALSE) {
208: throw new Nette\InvalidStateException('Nette\\Database\\ResultSet implements only one way iterator.');
209: }
210: }
211:
212:
213: public function current()
214: {
215: return $this->result;
216: }
217:
218:
219: public function key()
220: {
221: return $this->resultKey;
222: }
223:
224:
225: public function next()
226: {
227: $this->result = FALSE;
228: }
229:
230:
231: public function valid()
232: {
233: if ($this->result) {
234: return TRUE;
235: }
236:
237: return $this->fetch() !== FALSE;
238: }
239:
240:
241:
242:
243:
244: 245: 246:
247: public function fetch()
248: {
249: $data = $this->pdoStatement ? $this->pdoStatement->fetch() : NULL;
250: if (!$data) {
251: $this->pdoStatement->closeCursor();
252: return FALSE;
253: }
254:
255: $row = new Row;
256: foreach ($this->normalizeRow($data) as $key => $value) {
257: if ($key !== '') {
258: $row->$key = $value;
259: }
260: }
261:
262: if ($this->result === NULL && count($data) !== $this->pdoStatement->columnCount()) {
263: trigger_error('Found duplicate columns in database result set.', E_USER_NOTICE);
264: }
265:
266: $this->resultKey++;
267: return $this->result = $row;
268: }
269:
270:
271: 272: 273: 274: 275:
276: public function fetchField($column = 0)
277: {
278: $row = $this->fetch();
279: return $row ? $row[$column] : FALSE;
280: }
281:
282:
283: 284: 285:
286: public function fetchPairs($key = NULL, $value = NULL)
287: {
288: return Helpers::toPairs($this->fetchAll(), $key, $value);
289: }
290:
291:
292: 293: 294:
295: public function fetchAll()
296: {
297: if ($this->results === NULL) {
298: $this->results = iterator_to_array($this);
299: }
300: return $this->results;
301: }
302:
303:
304: 305: 306:
307: public function fetchAssoc($path)
308: {
309: return Nette\Utils\Arrays::associate($this->fetchAll(), $path);
310: }
311:
312: }
313: