phplib/DB.php
<?php
namespace FOO;
/**
* Class DB
* Database wrapper.
* @package FOO
*/
class DB {
// Return types.
/** Return # of rows updated. */
const CNT = 0;
/** Return a single value. */
const VAL = 1;
/** Return a single row. */
const ROW = 2;
/** Return all values. */
const COL = 3;
/** Return all rows. */
const ALL = 4;
/** @var \PDO Database handle. */
public static $dbh = null;
/**
* Initializes the class.
*/
public static function init() {
if(self::$dbh) {
return;
}
$dbcfg = Config::get('db');
self::connect($dbcfg['dsn'], $dbcfg['user'], $dbcfg['pass']);
}
/**
* Connect to the database.
* @param string $dsn Database dsn.
* @param string $usr Database username.
* @param string $pwd Database password.
*/
public static function connect($dsn, $usr, $pwd) {
self::$dbh = new \PDO($dsn, $usr, $pwd, [
// \PDO::ATTR_PERSISTENT => true,
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC
]);
Hook::call('db.connect');
}
/**
* Disconnect from the database.
*/
public static function disconnect() {
self::$dbh = null;
Hook::call('db.disconnect');
}
public static function getType() {
return self::$dbh->getAttribute(\PDO::ATTR_DRIVER_NAME);
}
/**
* Keep the connection alive.
*/
public static function keepAlive() {}
/**
* Query the database.
* @param string $query The query to execute.
* @param mixed[] $params The parameters to pass in.
* @param int $ret_type The type of data to return.
* @return mixed Data from the DB.
* @throws DBException
*/
public static function query($query, array $params=[], $ret_type=self::ALL) {
self::keepAlive();
list($query) = Hook::call('db.query', [$query]);
$stmt = self::$dbh->prepare($query);
if(!$stmt) {
throw new DBException('Query failed: ' . $stmt->errorInfo()[2]);
}
$i = 1;
foreach($params as $param) {
if(is_int($param)) {
$type = \PDO::PARAM_INT;
} else if(is_bool($param)) {
$type = \PDO::PARAM_BOOL;
} else if(is_null($param)) {
$type = \PDO::PARAM_NULL;
} else {
$type = \PDO::PARAM_STR;
}
$stmt->bindValue($i, $param, $type);
$i += 1;
}
Logger::dbg($query, $params);
if(!$stmt->execute()) {
throw new DBException('Query failed: ' . $stmt->errorInfo()[2]);
}
$has_res = $stmt->columnCount() > 0;
$ret = null;
switch($ret_type) {
case self::CNT:
$ret = $has_res ? null:$stmt->rowCount();
break;
case self::VAL:
$ret = $has_res ? $stmt->fetchColumn():null;
break;
case self::ROW:
$ret = $has_res ? $stmt->fetch(\PDO::FETCH_ASSOC):null;
break;
case self::COL:
$ret = $has_res ? $stmt->fetchAll(\PDO::FETCH_COLUMN, 0):null;
break;
case self::ALL:
$ret = $has_res ? $stmt->fetchAll(\PDO::FETCH_ASSOC):null;
break;
}
$stmt->closeCursor();
if($ret === false) {
$ret = null;
}
return $ret;
}
/**
* Get the ID of the last inserted row.
* @return int An ID.
*/
public static function insertId() {
return (int) self::$dbh->lastInsertId();
}
/**
* Helper function to generate the (?, ?, ..., ?) string necessary for parameterized queries with an IN clause.
* @param int $n The number of values.
* @return string Placeholder fragment for the IN clause.
*/
public static function inPlaceholder($n) {
return '(' . ($n ? implode(',', array_fill(0, $n, '?')):'NULL') . ')';
}
/**
* Generate a placeholder of the form "`FIELD`" for parameterized queries. If a table name is passed in, this
* will generate "`TABLE`.`FIELD`".
* @param string $n The key name.
* @param string $t The table name.
* @return string A placeholder fragment.
*/
public static function kPlaceholder($n, $t=null) {
return is_null($t) ?
sprintf('`%s`', $n):sprintf('`%s`.`%s`', $t, $n);
}
/**
* Same as kPlaceholder, but accepts an array.
* @param mixed[] $arr The array of values.
* @param string $t The table name.
* @return string[] An array of placeholder fragments.
*/
public static function kPlaceholders($arr, $t=null) {
$ret = [];
foreach($arr as $val) {
$ret[] = self::kPlaceholder($val, $t);
}
return $ret;
}
/**
* Generate a placeholder of the form "`FIELD` = ?" for parameterized queries. If a table name is passed in, this
* will generate "`TABLE`.`FIELD` = ?".
* @param string $n The key name.
* @param string $t The table name.
* @return string A placeholder fragment.
*/
public static function kvPlaceholder($n, $t=null) {
return sprintf('%s = ?', self::kPlaceholder($n, $t));
}
/**
* Same as kvPlaceholder, but accepts an array.
* @param mixed[] $arr The array of values.
* @param string $t The table name.
* @return string[] An array of placeholder fragments.
*/
public static function kvPlaceholders($arr, $t=null) {
$ret = [];
foreach($arr as $val) {
$ret[] = self::kvPlaceholder($val, $t);
}
return $ret;
}
}
/**
* Class DBException
* Base DB exception class.
* @package FOO
*/
class DBException extends \Exception {}
/**
* Class ValidationException
* Exception while validating a Model attribute.
* @package FOO
*/
class ValidationException extends DBException {}