513 Zeilen
13 KiB
PHP
513 Zeilen
13 KiB
PHP
<?php
|
|
/**
|
|
* Project:
|
|
* Contenido Content Management System
|
|
*
|
|
* Description:
|
|
* PDO MySQL database driver
|
|
*
|
|
* NOTE: Is still in development state, don't use it!
|
|
*
|
|
* Requirements:
|
|
* @con_php_req 5
|
|
*
|
|
*
|
|
* @package Contenido database
|
|
* @version 0.1.1
|
|
* @author Murat Purc <murat@purc.de>
|
|
* @copyright four for business AG <www.4fb.de>
|
|
* @license http://www.contenido.org/license/LIZENZ.txt
|
|
* @link http://www.4fb.de
|
|
* @link http://www.contenido.org
|
|
* @since file available since contenido release 4.8.15
|
|
*
|
|
* {@internal
|
|
* created 2011-02-28
|
|
* modified 2011-03-13, Murat Purc, Cleanup and documentation.
|
|
* modified 2011-04-22, Murat Purc, Connect to DB server without database and more
|
|
* readable connection settings.
|
|
*
|
|
* $Id$:
|
|
* }}
|
|
*
|
|
*/
|
|
|
|
if (!defined('CON_FRAMEWORK')) {
|
|
die('Illegal call');
|
|
}
|
|
|
|
|
|
class DB_Sql extends DB_Sql_Abstract
|
|
{
|
|
|
|
/**
|
|
* PDO connection
|
|
* @var PDO
|
|
*/
|
|
public $Query_ID;
|
|
|
|
/**
|
|
* PDO statement
|
|
* @var PDOStatement
|
|
*/
|
|
public $Link_ID;
|
|
|
|
protected $_aDataTypes = array(
|
|
PDO::PARAM_BOOL => 'bool',
|
|
PDO::PARAM_NULL => 'null',
|
|
PDO::PARAM_INT => 'int',
|
|
PDO::PARAM_STR => 'string',
|
|
PDO::PARAM_LOB => 'blob',
|
|
PDO::PARAM_STMT => 'statement'
|
|
);
|
|
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param array $options Optional assoziative options
|
|
*/
|
|
public function __construct(array $options = array())
|
|
{
|
|
$options = array_merge($options, array(
|
|
'type' => 'pdo_mysql',
|
|
));
|
|
parent::__construct($options);
|
|
}
|
|
|
|
|
|
/**
|
|
* @see DB_Sql_Abstract::_connect()
|
|
*/
|
|
protected function _connect()
|
|
{
|
|
$aCon = $this->_aDbCfg['connection'];
|
|
if (!isset($aCon['host']) || !isset($aCon['user']) || !isset($aCon['password'])) {
|
|
$this->halt('MySQL _connect() Connection settings not complete');
|
|
return null;
|
|
}
|
|
|
|
$dsn = 'mysql:';
|
|
if (isset($aCon['database'])) {
|
|
$dsn .= 'dbname=' . $aCon['database'] . ';';
|
|
}
|
|
$dsn .= 'host=' . $aCon['host'];
|
|
|
|
if (!isset($aCon['driver_options']) || !is_array($aCon['driver_options'])) {
|
|
$aCon['driver_options'] = array();
|
|
}
|
|
|
|
try {
|
|
// Create a new PDO connection
|
|
$dbh = new PDO($dsn, $aCon['user'], $aCon['password'], $aCon['driver_options']);
|
|
} catch (PDOException $e) {
|
|
$this->Errno = $e->getCode();
|
|
$this->Error = $e->getMessage();
|
|
}
|
|
|
|
if (!$dbh) {
|
|
$this->halt('PDO_MySQL _connect() Failed');
|
|
return null;
|
|
}
|
|
|
|
if (isset($aCon['database'])) {
|
|
$this->Database = $aCon['database'];
|
|
}
|
|
$this->User = $aCon['user'];
|
|
|
|
return $dbh;
|
|
}
|
|
|
|
|
|
/**
|
|
* Discard the query result
|
|
*/
|
|
public function free()
|
|
{
|
|
if ($this->Query_ID) {
|
|
$this->Query_ID->closeCursor();
|
|
unset($this->Query_ID);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @see DB_Sql_Abstract::_query()
|
|
*/
|
|
protected function _query($sQuery)
|
|
{
|
|
$this->Query_ID = $this->Link_ID->query($sQuery);
|
|
$this->Row = 0;
|
|
$this->Errno = $this->_getErrorNumber();
|
|
$this->Error = $this->_getErrorMessage();
|
|
if (!$this->Query_ID) {
|
|
$this->halt($sQuery);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @see DB_Sql_Abstract::next_record()
|
|
*/
|
|
public function next_record()
|
|
{
|
|
$this->Record = $this->Query_ID->fetch(PDO::FETCH_BOTH);
|
|
$this->Row += 1;
|
|
$this->Errno = $this->_getErrorNumber();
|
|
$this->Error = $this->_getErrorMessage();
|
|
$stat = is_array($this->Record);
|
|
if (!$stat && $this->Auto_Free) {
|
|
$this->free();
|
|
}
|
|
return $stat;
|
|
}
|
|
|
|
|
|
/**
|
|
* @see DB_Sql_Abstract::seek()
|
|
*/
|
|
public function seek($pos = 0)
|
|
{
|
|
throw new Exception('seek not supported');
|
|
}
|
|
|
|
|
|
/**
|
|
* @see DB_Sql_Abstract::lock()
|
|
*/
|
|
public function lock($table, $mode = 'write')
|
|
{
|
|
if ($this->_bNolock == true) {
|
|
return true;
|
|
}
|
|
$query = 'LOCK TABLES ';
|
|
if (is_array($table)) {
|
|
while (list ($key, $value) = each($table)) {
|
|
if (!is_int($key)) {
|
|
// texts key are "read", "read local", "write", "low priority write"
|
|
$query .= "$value $key, ";
|
|
} else {
|
|
$query .= "$value $mode, ";
|
|
}
|
|
}
|
|
$query = substr($query, 0, -2);
|
|
} else {
|
|
$query .= "$table $mode";
|
|
}
|
|
$res = $this->query($query);
|
|
if (!$res) {
|
|
$this->halt('lock() failed.');
|
|
return 0;
|
|
}
|
|
return $res;
|
|
}
|
|
|
|
|
|
/**
|
|
* @see DB_Sql_Abstract::unlock()
|
|
*/
|
|
public function unlock()
|
|
{
|
|
if ($this->_bNolock == true) {
|
|
return true;
|
|
}
|
|
|
|
$res = $this->query('UNLOCK TABLES');
|
|
if (!$res) {
|
|
$this->halt('unlock() failed.');
|
|
}
|
|
return $res;
|
|
}
|
|
|
|
|
|
/**
|
|
* @see DB_Sql_Abstract::affected_rows()
|
|
*/
|
|
public function affected_rows()
|
|
{
|
|
return ($this->Query_ID) ? $this->Query_ID->rowCount() : 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* @see DB_Sql_Abstract::num_rows()
|
|
*/
|
|
public function num_rows()
|
|
{
|
|
if ($this->Query_ID) {
|
|
// clone statement and get count by using fetchAll
|
|
$stmt = clone $this->Query_ID;
|
|
$res = $stmt->fetchAll();
|
|
return (is_array($res)) ? count($stmt->fetchAll()) : 0;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @see DB_Sql_Abstract::num_fields()
|
|
*/
|
|
public function num_fields()
|
|
{
|
|
return count($this->Record / 2);
|
|
}
|
|
|
|
|
|
/**
|
|
* @see DB_Sql_Abstract::nextid()
|
|
*/
|
|
public function nextid($seq_name)
|
|
{
|
|
$this->connect();
|
|
|
|
if ($this->lock($this->Seq_Table)) {
|
|
/* get sequence number (locked) and increment */
|
|
$q = sprintf("SELECT nextid FROM `%s` WHERE seq_name = '%s'", $this->Seq_Table, $seq_name);
|
|
$stmt = $this->Link_ID->query($q);
|
|
$res = ($stmt) ? $stmt->fetch(PDO::FETCH_BOTH) : null;
|
|
|
|
/* No current value, make one */
|
|
if (!is_array($res)) {
|
|
$currentid = 0;
|
|
$q = sprintf("INSERT INTO `%s` VALUES('%s', %s)", $this->Seq_Table, $seq_name, $currentid);
|
|
$stmt = $this->Link_ID->query($q);
|
|
} else {
|
|
$currentid = $res['nextid'];
|
|
}
|
|
$nextid = $currentid + 1;
|
|
$q = sprintf("UPDATE `%s` set nextid = '%s' WHERE seq_name = '%s'", $this->Seq_Table, $nextid, $seq_name);
|
|
$stmt = $this->Link_ID->query($q);
|
|
$this->unlock();
|
|
} else {
|
|
$this->halt('Cannot lock ' . $this->Seq_Table . ' - has it been created?');
|
|
return 0;
|
|
}
|
|
return $nextid;
|
|
}
|
|
|
|
|
|
/**
|
|
* @see DB_Sql_Abstract::disconnect()
|
|
*/
|
|
public function disconnect()
|
|
{
|
|
$this->_debug("Debug: Disconnecting $this->Link_ID...");
|
|
// Destroy the PDO and PDOStatement object
|
|
$this->_removeConnection($this->Link_ID);
|
|
$this->Link_ID = null;
|
|
$this->Query_ID = null;
|
|
}
|
|
|
|
|
|
/**
|
|
* @see DB_Sql_Abstract::_metaData()
|
|
*/
|
|
protected function _metaData($table = '', $full = false)
|
|
{
|
|
$count = 0;
|
|
$id = 0;
|
|
$res = array();
|
|
|
|
/*
|
|
* Due to compatibility problems with Table we changed the behavior
|
|
* of metadata();
|
|
* depending on $full, metadata returns the following values:
|
|
*
|
|
* - full is false (default):
|
|
* $result[]:
|
|
* [0]["table"] table name
|
|
* [0]["name"] field name
|
|
* [0]["type"] field type
|
|
* [0]["len"] field length
|
|
* [0]["flags"] field flags
|
|
*
|
|
* - full is true
|
|
* $result[]:
|
|
* ["num_fields"] number of metadata records
|
|
* [0]["table"] table name
|
|
* [0]["name"] field name
|
|
* [0]["type"] field type
|
|
* [0]["len"] field length
|
|
* [0]["flags"] field flags
|
|
* ["meta"][field name] index of field named "field name"
|
|
* This last one could be used if you have a field name, but no index.
|
|
* Test: if (isset($result['meta']['myfield'])) { ...
|
|
*/
|
|
|
|
// if no $table specified, assume that we are working with a query
|
|
// result
|
|
if ($table) {
|
|
$this->connect();
|
|
$stmt = $this->Link_ID->query(sprintf("DESCRIBE `%s`", $table));
|
|
if (!$stmt) {
|
|
$this->halt('Metadata query failed.');
|
|
return false;
|
|
}
|
|
} else {
|
|
$stmt = $this->Query_ID;
|
|
if (!$stmt) {
|
|
$this->halt('No query specified.');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// loop thru the result and collect meta data
|
|
$res = array();
|
|
while ($rs = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
|
$field = $this->_getFieldTypeDetails($rs['Type']);
|
|
$item = array();
|
|
$item['table'] = $table;
|
|
$item['name'] = $rs['Field'];
|
|
$item['type'] = $field['type'];
|
|
$item['len'] = $field['size'];
|
|
$item['flags'] = null; // @todo detect field flags
|
|
$res[$count] = $item;
|
|
if ($full) {
|
|
$res['meta'][$item['name']] = $count;
|
|
}
|
|
$count++;
|
|
}
|
|
if ($full) {
|
|
$res['num_fields'] = $count;
|
|
}
|
|
|
|
unset($stmt);
|
|
|
|
return $res;
|
|
}
|
|
|
|
|
|
/**
|
|
* @see DB_Sql_Abstract::escape()
|
|
*/
|
|
public function escape($sString)
|
|
{
|
|
$sResult = '';
|
|
$sResult = str_replace("'", "''", $sString);
|
|
// @todo adapt pdo quote method to own requirements
|
|
# if ($this->connect()) {
|
|
# $sResult = $this->Link_ID->quote($sString);
|
|
# }
|
|
return $sResult;
|
|
}
|
|
|
|
|
|
/**
|
|
* @see DB_Sql_Abstract::_tableNames()
|
|
*/
|
|
protected function _tableNames()
|
|
{
|
|
$return = array();
|
|
|
|
$stmt = $this->Link_ID->query('SHOW TABLES');
|
|
$i = 0;
|
|
while ($rs = $stmt->fetch(PDO::FETCH_NUM)) {
|
|
$return[$i]['table_name'] = $rs[0];
|
|
$return[$i]['tablespace_name'] = $this->Database;
|
|
$return[$i]['database'] = $this->Database;
|
|
$i ++;
|
|
}
|
|
|
|
unset($stmt);
|
|
|
|
return $return;
|
|
}
|
|
|
|
|
|
/**
|
|
* @see DB_Sql_Abstract::_serverInfo()
|
|
*/
|
|
protected function _serverInfo()
|
|
{
|
|
$arr['description'] = $this->Link_ID->getAttribute(PDO::ATTR_SERVER_INFO);
|
|
$arr['version'] = $this->Link_ID->getAttribute(PDO::ATTR_SERVER_VERSION);
|
|
return $arr;
|
|
}
|
|
|
|
|
|
/**
|
|
* @see DB_Sql_Abstract::_getErrorMessage()
|
|
*/
|
|
protected function _getErrorMessage()
|
|
{
|
|
$err = null;
|
|
if ($this->Query_ID) {
|
|
$err = $this->Query_ID->errorInfo();
|
|
} elseif ($this->Link_ID) {
|
|
$err = $this->Link_ID->errorInfo();
|
|
}
|
|
if (null !== $err && (int) $err[0] > 0) {
|
|
return $err[2];
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @see DB_Sql_Abstract::_getErrorNumber()
|
|
*/
|
|
protected function _getErrorNumber()
|
|
{
|
|
$err = null;
|
|
if ($this->Query_ID) {
|
|
$err = $this->Query_ID->errorCode();
|
|
} elseif ($this->Link_ID) {
|
|
$err = $this->Link_ID->errorCode();
|
|
}
|
|
if (null !== $err && (int) $err[0] > 0) {
|
|
return $err[0];
|
|
}
|
|
}
|
|
|
|
|
|
protected function _getFieldTypeDetails($field)
|
|
{
|
|
$ret = array('type' => null, 'size' => null);
|
|
if (!$field) {
|
|
return $ret;
|
|
}
|
|
if (preg_match('/^([a-z].*)\(([0-9].*)\)/', $field, $matches)) {
|
|
$ret = array('type' => $matches[1], 'size' => (int) $matches[2]);
|
|
} else {
|
|
$ret['type'] = $field;
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* This method festches the current result set and returns the it as object or
|
|
* null if no result set is left. If optional param $sClassName is set, the
|
|
* result object is an instance of class $sClassName.
|
|
*
|
|
* @param string sClassName Optional the classname to instantiate.
|
|
* @return object|null
|
|
*/
|
|
public function getResultObject($sClassName = null)
|
|
{
|
|
$oResult = null;
|
|
|
|
if ($this->Link_ID && $this->Query_ID) {
|
|
if ($rs = $this->Query_ID->fetch(PDO::FETCH_ASSOC)) {
|
|
if ($sClassName == null) {
|
|
$oResult = (object) $rs;
|
|
} else {
|
|
$oResult = new $sClassName();
|
|
foreach ($rs as $key => $value) {
|
|
$oResult->{$key} = $value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $oResult;
|
|
}
|
|
|
|
public function getServerInfo() {
|
|
return '';
|
|
}
|
|
|
|
public function getClientEncoding() {
|
|
return '';
|
|
}
|
|
|
|
}
|