291 lines
No EOL
6.6 KiB
PHP
291 lines
No EOL
6.6 KiB
PHP
<?php
|
|
/*
|
|
* Copyright (c) 2010 by Justin Otherguy <justin@justinotherguy.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License (either version 2 or
|
|
* version 3) as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
* For more information on the GPL, please go to:
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*/
|
|
|
|
/**
|
|
* @brief base exception for database queries
|
|
*/
|
|
class DatabaseException extends Exception {}
|
|
|
|
/**
|
|
* @brief abstract resultset definition
|
|
*/
|
|
abstract class DatabaseResultSet implements Iterator {
|
|
/**
|
|
* @brief rowcount of the result
|
|
* @var int
|
|
*/
|
|
protected $_num_rows = 0;
|
|
|
|
/**
|
|
* @brief result
|
|
* @var array
|
|
*/
|
|
protected $_rows = array();
|
|
|
|
/**
|
|
* @param resource $resource database resource
|
|
*/
|
|
abstract function __construct($resource);
|
|
|
|
/**
|
|
* @brief current element (iterator)
|
|
* @return array
|
|
*/
|
|
public function current() {
|
|
return current($this->_rows);
|
|
}
|
|
|
|
/**
|
|
* @brief next element (iterator)
|
|
* @return array
|
|
*/
|
|
public function next() {
|
|
return next($this->_rows); // TODO with fetch_assoc
|
|
}
|
|
|
|
/**
|
|
* @brief previous element (iterator)
|
|
* @return array
|
|
*/
|
|
public function prev() {
|
|
return prev($this->_rows);
|
|
}
|
|
|
|
/**
|
|
* @brief index of current element (iterator)
|
|
* @return array
|
|
*/
|
|
public function key() {
|
|
return key($this->_rows);
|
|
}
|
|
|
|
/**
|
|
* @brief first element (pointer reset, iterator)
|
|
* @return array
|
|
*/
|
|
public function rewind() {
|
|
return reset($this->_rows);
|
|
}
|
|
|
|
/**
|
|
* @brief last element (iterator)
|
|
* @return array
|
|
*/
|
|
public function end() {
|
|
return end($this->_rows);
|
|
}
|
|
|
|
/**
|
|
* @brief check current element (iterator)
|
|
* @return bool
|
|
*/
|
|
public function valid() {
|
|
return (bool) is_array($this->current());
|
|
}
|
|
|
|
/**
|
|
* @brief count rows of the result set
|
|
* @return integer
|
|
*/
|
|
public function count() {
|
|
return $this->_num_rows;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief interface database definition
|
|
*/
|
|
interface DatabaseInterface {
|
|
/**
|
|
* @brief create database connection
|
|
* @param array connection info
|
|
*/
|
|
public function __construct($config);
|
|
|
|
/**
|
|
* @brief close database connection
|
|
*/
|
|
public function close();
|
|
|
|
/**
|
|
* @brief execute query
|
|
* @param string $sql query
|
|
* @return mixed
|
|
*/
|
|
public function execute($sql);
|
|
|
|
/**
|
|
* @brief query
|
|
* @param string $sql
|
|
* @param int $offset
|
|
* @param int $limit
|
|
* @return TDatabaseResultSet
|
|
*/
|
|
public function query($sql, $limit = NULL, $offset = NULL);
|
|
|
|
/**
|
|
* @brief escape strings
|
|
* @param string $string to escape
|
|
* @return string escaped string
|
|
*/
|
|
public function escapeString($string);
|
|
|
|
/**
|
|
* @brief escape expression
|
|
* @param mixed $string to escape
|
|
* @return string
|
|
*/
|
|
public function escape($value);
|
|
|
|
/**
|
|
* @brief get last inserted id
|
|
* @return integer of the last record
|
|
*/
|
|
public function lastInsertId();
|
|
|
|
public function select($table, $fields = '*', $filter = array(), $conjunction = true, $joins = array(), $limit = NULL, $offset = NULL);
|
|
public function delete($table, $filters, $conjunction = true);
|
|
public function update($table, $data, $filters, $conjunction = true);
|
|
}
|
|
|
|
/**
|
|
* @brief abstract database layer definition
|
|
*/
|
|
abstract class Database implements DatabaseInterface {
|
|
static private $connection = NULL;
|
|
|
|
/**
|
|
* @brief database handle
|
|
* @var resource
|
|
*/
|
|
protected $resource = false;
|
|
|
|
/**
|
|
* @brief container with exectuted queries
|
|
* @var array
|
|
*/
|
|
protected $statements = array();
|
|
|
|
/*
|
|
* @return singleton instance
|
|
*/
|
|
static public function getConnection() {
|
|
if (is_null(self::$connection)) {
|
|
$config = Registry::get('config');
|
|
|
|
if (!class_exists($config['db']['backend']) || !is_subclass_of($config['db']['backend'], 'Database')) {
|
|
throw new InvalidArgumentException('\'' . $config['db']['backend'] . '\' is not a valid database backend');
|
|
}
|
|
self::$connection = new $config['db']['backend']($config['db']);
|
|
}
|
|
|
|
return self::$connection;
|
|
}
|
|
|
|
public function escape($value) {
|
|
if (is_numeric($value)) {
|
|
return (string) $value;
|
|
}
|
|
else {
|
|
$value = '\'' . $this->escapeString($value) . '\'';
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
public function query($sql, $limit = NULL, $offset = NULL) {
|
|
if (!is_null($limit))
|
|
$sql .= ' LIMIT ' . (int) $limit;
|
|
|
|
if (!is_null($offset))
|
|
$sql .= ' OFFSET ' . (int) $offset;
|
|
|
|
$rs = get_class($this) . 'ResultSet';
|
|
|
|
return new $rs($this->execute($sql));
|
|
}
|
|
|
|
/*
|
|
* Query functions
|
|
*/
|
|
public function select($table, $fields = '*', $filter = array(), $conjunction = true, $joins = array(), $limit = NULL, $offset = NULL) {
|
|
$sql = 'SELECT ' . implode(' ,', $fields) . ' FROM ' . $table;
|
|
|
|
foreach ($joins as $join) {
|
|
$sql .= $this->buildJoin($join);
|
|
}
|
|
|
|
$sql .= $this->buildFilter($filter, $conjunction);
|
|
|
|
return $this->query($sql, $limit, $offset);
|
|
}
|
|
|
|
public function delete($table, $filters, $conjunction = true) {
|
|
return $this->execute('DELETE FROM ' . $table . $this->buildFilter($filters, $conjunction));
|
|
}
|
|
|
|
public function update($table, $data, $filters, $conjunction = true) {
|
|
$updateFields = array();
|
|
foreach ($data as $column => $value) {
|
|
$updateFields[] = $column . ' = ' . $this->escape($value);
|
|
}
|
|
|
|
$sql = 'UPDATE ' . $table . ' SET' . implode(' ,' , $updateFields) . $this->filter($filters, $conjunction);
|
|
|
|
return $this->execute($sql);
|
|
}
|
|
|
|
public function insert($table, $data) {
|
|
$sql = 'INSERT INTO ' . $table . ' (' . implode(' ,' , array_keys($data)) . ') VALUES (' . implode(', ', array_map(array($this, 'escape'), $value)) . ')';
|
|
|
|
return $this->execute($sql);
|
|
}
|
|
|
|
protected function buildFilter($filters, $conjunction) {
|
|
$where = array();
|
|
foreach ($filters as $column => $value) {
|
|
if (is_array($value)) {
|
|
$where[] = $column . ' IN (' . implode(', ', array_map(array($this, 'escape'), $value)) . ')';
|
|
}
|
|
else {
|
|
$where[] = $column . ' = ' . $this->escape($value);
|
|
}
|
|
}
|
|
|
|
if (count($where) > 0) {
|
|
return ' WHERE ' . implode(($conjunction === true) ? ' && ' : ' || ', $where);
|
|
}
|
|
}
|
|
|
|
protected function buildJoin($join) {
|
|
if (!isset($join['type'])) {
|
|
$join['type'] = 'left';
|
|
}
|
|
|
|
return ' ' . strtoupper($join['type']) . ' JOIN ' . $join['table'] . ' ON ' . $join['condition'];
|
|
}
|
|
|
|
public function __desctruct() {
|
|
$this->close();
|
|
}
|
|
}
|
|
|
|
?>
|