408 lines
9.6 KiB
PHP
408 lines
9.6 KiB
PHP
<?php
|
||
|
||
/**
|
||
* @package system.db
|
||
* Класс оболочка для PDO для замены Creole
|
||
*/
|
||
class Database extends PDO
|
||
{
|
||
public function __construct($dsn, $username = false, $password = false)
|
||
{
|
||
parent::__construct($dsn, $username, $password);
|
||
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||
$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('PDODatabaseStatement', array()));
|
||
}
|
||
|
||
public function getDSN()
|
||
{
|
||
return $this->dsn;
|
||
}
|
||
|
||
public function isPostgres(){
|
||
return ($this->dsn["phptype"] == "pgsql");
|
||
}
|
||
/**
|
||
* Создает соединение с базой данных
|
||
*/
|
||
static function getConnection(array $dsn)
|
||
{
|
||
if ($dsn['phptype'] == 'pgsql' || $dsn['phptype'] == 'mysql') {
|
||
$port = (isset($dsn['port'])) ? "port={$dsn['port']};" : "";
|
||
$connection = new Database("{$dsn['phptype']}:host={$dsn['hostspec']}; $port dbname={$dsn['database']}", $dsn['username'], $dsn['password']);
|
||
$connection->query('SET client_encoding = "UTF-8"');
|
||
}
|
||
if ($dsn['phptype'] == 'sqlite') {
|
||
$connection = new Database("{$dsn['phptype']}:{$dsn['database']}");
|
||
}
|
||
$connection->dsn = $dsn;
|
||
return $connection;
|
||
}
|
||
|
||
public function executeQuery($query)
|
||
{
|
||
$stmt = $this->prepare($query);
|
||
$stmt->setFetchMode(PDO::FETCH_ASSOC);
|
||
$stmt->execute();
|
||
$stmt->cache = $stmt->fetchAll();
|
||
return $stmt;//$sth->fetchAll();
|
||
}
|
||
|
||
public function prepareStatement($query)
|
||
{
|
||
return new DatabaseStatement($query, $this);
|
||
}
|
||
|
||
// Для совместимости со старым представлением баз данных CIS
|
||
/**
|
||
* Извлекает из базы все элементы по запросу
|
||
*/
|
||
public function fetchAllArray($query,$values=null)
|
||
{
|
||
$sth = $this->prepare($query);
|
||
$prep = $this->prepareValues($values);
|
||
$sth->execute($prep);
|
||
return $sth->fetchAll(PDO::FETCH_ASSOC);
|
||
}
|
||
|
||
/**
|
||
* Извлекает из базы первый элемент по запросу
|
||
*/
|
||
public function fetchOneArray($query,$values=null)
|
||
{
|
||
$sth = $this->prepare($query);
|
||
$prep = $this->prepareValues($values);
|
||
$sth->execute($prep);
|
||
return $sth->fetch(PDO::FETCH_ASSOC);
|
||
}
|
||
|
||
private function assignQuote($x, $y)
|
||
{
|
||
return $x . "=" . $this->quote($y);
|
||
}
|
||
|
||
private function prepareValues($values)
|
||
{
|
||
if (!$values) {
|
||
return null;
|
||
}
|
||
$pg = $this->isPostgres();
|
||
$prep = array();
|
||
foreach ($values as $key => $value) {
|
||
$result = null;
|
||
if(is_bool($value)) {
|
||
if ($pg) {
|
||
$result = $value ? 'true' : 'false';
|
||
} else {
|
||
$result = $value ? 1 : 0;
|
||
}
|
||
} else {
|
||
$result = $value;
|
||
}
|
||
$prep[":" . $key] = $result;
|
||
}
|
||
return $prep;
|
||
}
|
||
/**
|
||
* Создает INSERT запрос
|
||
*/
|
||
function insertQuery($table, array $values, $return_id = false, $index = null)
|
||
{
|
||
$prep = $this->prepareValues($values);
|
||
|
||
$sql = "INSERT INTO $table (" . implode(",", array_keys($values))
|
||
. ") VALUES (" . implode(",", array_keys($prep)). ")";
|
||
|
||
if($return_id){
|
||
if ($this->isPostgres()){
|
||
$sql = $sql." RETURNING $index";
|
||
}
|
||
}
|
||
$stmt = $this->prepare($sql);
|
||
$stmt->setFetchMode(PDO::FETCH_ASSOC);
|
||
$stmt->execute($prep);
|
||
$result = $stmt->fetch();
|
||
if ($return_id) {
|
||
if ($this->isPostgres()) {
|
||
return $result[$index];
|
||
} else {
|
||
$result = $this->fetchOneArray("SELECT $index AS lastid FROM $table WHERE OID = last_insert_rowid()");
|
||
return $result['lastid'];
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Создает UPDATE запрос
|
||
*/
|
||
function updateQuery($table, array $values, $cond)
|
||
{
|
||
return $this->query("UPDATE $table SET " . implode(",",
|
||
array_map(array($this, 'assignQuote'), array_keys($values), array_values($values))) . " WHERE $cond");
|
||
}
|
||
|
||
function getIdGenerator() {
|
||
return new IdGenerator($this);
|
||
}
|
||
|
||
/**
|
||
* Замечание: Только для Postgres SQL
|
||
* @param string $seq Имя последовательности для ключа таблицы
|
||
* @return int Идентефикатор следующей записи
|
||
*/
|
||
function getNextId($seq)
|
||
{
|
||
$result = $this->fetchOneArray("SELECT nextval('$seq')");
|
||
return $result['nextval'];
|
||
}
|
||
|
||
function close()
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
class IdGenerator {
|
||
private $db;
|
||
|
||
function __construct($db) {
|
||
$this->db = $db;
|
||
}
|
||
|
||
function isBeforeInsert() {
|
||
return false;
|
||
}
|
||
|
||
function isAfterInsert() {
|
||
return true;
|
||
}
|
||
|
||
function getId($seq) {
|
||
$result = $this->db->fetchOneArray("SELECT nextval('$seq')");
|
||
return $result['nextval'];
|
||
// $result = $this->db->fetchOneArray("SELECT last_insert_rowid() AS nextval");
|
||
// return $result['nextval'];
|
||
}
|
||
}
|
||
|
||
class PDODatabaseStatementIterator implements Iterator
|
||
{
|
||
|
||
private $result;
|
||
private $pos = 0;
|
||
private $fetchmode;
|
||
private $row_count;
|
||
private $rs;
|
||
|
||
|
||
/**
|
||
* Construct the iterator.
|
||
* @param PgSQLResultSet $rs
|
||
*/
|
||
public function __construct($rs)
|
||
{
|
||
$this->result = $rs;
|
||
$this->row_count = $rs->getRecordCount();
|
||
}
|
||
|
||
function rewind()
|
||
{
|
||
$this->pos = 0;
|
||
}
|
||
|
||
function valid()
|
||
{
|
||
return ($this->pos < $this->row_count);
|
||
}
|
||
|
||
function key()
|
||
{
|
||
return $this->pos;
|
||
}
|
||
|
||
function current()
|
||
{
|
||
if (!isset($this->result->cache[$this->pos])) {
|
||
$this->result->cache[$this->pos] = $this->result->fetch(PDO::FETCH_ASSOC);
|
||
}
|
||
return $this->result->cache[$this->pos];
|
||
}
|
||
|
||
function next()
|
||
{
|
||
$this->pos++;
|
||
}
|
||
|
||
function seek ( $index )
|
||
{
|
||
$this->pos = $index;
|
||
}
|
||
|
||
function count ( ) {
|
||
return $this->row_count;
|
||
}
|
||
}
|
||
|
||
class PDODatabaseStatement extends PDOStatement implements IteratorAggregate
|
||
{
|
||
protected $cursorPos = 0;
|
||
public $cache = array();
|
||
public $fields;
|
||
|
||
function getIterator()
|
||
{
|
||
return new PDODatabaseStatementIterator($this);
|
||
}
|
||
|
||
protected function __construct() {
|
||
}
|
||
|
||
function rewind()
|
||
{
|
||
$this->cursorPos = 0;
|
||
}
|
||
|
||
public function seek($rownum)
|
||
{
|
||
if ($rownum < 0) {
|
||
return false;
|
||
}
|
||
|
||
// PostgreSQL rows start w/ 0, but this works, because we are
|
||
// looking to move the position _before_ the next desired position
|
||
$this->cursorPos = $rownum;
|
||
return true;
|
||
}
|
||
|
||
function valid()
|
||
{
|
||
return ( true );
|
||
}
|
||
|
||
|
||
public function first()
|
||
{
|
||
if($this->cursorPos !== 0) { $this->seek(0); }
|
||
return $this->next();
|
||
}
|
||
|
||
function next()
|
||
{
|
||
if ($this->getRecordCount() > $this->cursorPos) {
|
||
if (!isset($this->cache[$this->cursorPos])) {
|
||
$this->cache[$this->cursorPos] = $this->fetch(PDO::FETCH_ASSOC);
|
||
}
|
||
$this->fields = $this->cache[$this->cursorPos];
|
||
|
||
$this->cursorPos++;
|
||
return true;
|
||
} else {
|
||
$this->fields = null;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
function key() {
|
||
return $this->cursorPos;
|
||
}
|
||
|
||
function current()
|
||
{
|
||
return $this->result->fetch(PDO::FETCH_ASSOC);
|
||
}
|
||
|
||
function getRow()
|
||
{
|
||
return $this->fields;
|
||
}
|
||
|
||
function getInt($name)
|
||
{
|
||
return intval($this->fields[$name]);
|
||
}
|
||
|
||
function getBlob($name)
|
||
{
|
||
return $this->fields[$name];
|
||
}
|
||
|
||
function getString($name)
|
||
{
|
||
return $this->fields[$name];
|
||
}
|
||
|
||
function getBoolean($name)
|
||
{
|
||
return (bool)$this->fields[$name];
|
||
}
|
||
|
||
function get($name)
|
||
{
|
||
return $this->fields[$name];
|
||
}
|
||
|
||
function getRecordCount()
|
||
{
|
||
return count($this->cache);
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* Класс оболочка для PDOStatement для замены Creole
|
||
*/
|
||
class DatabaseStatement
|
||
{
|
||
protected $limit = null;
|
||
protected $offset = null;
|
||
protected $statement = null;
|
||
protected $binds = array();
|
||
protected $conn;
|
||
protected $query;
|
||
|
||
function __construct($query, $conn) {
|
||
$this->query = $query;
|
||
$this->conn = $conn;
|
||
}
|
||
|
||
function setInt($n, $value)
|
||
{
|
||
$this->binds [] = array($n, $value, PDO::PARAM_INT);
|
||
}
|
||
|
||
function setString($n, $value)
|
||
{
|
||
$this->binds [] = array($n, $value, PDO::PARAM_STR);
|
||
}
|
||
|
||
function setBlob($n, $value)
|
||
{
|
||
$this->binds [] = array($n, $value, PDO::PARAM_LOB);
|
||
}
|
||
|
||
function setLimit($limit)
|
||
{
|
||
$this->limit = $limit;
|
||
}
|
||
|
||
function setOffset($offset)
|
||
{
|
||
$this->offset = $offset;
|
||
}
|
||
|
||
function executeQuery()
|
||
{
|
||
if ($this->limit) {
|
||
$this->query .= " LIMIT {$this->limit} OFFSET {$this->offset}";
|
||
}
|
||
$stmt = $this->conn->prepare($this->query);
|
||
foreach ($this->binds as $bind) {
|
||
list($n, $value, $type) = $bind;
|
||
$stmt->bindValue($n, $value, $type);
|
||
}
|
||
$stmt->setFetchMode(PDO::FETCH_ASSOC);
|
||
$stmt->execute();
|
||
$stmt->cache = $stmt->fetchAll();
|
||
|
||
return $stmt;
|
||
}
|
||
}
|