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