1
0
mirror of https://github.com/dg/dibi.git synced 2025-02-24 10:53:17 +01:00
php-dibi/dibi/libs/resultset.php

483 lines
11 KiB
PHP
Raw Normal View History

2006-06-04 23:06:33 +00:00
<?php
/**
2007-04-11 18:30:30 +00:00
* This file is part of the "dibi" project (http://dibi.texy.info/)
2006-06-04 23:06:33 +00:00
*
2007-04-11 18:30:30 +00:00
* Copyright (c) 2005-2007 David Grudl aka -dgx- <dave@dgx.cz>
2006-06-04 23:06:33 +00:00
*
2007-04-11 18:30:30 +00:00
* @version $Revision$ $Date$
* @package dibi
2006-06-04 23:06:33 +00:00
*/
// security - include dibi.php, not this file
if (!class_exists('dibi', FALSE)) die();
2006-06-04 23:06:33 +00:00
// PHP < 5.1 compatibility
if (!interface_exists('Countable', false)) {
interface Countable
{
function count();
}
}
/**
* dibi result-set abstract class
*
* <code>
* $result = dibi::query('SELECT * FROM [table]');
* $value = $result->fetchSingle();
* $all = $result->fetchAll();
* $assoc = $result->fetchAssoc('id');
* $assoc = $result->fetchAssoc('active', 'id');
2006-06-04 23:06:33 +00:00
* unset($result);
* </code>
*/
abstract class DibiResult implements IteratorAggregate, Countable
{
/**
* Describes columns types
* @var array
*/
protected $convert;
/**
* Describes columns types
* @var array
*/
protected $meta;
2006-06-04 23:06:33 +00:00
static private $types = array(
dibi::FIELD_TEXT => 'string',
dibi::FIELD_BINARY => 'string',
dibi::FIELD_BOOL => 'bool',
dibi::FIELD_INTEGER => 'int',
dibi::FIELD_FLOAT => 'float',
dibi::FIELD_COUNTER => 'int',
);
2006-06-04 23:06:33 +00:00
/**
* Moves cursor position without fetching row
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
*/
abstract public function seek($row);
2006-06-04 23:06:33 +00:00
/**
* Returns the number of rows in a result set
* @return int
*/
abstract public function rowCount();
/**
* Frees the resources allocated for this result set
* @return void
*/
abstract protected function free();
2006-06-04 23:06:33 +00:00
/**
* Fetches the row at current position and moves the internal cursor to the next position
* internal usage only
* @return array|FALSE array() on success, FALSE if no next record
*/
abstract protected function doFetch();
2006-06-04 23:06:33 +00:00
/**
* Fetches the row at current position, process optional type conversion
* and moves the internal cursor to the next position
* @return array|FALSE array() on success, FALSE if no next record
*/
final public function fetch()
{
$rec = $this->doFetch();
if (!is_array($rec))
return FALSE;
// types-converting?
if ($t = $this->convert) { // little speed-up
foreach ($rec as $key => $value) {
if (isset($t[$key]))
$rec[$key] = $this->convert($value, $t[$key]);
}
}
return $rec;
}
/**
* Like fetch(), but returns only first field
* @return mixed value on success, FALSE if no next record
*/
final function fetchSingle()
{
$rec = $this->doFetch();
if (!is_array($rec))
return FALSE;
// types-converting?
if ($t = $this->convert) { // little speed-up
$value = reset($rec);
$key = key($rec);
return isset($t[$key])
? $this->convert($value, $t[$key])
: $value;
}
return reset($rec);
}
/**
2007-02-05 05:14:48 +00:00
* Fetches all records from table.
2006-06-04 23:06:33 +00:00
* @return array
*/
final function fetchAll()
{
@$this->seek(0);
$rec = $this->fetch();
if (!$rec)
return array(); // empty resultset
$arr = array();
if (count($rec) == 1) {
$key = key($rec);
2006-06-04 23:06:33 +00:00
do {
$arr[] = $rec[$key];
2006-06-04 23:06:33 +00:00
} while ($rec = $this->fetch());
} else {
do {
$arr[] = $rec;
} while ($rec = $this->fetch());
2006-06-04 23:06:33 +00:00
}
return $arr;
}
/**
2007-02-05 05:14:48 +00:00
* Fetches all records from table and returns associative tree
* Associative descriptor: assoc1,*,assoc2,#,assco3
* builds a tree: $arr[value1][index][value2]['assoc3'][value3] = {record}
*
* @param string associative descriptor
* @return array
*/
2007-02-05 05:14:48 +00:00
final function fetchAssoc($assoc)
{
@$this->seek(0);
$rec = $this->fetch();
2007-02-05 05:14:48 +00:00
if (!$rec) return array(); // empty resultset
2007-02-05 05:14:48 +00:00
$arr = NULL;
$assoc = explode(',', $assoc);
2007-02-05 05:14:48 +00:00
if (count($assoc) === 1) { // speed-up
$as = $assoc[0];
do {
$arr[ $rec[$as] ] = $rec;
} while ($rec = $this->fetch());
return $arr;
}
2007-02-05 05:14:48 +00:00
$last = count($assoc) - 1;
if ($assoc[$last] === '#') unset($assoc[$last]);
2007-02-05 05:14:48 +00:00
// make associative tree
do {
$x = & $arr;
// iterative deepening
foreach ($assoc as $i => $as) {
if ($as === '*') { // indexed-array node
$x = & $x[];
} elseif ($as === '#') { // "record" node
if ($x === NULL) {
$x = $rec;
$x = & $x[ $assoc[$i+1] ];
$x = NULL; // prepare child node
} else {
$x = & $x[ $assoc[$i+1] ];
}
} else { // associative-array node
$x = & $x[ $rec[ $as ] ];
}
2006-06-04 23:06:33 +00:00
}
2007-02-05 05:14:48 +00:00
if ($x === NULL) $x = $rec; // build leaf
2006-06-04 23:06:33 +00:00
} while ($rec = $this->fetch());
2007-02-05 05:14:48 +00:00
unset($x);
2006-06-04 23:06:33 +00:00
return $arr;
}
2007-02-05 05:14:48 +00:00
2006-06-04 23:06:33 +00:00
/**
* Fetches all records from table like $key => $value pairs
2007-04-25 08:19:03 +00:00
* @param string associative key
* @param string value
2006-06-04 23:06:33 +00:00
* @return array
*/
2007-04-25 08:19:03 +00:00
final function fetchPairs($key=NULL, $value=NULL)
2006-06-04 23:06:33 +00:00
{
@$this->seek(0);
$rec = $this->fetch();
2007-05-17 21:02:26 +00:00
if (!$rec) return array(); // empty resultset
2006-06-04 23:06:33 +00:00
2007-04-25 08:19:03 +00:00
if ($value === NULL) {
$tmp = array_keys($rec);
$key = $tmp[0];
$value = $tmp[1];
} else {
if (!array_key_exists($key, $rec)) return FALSE;
if (!array_key_exists($value, $rec)) return FALSE;
}
2006-06-04 23:06:33 +00:00
$arr = array();
do {
$arr[ $rec[$key] ] = $rec[$value];
} while ($rec = $this->fetch());
return $arr;
}
/**
* Automatically frees the resources allocated for this result set
* @return void
*/
public function __destruct()
{
@$this->free();
}
public function setType($field, $type = NULL)
{
if ($field === TRUE)
$this->detectTypes();
elseif (is_array($field))
$this->convert = $field;
else
$this->convert[$field] = $type;
}
2006-06-04 23:06:33 +00:00
/** is this needed? */
public function getType($field)
{
return isset($this->convert[$field]) ? $this->convert[$field] : NULL;
}
2006-06-04 23:06:33 +00:00
public function convert($value, $type)
{
if ($value === NULL || $value === FALSE)
return $value;
if (isset(self::$types[$type])) {
settype($value, self::$types[$type]);
2006-06-04 23:06:33 +00:00
return $value;
}
2006-06-08 01:35:44 +00:00
if ($type == dibi::FIELD_DATE)
2006-06-07 15:50:32 +00:00
return strtotime($value); // !!! not good
2006-06-04 23:06:33 +00:00
2006-06-08 01:35:44 +00:00
if ($type == dibi::FIELD_DATETIME)
2006-06-07 15:50:32 +00:00
return strtotime($value); // !!! not good
2006-06-04 23:06:33 +00:00
return $value;
}
/**
* Gets an array of field names
* @return array
*/
public function getFields()
{
// lazy init
if ($this->meta === NULL) $this->buildMeta();
return array_keys($this->meta);
}
/**
* Gets an array of meta informations about column
* @param string column name
* @return array
*/
public function getMetaData($field)
{
// lazy init
if ($this->meta === NULL) $this->buildMeta();
return isset($this->meta[$field]) ? $this->meta[$field] : FALSE;
}
/**
* Acquires ....
* @return void
*/
protected function detectTypes()
{
if ($this->meta === NULL) $this->buildMeta();
}
/**
* @return void
*/
abstract protected function buildMeta();
2006-06-04 23:06:33 +00:00
/** these are the required IteratorAggregate functions */
public function getIterator($offset = NULL, $count = NULL)
{
return new DibiResultIterator($this, $offset, $count);
}
/** end required IteratorAggregate functions */
2006-06-04 23:06:33 +00:00
/** these are the required Countable functions */
public function count()
{
return $this->rowCount();
}
/** end required Countable functions */
/**
* Undefined property usage prevention
*/
function __get($nm) { throw new Exception("Undefined property '" . get_class($this) . "::$$nm'"); }
function __set($nm, $val) { $this->__get($nm); }
private function __unset($nm) { $this->__get($nm); }
private function __isset($nm) { $this->__get($nm); }
2006-06-04 23:06:33 +00:00
} // class DibiResult
/**
* Basic Result set iterator.
*
* This can be returned by DibiResult::getIterator() method or directly using foreach:
* <code>
* $result = dibi::query('SELECT * FROM table');
* foreach ($result as $fields) {
* print_r($fields);
* }
* unset($result);
* </code>
*
* Optionally you can specify offset and limit:
* <code>
* foreach ($result->getIterator(2, 3) as $fields) {
* print_r($fields);
* }
* </code>
*/
class DibiResultIterator implements Iterator
{
private
$result,
$offset,
$count,
$record,
$row;
public function __construct(DibiResult $result, $offset = NULL, $count = NULL)
{
$this->result = $result;
$this->offset = (int) $offset;
2006-06-04 23:09:53 +00:00
$this->count = $count === NULL ? 2147483647 /*PHP_INT_MAX till 5.0.5 */ : (int) $count;
2006-06-04 23:06:33 +00:00
}
2006-06-04 23:06:33 +00:00
/** these are the required Iterator functions */
public function rewind()
{
$this->row = 0;
@$this->result->seek($this->offset);
$this->record = $this->result->fetch();
}
2006-06-04 23:06:33 +00:00
public function key()
{
return $this->row;
}
2006-06-04 23:06:33 +00:00
public function current()
{
return $this->record;
}
2006-06-04 23:06:33 +00:00
public function next()
{
$this->record = $this->result->fetch();
$this->row++;
}
2006-06-04 23:06:33 +00:00
public function valid()
{
return is_array($this->record) && ($this->row < $this->count);
}
/** end required Iterator functions */
2006-06-04 23:06:33 +00:00
} // class DibiResultIterator