Projekt

Obecné

Profil

Stáhnout (14.6 KB) Statistiky
| Větev: | Tag: | Revize:
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Database API driver
*
* $Id: Database.php 4343 2009-05-08 17:04:48Z jheathco $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
abstract class Database_Driver {

protected $query_cache;

/**
* Connect to our database.
* Returns FALSE on failure or a MySQL resource.
*
* @return mixed
*/
abstract public function connect();

/**
* Perform a query based on a manually written query.
*
* @param string SQL query to execute
* @return Database_Result
*/
abstract public function query($sql);

/**
* Builds a DELETE query.
*
* @param string table name
* @param array where clause
* @return string
*/
public function delete($table, $where)
{
return 'DELETE FROM '.$this->escape_table($table).' WHERE '.implode(' ', $where);
}

/**
* Builds an UPDATE query.
*
* @param string table name
* @param array key => value pairs
* @param array where clause
* @return string
*/
public function update($table, $values, $where)
{
foreach ($values as $key => $val)
{
$valstr[] = $this->escape_column($key).' = '.$val;
}
return 'UPDATE '.$this->escape_table($table).' SET '.implode(', ', $valstr).' WHERE '.implode(' ',$where);
}

/**
* Set the charset using 'SET NAMES <charset>'.
*
* @param string character set to use
*/
public function set_charset($charset)
{
throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__);
}

/**
* Wrap the tablename in backticks, has support for: table.field syntax.
*
* @param string table name
* @return string
*/
abstract public function escape_table($table);

/**
* Escape a column/field name, has support for special commands.
*
* @param string column name
* @return string
*/
abstract public function escape_column($column);

/**
* Builds a WHERE portion of a query.
*
* @param mixed key
* @param string value
* @param string type
* @param int number of where clauses
* @param boolean escape the value
* @return string
*/
public function where($key, $value, $type, $num_wheres, $quote)
{
$prefix = ($num_wheres == 0) ? '' : $type;

if ($quote === -1)
{
$value = '';
}
else
{
if ($value === NULL)
{
if ( ! $this->has_operator($key))
{
$key .= ' IS';
}

$value = ' NULL';
}
elseif (is_bool($value))
{
if ( ! $this->has_operator($key))
{
$key .= ' =';
}

$value = ($value == TRUE) ? ' 1' : ' 0';
}
else
{
if ( ! $this->has_operator($key) AND ! empty($key))
{
$key = $this->escape_column($key).' =';
}
else
{
preg_match('/^(.+?)([<>!=]+|\bIS(?:\s+NULL))\s*$/i', $key, $matches);
if (isset($matches[1]) AND isset($matches[2]))
{
$key = $this->escape_column(trim($matches[1])).' '.trim($matches[2]);
}
}

$value = ' '.(($quote == TRUE) ? $this->escape($value) : $value);
}
}

return $prefix.$key.$value;
}

/**
* Builds a LIKE portion of a query.
*
* @param mixed field name
* @param string value to match with field
* @param boolean add wildcards before and after the match
* @param string clause type (AND or OR)
* @param int number of likes
* @return string
*/
public function like($field, $match, $auto, $type, $num_likes)
{
$prefix = ($num_likes == 0) ? '' : $type;

$match = $this->escape_str($match);

if ($auto === TRUE)
{
// Add the start and end quotes
$match = '%'.str_replace('%', '\\%', $match).'%';
}

return $prefix.' '.$this->escape_column($field).' LIKE \''.$match . '\' COLLATE utf8_general_ci';
}

/**
* Builds a NOT LIKE portion of a query.
*
* @param mixed field name
* @param string value to match with field
* @param string clause type (AND or OR)
* @param int number of likes
* @return string
*/
public function notlike($field, $match, $auto, $type, $num_likes)
{
$prefix = ($num_likes == 0) ? '' : $type;

$match = $this->escape_str($match);

if ($auto === TRUE)
{
// Add the start and end quotes
$match = '%'.$match.'%';
}

return $prefix.' '.$this->escape_column($field).' NOT LIKE \''.$match.'\' COLLATE utf8_general_ci';
}

/**
* Builds a REGEX portion of a query.
*
* @param string field name
* @param string value to match with field
* @param string clause type (AND or OR)
* @param integer number of regexes
* @return string
*/
public function regex($field, $match, $type, $num_regexs)
{
throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__);
}

/**
* Builds a NOT REGEX portion of a query.
*
* @param string field name
* @param string value to match with field
* @param string clause type (AND or OR)
* @param integer number of regexes
* @return string
*/
public function notregex($field, $match, $type, $num_regexs)
{
throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__);
}

/**
* Builds an INSERT query.
*
* @param string table name
* @param array keys
* @param array values
* @return string
*/
public function insert($table, $keys, $values)
{
// Escape the column names
foreach ($keys as $key => $value)
{
$keys[$key] = $this->escape_column($value);
}
return 'INSERT INTO '.$this->escape_table($table).' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';
}

/**
* Builds a MERGE portion of a query.
*
* @param string table name
* @param array keys
* @param array values
* @return string
*/
public function merge($table, $keys, $values)
{
throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__);
}

/**
* Builds a LIMIT portion of a query.
*
* @param integer limit
* @param integer offset
* @return string
*/
abstract public function limit($limit, $offset = 0);

/**
* Creates a prepared statement.
*
* @param string SQL query
* @return Database_Stmt
*/
public function stmt_prepare($sql = '')
{
throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__);
}

/**
* Compiles the SELECT statement.
* Generates a query string based on which functions were used.
* Should not be called directly, the get() function calls it.
*
* @param array select query values
* @return string
*/
abstract public function compile_select($database);

/**
* Determines if the string has an arithmetic operator in it.
*
* @param string string to check
* @return boolean
*/
public function has_operator($str)
{
return (bool) preg_match('/[<>!=]|\sIS(?:\s+NOT\s+)?\b|BETWEEN/i', trim($str));
}

/**
* Escapes any input value.
*
* @param mixed value to escape
* @return string
*/
public function escape($value)
{
if ( ! $this->db_config['escape'])
return $value;

switch (gettype($value))
{
case 'string':
$value = '\''.$this->escape_str($value).'\'';
break;
case 'boolean':
$value = (int) $value;
break;
case 'double':
// Convert to non-locale aware float to prevent possible commas
$value = sprintf('%F', $value);
break;
default:
$value = ($value === NULL) ? 'NULL' : $value;
break;
}

return (string) $value;
}

/**
* Escapes a string for a query.
*
* @param mixed value to escape
* @return string
*/
abstract public function escape_str($str);

/**
* Lists all tables in the database.
*
* @return array
*/
abstract public function list_tables();

/**
* Lists all fields in a table.
*
* @param string table name
* @return array
*/
abstract function list_fields($table);

/**
* Returns the last database error.
*
* @return string
*/
abstract public function show_error();

/**
* Returns field data about a table.
*
* @param string table name
* @return array
*/
abstract public function field_data($table);

/**
* Fetches SQL type information about a field, in a generic format.
*
* @param string field datatype
* @return array
*/
protected function sql_type($str)
{
static $sql_types;

if ($sql_types === NULL)
{
// Load SQL data types
//$sql_types = Config::get('sql_types');

$sql_types = array
(
'tinyint' => array('type' => 'int', 'max' => 127),
'smallint' => array('type' => 'int', 'max' => 32767),
'mediumint' => array('type' => 'int', 'max' => 8388607),
'int' => array('type' => 'int', 'max' => 2147483647),
'integer' => array('type' => 'int', 'max' => 2147483647),
'bigint' => array('type' => 'int', 'max' => 9223372036854775807),
'float' => array('type' => 'float'),
'boolean' => array('type' => 'boolean'),
'time' => array('type' => 'string', 'format' => '00:00:00'),
'date' => array('type' => 'string', 'format' => '0000-00-00'),
'year' => array('type' => 'string', 'format' => '0000'),
'datetime' => array('type' => 'string', 'format' => '0000-00-00 00:00:00'),
'char' => array('type' => 'string', 'exact' => TRUE),
'binary' => array('type' => 'string', 'binary' => TRUE, 'exact' => TRUE),
'varchar' => array('type' => 'string'),
'varbinary' => array('type' => 'string', 'binary' => TRUE),
'blob' => array('type' => 'string', 'binary' => TRUE),
'text' => array('type' => 'string'),
// added for suport of GPS -->
'point' => array('type' => 'string', 'binary' => TRUE),
'linestring' => array('type' => 'string', 'binary' => TRUE),
'polygon' => array('type' => 'string', 'binary' => TRUE),
'geometry' => array('type' => 'string', 'binary' => TRUE),
// <-- added for suport of GPS
);

// DOUBLE
$sql_types['double'] = $sql_types['decimal'] = $sql_types['real'] = $sql_types['float'];

// BIT
$sql_types['bit'] = $sql_types['boolean'];

// TIMESTAMP
$sql_types['timestamp'] = $sql_types['datetime'];

// ENUM
$sql_types['enum'] = $sql_types['varchar'];

// TEXT
$sql_types['tinytext'] = $sql_types['mediumtext'] = $sql_types['longtext'] = $sql_types['text'];

// BLOB
$sql_types['tinyblob'] = $sql_types['mediumblob'] = $sql_types['longblob'] = $sql_types['clob'] = $sql_types['blob'];

}

$str = strtolower(trim($str));

if (($open = strpos($str, '(')) !== FALSE)
{
// Find closing bracket
$close = strpos($str, ')', $open) - 1;

// Find the type without the size
$type = substr($str, 0, $open);
}
else
{
// No length
$type = $str;
}

empty($sql_types[$type]) and exit
(
'Unknown field type: '.$type.'. '.
'Please report this: http://trac.kohanaphp.com/newticket'
);

// Fetch the field definition
$field = $sql_types[$type];

switch ($field['type'])
{
case 'string':
case 'float':
if (isset($close))
{
// Add the length to the field info
$field['length'] = substr($str, $open + 1, $close - $open);
}
break;
case 'int':
// Add unsigned value
$field['unsigned'] = (strpos($str, 'unsigned') !== FALSE);
break;
}

return $field;
}

/**
* Clears the internal query cache.
*
* @param string SQL query
*/
public function clear_cache($sql = NULL)
{
if (empty($sql))
{
$this->query_cache = array();
}
else
{
unset($this->query_cache[$this->query_hash($sql)]);
}

Log::add('debug', 'Database cache cleared: '.get_class($this));
}

/**
* Creates a hash for an SQL query string. Replaces newlines with spaces,
* trims, and hashes.
*
* @param string SQL query
* @return string
*/
protected function query_hash($sql)
{
return sha1(str_replace("\n", ' ', trim($sql)));
}

} // End Database Driver Interface

/**
* Database_Result
*
*/
abstract class Database_Result implements ArrayAccess, Iterator, Countable {

// Result resource, insert id, and SQL
protected $result;
protected $insert_id;
protected $sql;

// Current and total rows
protected $current_row = 0;
protected $total_rows = 0;

// Fetch function and return type
protected $fetch_type;
protected $return_type;

/**
* Returns the SQL used to fetch the result.
*
* @return string
*/
public function sql()
{
return $this->sql;
}

/**
* Returns the insert id from the result.
*
* @return mixed
*/
public function insert_id()
{
return $this->insert_id;
}

/**
* Prepares the query result.
*
* @param boolean return rows as objects
* @param mixed type
* @return Database_Result
*/
abstract function result($object = TRUE, $type = FALSE);

/**
* Builds an array of query results.
*
* @param boolean return rows as objects
* @param mixed type
* @return array
*/
abstract function result_array($object = NULL, $type = FALSE);

/**
* Gets the fields of an already run query.
*
* @return array
*/
abstract public function list_fields();

/**
* Seek to an offset in the results.
*
* @return boolean
*/
abstract public function seek($offset);

/**
* Countable: count
*/
public function count()
{
return $this->total_rows;
}

/**
* ArrayAccess: offsetExists
*/
public function offsetExists($offset)
{
if ($this->total_rows > 0)
{
$min = 0;
$max = $this->total_rows - 1;

return ! ($offset < $min OR $offset > $max);
}

return FALSE;
}

/**
* ArrayAccess: offsetGet
*/
public function offsetGet($offset)
{
if ( ! $this->seek($offset))
return FALSE;

// Return the row by calling the defined fetching callback
return call_user_func($this->fetch_type, $this->result, $this->return_type);
}

/**
* ArrayAccess: offsetSet
*
* @throws Kohana_Database_Exception
*/
final public function offsetSet($offset, $value)
{
throw new Kohana_Database_Exception('database.result_read_only');
}

/**
* ArrayAccess: offsetUnset
*
* @throws Kohana_Database_Exception
*/
final public function offsetUnset($offset)
{
throw new Kohana_Database_Exception('database.result_read_only');
}

/**
* Iterator: current
*/
public function current()
{
return $this->offsetGet($this->current_row);
}

/**
* Iterator: key
*/
public function key()
{
return $this->current_row;
}

/**
* Iterator: next
*/
public function next()
{
++$this->current_row;
return $this;
}

/**
* Iterator: prev
*/
public function prev()
{
--$this->current_row;
return $this;
}

/**
* Iterator: rewind
*/
public function rewind()
{
$this->current_row = 0;
return $this;
}

/**
* Iterator: valid
*/
public function valid()
{
return $this->offsetExists($this->current_row);
}

} // End Database Result Interface
(3-3/5)