PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_OBJ,
PDO::ATTR_CASE=>PDO::CASE_LOWER,
PDO::ATTR_AUTOCOMMIT=>true
)
);
self::$pdo->exec("SET NAMES UTF8");
// Assign the singleton
self::$instance=new self;
}
// We don't access this directly, only via query(), exec() and prepare()
public static function getInstance() {
if (self::$pdo instanceof PDO) {
return self::$instance;
} else {
trigger_error('WT_DB::createInstance() must be called before WT_DB::getInstance().', E_USER_ERROR);
}
}
public static function isConnected() {
return (self::$pdo instanceof PDO);
}
//////////////////////////////////////////////////////////////////////////////
// LOGGING
// Keep a log of the statements executed using this connection
//////////////////////////////////////////////////////////////////////////////
private static $log=array();
// Add an entry to the log
public static function logQuery($query, $rows, $microtime, $bind_variables) {
if (WT_DEBUG_SQL) {
// Full logging
// Trace
$trace=debug_backtrace();
array_shift($trace);
array_shift($trace);
foreach ($trace as $n=>$frame) {
if (isset($frame['file']) && isset($frame['line'])) {
$trace[$n]=basename($frame['file']).':'.$frame['line'].' '.$frame['function'].'('./*implode(',', $frame['args']).*/')';
} else {
unset($trace[$n]);
}
}
$stack=''.(count(self::$log)+1).'';
// Bind variables
$query2='';
foreach ($bind_variables as $key=>$value) {
if (is_null($value)) {
$bind_variables[$key]='[NULL]';
}
}
foreach (str_split(htmlspecialchars($query)) as $char) {
if ($char=='?') {
$query2.=''.$char.'';
} else {
$query2.=$char;
}
}
// Highlight embedded literal strings.
if (preg_match('/[\'"]/', $query)) {
$query2=''.$query2.'';
}
// Highlight slow queries
$microtime*=1000; // convert to milliseconds
if ($microtime>1000) {
$microtime=sprintf('%.3f', $microtime);
} elseif ($microtime>100) {
$microtime=sprintf('%.3f', $microtime);
} elseif ($microtime>1) {
$microtime=sprintf('%.3f', $microtime);
} else {
$microtime=sprintf('%.3f', $microtime);
}
self::$log[]="
| {$stack} | {$query2} | {$rows} | {$microtime} |
";
} else {
// Just log query count for statistics
self::$log[]=true;
}
}
// Total number of queries executed, for the page statistics
public static function getQueryCount() {
return count(self::$log);
}
// Display the query log as a table, for debugging
public static function getQueryLog() {
$html='| # | Query | Rows | Time (ms) |
'.implode('', self::$log).'
';
self::$log=array();
return $html;
}
//////////////////////////////////////////////////////////////////////////////
// INTERROGATE DATA DICTIONARY
//////////////////////////////////////////////////////////////////////////////
public static function table_exists($table) {
global $DBNAME;
switch (self::$pdo->getAttribute(PDO::ATTR_DRIVER_NAME)) {
case 'mysql':
// Mysql 4.x does not support the information schema
default:
// Catch-all for other databases
try {
WT_DB::prepare("SELECT 1 FROM {$table}")->fetchOne();
return true;
} catch (PDOException $ex) {
return false;
}
}
}
public static function column_exists($table, $column) {
global $DBNAME;
switch (self::$pdo->getAttribute(PDO::ATTR_DRIVER_NAME)) {
case 'mysql':
// Mysql 4.x does not support the information schema
default:
// Catch-all for other databases
try {
WT_DB::prepare("SELECT {$column} FROM {$table}")->fetchOne();
return true;
} catch (PDOException $ex) {
return false;
}
}
}
//////////////////////////////////////////////////////////////////////////////
// FUNCTIONALITY ENHANCEMENTS
//////////////////////////////////////////////////////////////////////////////
// The native quote() function does not convert PHP nulls to DB nulls
public static function quote($string, $parameter_type=PDO::PARAM_STR) {
if (is_null($string)) {
return 'NULL';
} else {
return self::$pdo->quote($string, $parameter_type);
}
}
// Add logging to query()
public static function query($statement, $parameter_type= PDO::PARAM_STR) {
$statement=str_replace('##', WT_TBLPREFIX, $statement);
$start=microtime(true);
$result=self::$pdo->query($statement, $parameter_type);
$end=microtime(true);
self::logQuery($statement, count($result), $end-$start, array());
return $result;
}
// Add logging to exec()
public static function exec($statement) {
$statement=str_replace('##', WT_TBLPREFIX, $statement);
$start=microtime(true);
$result=self::$pdo->exec($statement);
$end=microtime(true);
self::logQuery($statement, $result, $end-$start, array());
return $result;
}
// Add logging/functionality to prepare()
public static function prepare($statement) {
if (!self::$pdo instanceof PDO) {
throw new PDOException("No Connection Established");
}
$statement=str_replace('##', WT_TBLPREFIX, $statement);
return new WT_DBStatement(self::$pdo->prepare($statement));
}
// Map all other functions onto the base PDO object
public function __call($function, $params) {
return call_user_func_array(array(self::$pdo, $function), $params);
}
//////////////////////////////////////////////////////////////////////////////
// Create/update tables, indexes, etc.
//////////////////////////////////////////////////////////////////////////////
public static function updateSchema($schema_dir, $schema_name, $target_version) {
try {
$current_version=(int)get_site_setting($schema_name);
} catch (PDOException $e) {
// During initial installation, this table won't exist.
// It will only be a problem if we can't subsequently create it.
$current_version=0;
}
while ($current_version<$target_version) {
$next_version=$current_version+1;
require $schema_dir.'db_schema_'.$current_version.'_'.$next_version.'.php';
// The updatescript should update the version or throw an exception
$current_version=(int)get_site_setting($schema_name);
if ($current_version!=$next_version) {
die("Internal error while updating {$schema_name} to {$next_version}");
}
}
}
}