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