diff options
Diffstat (limited to 'include/PgDB.class.php')
-rw-r--r-- | include/PgDB.class.php | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/include/PgDB.class.php b/include/PgDB.class.php new file mode 100644 index 0000000..b79b2ec --- /dev/null +++ b/include/PgDB.class.php @@ -0,0 +1,339 @@ +<?php +/** + * + * PgDB - future unified database access interface, + * currently only wrapping some global functions + */ + +// temporary workaround until new configuration framework will be in place. +class PgDB { + static private $_db = null; + static private $_master_db = null; + static private $_slave_db = null; + // counts how many times transaction has been started + static private $_transaction_depth = 0; + // states if current transaction has been rolled back at some point + static private $_transaction_failed = false; + + static private $_config = null; + + static private $_me = null; + + /** + * Private constructor prevents multiple objects creation. + * Guarantees singleton pattern usage of class. + */ + private function __construct() { + } + + /** + * returns object providing access to master database PDO functions + * \return object behaving like PDO Database object + */ + static function db() { + self::_needsMasterDB(); + if (self::$_me == null) { + self::$_me = new PgDB(); + } + return self::$_me; + } + + + /** + * Mapping method - thanks to it PgDB object behaves like PDO + * + * @param funcname PDO function to be mapped + * @param args array of PDO function arguments + * @return PDO function response + */ + public function __call($funcname, $args) { + return call_user_func_array([self::$_db,$funcname],$args); + } + + /** + * Declares database dependency, establishes connection if needed. + * Guarantees availablility of master connection. + */ + static private function _needsMasterDB() { + self::$_db = &self::$_master_db; + if (self::$_db == null) { + self::_masterConnect(); + } + } + + static private function _getConfig($key, $default = '') { + if (self::$_config === NULL) { + self::$_config = Env::get('db'); + } + if (isset(self::$_config[$key])) { + return self::$_config[$key]; + } + return $default; + } + + /** + * Establishes connection to master server + * (currently there's no multimaster support available). + */ + static private function _masterConnect() { + self::$_master_db = new PDO('pgsql:host='.self::_getConfig('host').';port='.self::_getConfig('port', 5432).';dbname='.self::_getConfig('database').'', self::_getConfig('user'), self::_getConfig('password')); + self::$_master_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + self::$_db = &self::$_master_db; + } + + /** returns value from first column of first row of query result. + * accepts two types of variable substitution: '?' or ':var', + * '?' expects variable parameter list, ':var' expects second argument + * to be array working as dictionary + * \param $query PgSQL query with optional parameter substitution + * \param $params (optional) array or first parameter for '?' type query + * \param $... - (optional) following parameters for '?' type query + */ + public static function fetchValue($query, $params = []) { + self::_needsMasterDB(); + $sth = self::$_db->prepare($query.';'); + if (!is_array($params)) { + $params = array_slice(func_get_args(),1); + } + $sth->execute($params); + $result = $sth->fetch(PDO::FETCH_NUM); + return isset($result[0]) ? $result[0] : null; + } + + /** returns first first row of query result as assiociative array. + * accepts two types of variable substitution: '?' or ':var', + * '?' expects variable parameter list, ':var' expects second argument + * to be array working as dictionary + * \param $query PgSQL query with optional parameter substitution + * \param $params (optional) array or first parameter for '?' type query + * \param $... - (optional) following parameters for '?' type query + */ + public static function fetchRow($query, $params = []) { + self::_needsMasterDB(); + $sth = self::$_db->prepare($query.';'); + if (!is_array($params)) { + $params = array_slice(func_get_args(),1); + } + + $sth->execute($params); + return $sth->fetch(PDO::FETCH_ASSOC); + } + + /** returns whole query result as numbered array of assiociative arrays. + * accepts two types of variable substitution: '?' or ':var', + * '?' expects variable parameter list, ':var' expects second argument + * to be array working as dictionary + * \param $query PgSQL query with optional parameter substitution + * \param $params (optional) array or first parameter for '?' type query + * \param $... - (optional) following parameters for '?' type query + */ + static public function fetchAll($query, $params = []) { + self::_needsMasterDB(); + $sth = self::$_db->prepare($query.';'); + if (!is_array($params)) { + $params = array_slice(func_get_args(),1); + } + + $sth->execute($params); + return $sth->fetchAll(PDO::FETCH_ASSOC); + } + + + static public function execute($query, $params = []) { + self::_needsMasterDB(); + $sth = self::$_db->prepare($query.';'); + if (!is_array($params)) { + $params = array_slice(func_get_args(),1); + } + $sth->execute($params); + return $sth->rowCount(); + + } + + static public function insert($table, $values, $functions = []) { + self::_needsMasterDB(); + list($query,$args) = self::_insert_prepare($table,$values,$functions); + $sth = self::$_db->prepare($query.';'); + $sth->execute($args); + return $sth->rowCount(); + } + + + static public function update($table,$values,$whereclause,$functions="") { + self::_needsMasterDB(); + $query = 'UPDATE "'. $table .'" SET '; + $cnt=0; + $where = ""; + $args = []; + + foreach ($values as $key => $val) { + if ($cnt) { + $query .=', '; + } + $query .= '"'. $key .'"=?'; + if (is_bool($val)) { + $args[] = ($val) ? 'TRUE' : 'FALSE'; + } + else { + $args[] = $val; + } + $cnt++; + } + + if ($functions!="") { + foreach ($functions as $key => $val) { + if ($cnt) { + $query .=', '; + } + $query .= '"'. $key .'"'. $val; + $cnt++; + } + } + + if (empty($values)) { + $query = 'SELECT * FROM '.$table; + } + + if (is_array($whereclause)) { + $cnt=0; + foreach ($whereclause as $key => $val) { + if ($cnt) { + $where .=' AND '; + } + $where .= '("'. $key .'"=?)'; + $args[] = $val; + $cnt++; + } + $query .= " WHERE ".$where .";"; + } + else { + $query .= " WHERE ". $whereclause .";"; + } + + $sth = self::$_db->prepare($query.';'); + $sth->execute($args); + return $sth->rowCount(); + } + + + public static function set($table,$values,$wherelist,$functions = []) { + $rowsAffected = self::update($table,$values,$wherelist,$functions); + if ($rowsAffected == 0) { + return self::insert($table,array_merge($values,$wherelist),$functions); + } + return $rowsAffcted; + } + + + public static function delete($table, $values, $extrarules="") { + $query = 'DELETE FROM "'. $table .'" WHERE '; + $cnt=0; + $args = []; + foreach ($values as $key => $val) { + if ($cnt) { + $query .= 'AND'; + } + $query .= ' ("'. $key .'" = ?)'; + $args[] = $val; + $cnt++; + } + + if ($extrarules !== "") { + $query .= ' AND '.$extrarules; + } + return self::execute($query,$args); + } + + static private function _insert_prepare($table,$values,$functions = []) { + $query = 'INSERT INTO "'. $table .'"('; + $cnt=0; + $valbuf = ""; + $args = []; + foreach ($values as $key => $val) { + if ($cnt) { + $query .=','; + $valbuf.=','; + } + $query .= '"'. $key .'"'; + $valbuf .= '?'; + + if (is_bool($val)) { + $args[] = ($val) ? 'TRUE' : 'FALSE'; + } + elseif ($val===null) { + $args[] = NULL; + } + else { + $args[] = $val; + } + $cnt++; + } + + foreach ($functions as $key => $val) { + if ($cnt) { + $query .=','; + $valbuf .=','; + } + $query .= '"'. $key .'"'; + $valbuf .= $val; + + $cnt++; + } + + $query .= ") VALUES (". $valbuf .");"; + + return [$query, $args]; + } + + + public static function begin() { + self::_needsMasterDB(); + if (self::$_transaction_depth == 0) { + self::$_db->beginTransaction(); + self::$_transaction_depth++; + } + elseif (self::$_transaction_failed) { + throw new Exception("Trying to begin transaction inside of already stopped transaction!"); + } + else { + self::$_transaction_depth++; + } + } + + public static function commit() { + if (self::$_transaction_depth>0) { + if (self::$_transaction_failed) { + // I'm not convinced that it's good idea, + // but we need to be sure transaction won't go to database. + self::rollback(); + throw new Exception("Transaction already stopped, couldn't be commited"); + } + self::$_transaction_depth--; + if (self::$_transaction_depth==0) { + return self::$_db->commit(); + } + } + else { + throw new Exception("To commit transaction it needs to be started first"); + } + } + + public static function rollback() { + self::$_transaction_failed = true; + self::$_transaction_depth--; + if (self::$_transaction_depth == 0) { + self::$_db->rollback(); + self::$_transaction_failed = false; + } + } + + public static function inTransaction() { + return (self::$_transaction_depth>0); + } + + public static function transactionStateFailed() { + return self::$_transaction_failed; + } +} + +?> |