summaryrefslogtreecommitdiff
path: root/include/PgDB.class.php
diff options
context:
space:
mode:
Diffstat (limited to 'include/PgDB.class.php')
-rw-r--r--include/PgDB.class.php339
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;
+ }
+}
+
+?>