1: <?php
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: class DibiResult extends DibiObject implements IDataSource
41: {
42:
43: private $driver;
44:
45:
46: private $types = array();
47:
48:
49: private $meta;
50:
51:
52: private $fetched = FALSE;
53:
54:
55: private $rowClass = 'DibiRow';
56:
57:
58: private $dateFormat = '';
59:
60:
61:
62: 63: 64: 65:
66: public function __construct($driver, $config)
67: {
68: $this->driver = $driver;
69: $this->detectTypes();
70:
71: if (!empty($config['formatDateTime'])) {
72: $this->dateFormat = is_string($config['formatDateTime']) ? $config['formatDateTime'] : '';
73: }
74: }
75:
76:
77:
78: 79: 80: 81:
82: final public function getResource()
83: {
84: return $this->getDriver()->getResultResource();
85: }
86:
87:
88:
89: 90: 91: 92:
93: final public function free()
94: {
95: if ($this->driver !== NULL) {
96: $this->driver->free();
97: $this->driver = $this->meta = NULL;
98: }
99: }
100:
101:
102:
103: 104: 105: 106: 107:
108: private function getDriver()
109: {
110: if ($this->driver === NULL) {
111: throw new RuntimeException('Result-set was released from memory.');
112: }
113:
114: return $this->driver;
115: }
116:
117:
118:
119:
120:
121:
122:
123: 124: 125: 126: 127: 128:
129: final public function seek($row)
130: {
131: return ($row !== 0 || $this->fetched) ? (bool) $this->getDriver()->seek($row) : TRUE;
132: }
133:
134:
135:
136: 137: 138: 139:
140: final public function count()
141: {
142: return $this->getDriver()->getRowCount();
143: }
144:
145:
146:
147: 148: 149: 150:
151: final public function getRowCount()
152: {
153: return $this->getDriver()->getRowCount();
154: }
155:
156:
157:
158: 159: 160: 161:
162: final public function rowCount()
163: {
164: trigger_error(__METHOD__ . '() is deprecated; use count($res) or $res->getRowCount() instead.', E_USER_WARNING);
165: return $this->getDriver()->getRowCount();
166: }
167:
168:
169:
170: 171: 172: 173:
174: final public function getIterator()
175: {
176: if (func_num_args()) {
177: trigger_error(__METHOD__ . ' arguments $offset & $limit have been dropped; use SQL clauses instead.', E_USER_WARNING);
178: }
179: return new DibiResultIterator($this);
180: }
181:
182:
183:
184:
185:
186:
187:
188: 189: 190: 191: 192:
193: public function setRowClass($class)
194: {
195: $this->rowClass = $class;
196: return $this;
197: }
198:
199:
200:
201: 202: 203: 204:
205: public function getRowClass()
206: {
207: return $this->rowClass;
208: }
209:
210:
211:
212: 213: 214: 215: 216:
217: final public function fetch()
218: {
219: $row = $this->getDriver()->fetch(TRUE);
220: if (!is_array($row)) {
221: return FALSE;
222: }
223: $this->fetched = TRUE;
224: $this->normalize($row);
225: return new $this->rowClass($row);
226: }
227:
228:
229:
230: 231: 232: 233:
234: final public function fetchSingle()
235: {
236: $row = $this->getDriver()->fetch(TRUE);
237: if (!is_array($row)) {
238: return FALSE;
239: }
240: $this->fetched = TRUE;
241: $this->normalize($row);
242: return reset($row);
243: }
244:
245:
246:
247: 248: 249: 250: 251: 252:
253: final public function fetchAll($offset = NULL, $limit = NULL)
254: {
255: $limit = $limit === NULL ? -1 : (int) $limit;
256: $this->seek((int) $offset);
257: $row = $this->fetch();
258: if (!$row) return array();
259:
260: $data = array();
261: do {
262: if ($limit === 0) break;
263: $limit--;
264: $data[] = $row;
265: } while ($row = $this->fetch());
266:
267: return $data;
268: }
269:
270:
271:
272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282:
283: final public function fetchAssoc($assoc)
284: {
285: if (strpos($assoc, ',') !== FALSE) {
286: return $this->oldFetchAssoc($assoc);
287: }
288:
289: $this->seek(0);
290: $row = $this->fetch();
291: if (!$row) return array();
292:
293: $data = NULL;
294: $assoc = preg_split('#(\[\]|->|=|\|)#', $assoc, NULL, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
295:
296:
297: foreach ($assoc as $as) {
298:
299: if ($as !== '[]' && $as !== '=' && $as !== '->' && $as !== '|' && !property_exists($row, $as)) {
300: throw new InvalidArgumentException("Unknown column '$as' in associative descriptor.");
301: }
302: }
303:
304: if ($as === '->') {
305: array_pop($assoc);
306: }
307:
308: if (empty($assoc)) {
309: $assoc[] = '[]';
310: }
311:
312:
313: do {
314: $x = & $data;
315:
316:
317: foreach ($assoc as $i => $as) {
318: if ($as === '[]') {
319: $x = & $x[];
320:
321: } elseif ($as === '=') {
322: $x = $row->{$assoc[$i+1]};
323: continue 2;
324:
325: } elseif ($as === '->') {
326: if ($x === NULL) {
327: $x = clone $row;
328: $x = & $x->{$assoc[$i+1]};
329: $x = NULL;
330: } else {
331: $x = & $x->{$assoc[$i+1]};
332: }
333:
334: } elseif ($as !== '|') {
335: $x = & $x[$row->$as];
336: }
337: }
338:
339: if ($x === NULL) {
340: $x = $row;
341: }
342:
343: } while ($row = $this->fetch());
344:
345: unset($x);
346: return $data;
347: }
348:
349:
350:
351: 352: 353:
354: private function oldFetchAssoc($assoc)
355: {
356: $this->seek(0);
357: $row = $this->fetch();
358: if (!$row) return array();
359:
360: $data = NULL;
361: $assoc = explode(',', $assoc);
362:
363:
364: $leaf = '@';
365: $last = count($assoc) - 1;
366: while ($assoc[$last] === '=' || $assoc[$last] === '@') {
367: $leaf = $assoc[$last];
368: unset($assoc[$last]);
369: $last--;
370:
371: if ($last < 0) {
372: $assoc[] = '#';
373: break;
374: }
375: }
376:
377: do {
378: $x = & $data;
379:
380: foreach ($assoc as $i => $as) {
381: if ($as === '#') {
382: $x = & $x[];
383:
384: } elseif ($as === '=') {
385: if ($x === NULL) {
386: $x = $row->toArray();
387: $x = & $x[ $assoc[$i+1] ];
388: $x = NULL;
389: } else {
390: $x = & $x[ $assoc[$i+1] ];
391: }
392:
393: } elseif ($as === '@') {
394: if ($x === NULL) {
395: $x = clone $row;
396: $x = & $x->{$assoc[$i+1]};
397: $x = NULL;
398: } else {
399: $x = & $x->{$assoc[$i+1]};
400: }
401:
402:
403: } else {
404: $x = & $x[$row->$as];
405: }
406: }
407:
408: if ($x === NULL) {
409: if ($leaf === '=') {
410: $x = $row->toArray();
411: } else {
412: $x = $row;
413: }
414: }
415:
416: } while ($row = $this->fetch());
417:
418: unset($x);
419: return $data;
420: }
421:
422:
423:
424: 425: 426: 427: 428: 429: 430:
431: final public function fetchPairs($key = NULL, $value = NULL)
432: {
433: $this->seek(0);
434: $row = $this->fetch();
435: if (!$row) return array();
436:
437: $data = array();
438:
439: if ($value === NULL) {
440: if ($key !== NULL) {
441: throw new InvalidArgumentException("Either none or both columns must be specified.");
442: }
443:
444:
445: $tmp = array_keys($row->toArray());
446: $key = $tmp[0];
447: if (count($row) < 2) {
448: do {
449: $data[] = $row[$key];
450: } while ($row = $this->fetch());
451: return $data;
452: }
453:
454: $value = $tmp[1];
455:
456: } else {
457: if (!property_exists($row, $value)) {
458: throw new InvalidArgumentException("Unknown value column '$value'.");
459: }
460:
461: if ($key === NULL) {
462: do {
463: $data[] = $row[$value];
464: } while ($row = $this->fetch());
465: return $data;
466: }
467:
468: if (!property_exists($row, $key)) {
469: throw new InvalidArgumentException("Unknown key column '$key'.");
470: }
471: }
472:
473: do {
474: $data[ $row[$key] ] = $row[$value];
475: } while ($row = $this->fetch());
476:
477: return $data;
478: }
479:
480:
481:
482:
483:
484:
485:
486: 487: 488: 489:
490: private function detectTypes()
491: {
492: $cache = DibiColumnInfo::getTypeCache();
493: try {
494: foreach ($this->getDriver()->getResultColumns() as $col) {
495: $this->types[$col['name']] = $cache->{$col['nativetype']};
496: }
497: } catch (DibiNotSupportedException $e) {}
498: }
499:
500:
501:
502: 503: 504: 505: 506:
507: private function normalize(array & $row)
508: {
509: foreach ($this->types as $key => $type) {
510: if (!isset($row[$key])) {
511: continue;
512: }
513: $value = $row[$key];
514: if ($value === FALSE || $type === dibi::TEXT) {
515:
516: } elseif ($type === dibi::INTEGER) {
517: $row[$key] = is_float($tmp = $value * 1) ? $value : $tmp;
518:
519: } elseif ($type === dibi::FLOAT) {
520: $row[$key] = (string) ($tmp = (float) $value) === $value ? $tmp : $value;
521:
522: } elseif ($type === dibi::BOOL) {
523: $row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F';
524:
525: } elseif ($type === dibi::DATE || $type === dibi::DATETIME) {
526: if ((int) $value === 0) {
527:
528: } elseif ($this->dateFormat === '') {
529: $row[$key] = new DibiDateTime(is_numeric($value) ? date('Y-m-d H:i:s', $value) : $value);
530:
531: } elseif ($this->dateFormat === 'U') {
532: $row[$key] = is_numeric($value) ? (int) $value : strtotime($value);
533:
534: } elseif (is_numeric($value)) {
535: $row[$key] = date($this->dateFormat, $value);
536:
537: } else {
538: $value = new DibiDateTime($value);
539: $row[$key] = $value->format($this->dateFormat);
540: }
541:
542: } elseif ($type === dibi::BINARY) {
543: $row[$key] = $this->getDriver()->unescape($value, $type);
544: }
545: }
546: }
547:
548:
549:
550: 551: 552: 553: 554: 555:
556: final public function setType($col, $type)
557: {
558: $this->types[$col] = $type;
559: return $this;
560: }
561:
562:
563:
564: 565: 566: 567:
568: final public function getType($col)
569: {
570: return isset($this->types[$col]) ? $this->types[$col] : NULL;
571: }
572:
573:
574:
575:
576:
577:
578:
579: 580: 581: 582:
583: public function getInfo()
584: {
585: if ($this->meta === NULL) {
586: $this->meta = new DibiResultInfo($this->getDriver());
587: }
588: return $this->meta;
589: }
590:
591:
592:
593: 594: 595:
596: final public function getColumns()
597: {
598: return $this->getInfo()->getColumns();
599: }
600:
601:
602:
603:
604: public function getColumnNames($fullNames = FALSE)
605: {
606: trigger_error(__METHOD__ . '() is deprecated; use $res->getInfo()->getColumnNames() instead.', E_USER_WARNING);
607: return $this->getInfo()->getColumnNames($fullNames);
608: }
609:
610:
611:
612:
613:
614:
615:
616: 617: 618: 619:
620: final public function dump()
621: {
622: $i = 0;
623: $this->seek(0);
624: while ($row = $this->fetch()) {
625: if ($i === 0) {
626: echo "\n<table class=\"dump\">\n<thead>\n\t<tr>\n\t\t<th>#row</th>\n";
627:
628: foreach ($row as $col => $foo) {
629: echo "\t\t<th>" . htmlSpecialChars($col) . "</th>\n";
630: }
631:
632: echo "\t</tr>\n</thead>\n<tbody>\n";
633: }
634:
635: echo "\t<tr>\n\t\t<th>", $i, "</th>\n";
636: foreach ($row as $col) {
637:
638: echo "\t\t<td>", htmlSpecialChars($col), "</td>\n";
639: }
640: echo "\t</tr>\n";
641: $i++;
642: }
643:
644: if ($i === 0) {
645: echo '<p><em>empty result set</em></p>';
646: } else {
647: echo "</tbody>\n</table>\n";
648: }
649: }
650:
651: }
652: