<?php defined('SYSPATH') OR die('No direct access to this file is allowed.');
/**
* Database class, specific to MySQL 4.1 or higher.
*
* This database class uses the PHP 5.1+ PDO class for everything.
*
* @package Noostr
* @subpackage Classes
*/
class Database {
private $autocommit = true; // autocommit status
private $commitcount; // counts queries within transaction
private $database; // database password to use
private $db; // database connection
private $host; // location of the database
private $lastdata; // the last data array
private $lastquery; // the last query run
private $password; // password to the database
private $user; // username to the database
public $count; // counts queries
public $datalist = array(); // recording of query data passed
public $numrows; // number of rows from last query
public $querylist = array(); // recording of queries passed
public $resultlist = array(); // recording of query results
/**
* Constructor for the class. Lazy loading is implemented so the connection to
* the database isn't actually made until the first query is executed. All we
* do here is store the params in private variables until needed.
*
* @param string $host Host address of database server.
* @param string $user Username for database server.
* @param string $password Password for user on database server.
* @param string $database Name of the database to use for this instance.
*/
public function __construct($host, $user, $password, $database) {
$this->host = $host;
$this->user = $user;
$this->password = $password;
$this->database = $database;
}
/**
* Calls the {@link disconnect()} method.
*/
public function __destruct() {
$this->disconnect();
}
/**
* Connect to the database using the private params from the {@link __construct()} method.
* We set the query counter and transaction queue counter to 0.
*/
private function connect() {
$this->db = null;
try {
$this->db = new PDO('mysql:host='.$this->host.';dbname='.$this->database.'', $this->user, $this->password, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
} catch(PDOException $e) {
echo "Database error!!!<br />";
switch ($e->getCode()) {
case 1045:
// Cannot connect to database
die('Cannot connect to database!');
break;
}
exit();
}
$this->commitcount = 0;
$this->count = 0;
}
/**
* Disconnects from the database after doing a rollback, if needed.
*/
public function disconnect() {
if (isset($this->db)) {
$this->rollback();
write_log();
$this->db = null;
}
}
/**
* Sets the database's "autocommit" feature on or off. Currently not used except
* to enable transaction queue counting.
*
* @param bool $enabled Is the autocommit function enabled?
*/
public function autocommit($enabled) {
$this->autocommit = $enabled;
}
/**
* Begins a transaction if {@link autocommit()} is disabled.
*/
public function begin() {
if (!$this->autocommit) {
if (!$this->db) {
$this->connect();
}
$this->db->beginTransaction();
}
}
/**
* Commits a transaction if {@link autocommit()} is disabled.
*/
public function commit() {
if (!$this->autocommit && $this->autocommit > 0) {
$this->db->commit();
$this->commitcount = 0;
}
}
/**
* Rolls back a transaction if {@link autocommit()} is disabled.
*/
public function rollback() {
if (!$this->autocommit && $this->commitcount > 0) {
$this->db->rollBack();
$this->commitcount = 0;
}
}
/**
* Runs a query against the database, and returns the result (if any) or FALSE
* if the query fails. Placeholders and prepared statements should be used
* wherever possible to avoid SQL injection attacks.
*
* @param string $query Query string to run, with or without placeholders.
* @param array|string $data The data to use instead of the placeholders.
* @param int $start Starting row of returned data.
* @param int $rows Number of rows to return.
* @return array|bool Array of results if query is good, FALSE otherwise.
*/
public function query($query, $data = null, $start = null, $rows = null) {
$return = null;
$result = null;
$db = null;
$dberror = null;
if (!is_array($data)) {
$data = array($data);
}
$this->numrows = 0;
if (isset($start) && isset($rows)) {
$query .= ' limit '.$start.', '.$rows;
}
if (!$this->autocommit && $this->commitcount == 0) {
$this->commitcount++;
$this->begin();
}
// Check if this query/data has already been run.
$querycheck = array_keys($this->querylist, $query);
if (count($querycheck) > 0) {
for ($i = 0, $c = count($querycheck); $i < $c; $i++) {
if ($this->datalist[$querycheck[$i]] == json_encode($data)) {
// Found a match! Return the cached data.
$return = json_decode($this->resultlist[$querycheck[$i]], true);
}
}
}
$this->querylist[] = $query;
$this->datalist[] = json_encode($data);
//echo '<em>'.$query.'</em><br />';
if (is_null($return)) {
if (!$this->db) {
$this->connect();
}
if ($query != $this->lastquery || json_encode($data) != $this->lastdata) {
$db = $this->db->prepare($query.';');
$this->lastquery = $query;
}
$this->lastdata = json_encode($data);
$db->execute(array_values($data));
$dberror = $db->errorInfo();
if ($dberror[0] == '42S02') {
// Table not found!
if (!defined('INSTALL')) {
locate(HTTP.URL.PORT.'/install/', true);
}
}
$result = $db->fetchAll(PDO::FETCH_ASSOC);
$this->count++;
if (!$this->autocommit && $this->commitcount > 0) {
$this->commitcount++;
}
//print_r($result);
if (!is_bool($result)) {
$this->numrows = count($result);
$return = $result;
}
if (strtolower(substr($query, 0, 6)) == 'insert') {
$return = $this->db->lastInsertId();
}
$this->resultlist[] = json_encode($return);
}
return $return;
}
}