Файл: sys/library/goDB/DB.php
Строк: 634
<?php
/**
* goDB2: the library for working with relational databases (for PHP)
*
* @package goDB
* @license https://raw.github.com/vasa-c/go-db/master/LICENSE MIT
* @link https://github.com/vasa-c/go-db source
* @link https://github.com/vasa-c/go-db/wiki documentation
* @uses PHP >= 5.3
* @uses specific dependencies for a specific adapters
*/
namespace goDB;
use goDBHelpersFetchersCursor as CursorFetcher;
use goDBHelpersConnector;
use goDBHelpersTemplater;
use goDBHelpersConfig;
use goDBExceptionsUnknownAdapter;
use goDBExceptionsQuery;
use goDBExceptionsClosed;
use goDBExceptionsConfigSys;
use goDBHelpersDebuggersOutConsole as DebuggerOutConsole;
use goDBHelpersDebuggersOutHtml as DebuggerOutHtml;
/**
* The basic class of database adapters
*
* @author Oleg Grigoriev <go.vasac@gmail.com>
*/
abstract class DB
{
/**
* Creates an instance for database access
*
* @param array $params
* database connection parameters
* @param string $adapter [optional]
* a name of a db-adapter (if it not specified in $params)
* @return goDBDB
* the instance for database access
* @throws goDBExceptionsConfig
* parameters is invalid
* @throws goDBExceptionsConnect
* a connection error
*/
final public static function create(array $params, $adapter = null)
{
$adapter = isset($params['_adapter']) ? $params['_adapter'] : $adapter;
$adapter = strtolower($adapter);
$classname = __NAMESPACE__.'\Adapters\'.ucfirst($adapter);
if (!class_exists($classname, true)) {
throw new UnknownAdapter($adapter);
}
$params['_adapter'] = $adapter;
return (new $classname($params));
}
/**
* Returns the list of available adapters
*
* @return array
*/
final public static function getAvailableAdapters()
{
if (!self::$availableAdapters) {
$adapters = array();
foreach (glob(__DIR__.'/Adapters/*.php') as $filename) {
if (preg_match('~([a-z0-9]*).php$~si', $filename, $matches)) {
$adapters[] = strtolower($matches[1]);
}
}
self::$availableAdapters = $adapters;
}
return self::$availableAdapters;
}
/**
* Performs a query on the database
*
* @param string $pattern
* the query pattern
* @param array $data [optional]
* the incoming data for the query pattern
* @param string $fetch [optional]
* the result format
* @param string $prefix [optional]
* a table prefix for the current query
* @return goDBResult
* the query result in specified format
* @throws goDBExceptionsConnect
* an error of lazy connection
* @throws goDBExceptionsClosed
* the connection is closed
* @throws goDBExceptionsTemplater
* an error of the templating system ($pattern or $data is invalid)
* @throws goDBExceptionsQuery
* an error of the query
* @throws goDBExceptionsFetch
* the result format is invalid for this query type
*/
final public function query($pattern, $data = null, $fetch = null, $prefix = null)
{
$query = $this->makeQuery($pattern, $data, $prefix);
return $this->plainQuery($query, $fetch);
}
/**
* Saves a pre-query
*
* @param string $pattern
* @param array $data [optional]
* @throws goDBExceptionsException
*/
final public function preQuery($pattern, $data = null)
{
$this->preQueries[] = array($pattern, $data);
if ($this->connected) {
$this->execPre();
}
}
/**
* Performs a "plain" query
*
* @param string $query
* a plain query
* @param string $fetch [optional]
* the result format
* @return goDBResult
* the query result in specified format
* @throws goDBExceptionsConnect
* an error of lazy connection
* @throws goDBExceptionsClosed
* the connection is closed
* @throws goDBExceptionsQuery
* an error of the query
* @throws goDBExceptionsFetch
* the result format is invalid for this query type
*/
final public function plainQuery($query, $fetch = null)
{
$this->forcedConnect();
$implementation = $this->connector->getImplementation();
$connection = $this->connector->getConnection();
$duration = microtime(true);
$cursor = $implementation->query($connection, $query);
$duration = microtime(true) - $duration;
$this->query_number++;
$this->tpassed += $duration; // TODO: ???
if (!$cursor) {
$errorInfo = $implementation->getErrorInfo($connection);
$errorCode = $implementation->getErrorCode($connection);
throw new Query($query, $errorInfo, $errorCode);
}
$this->debugLog($query, $duration, null);
$fetcher = $this->createFetcher($cursor);
if ($fetch === null) {
return $fetcher;
}
return $fetcher->fetch($fetch);
}
/**
* @alias for query()
*
* The next examples are identical:
* @example $db->query('SELECT * FROM `table`');
* @example $db('SELECT * FROM `table`');
*
* @param string $pattern
* @param array $data [optional]
* @param string $fetch [optional]
* @param string $prefix [optional]
* @return goDBResult
* @throws goDBExceptionsConnect
* @throws goDBExceptionsClosed
* @throws goDBExceptionsTemplater
* @throws goDBExceptionsQuery
* @throws goDBExceptionsFetch
*/
final public function __invoke($pattern, $data = null, $fetch = null, $prefix = null)
{
return $this->query($pattern, $data, $fetch, $prefix);
}
/**
* Checks if connection is actually established
*
* @return bool
*/
final public function isConnected()
{
if ($this->hardClosed) {
return false;
}
return $this->connector->isConnected();
}
/**
* Chick if connection is closed (hard)
*
* @return bool
*/
final public function isClosed()
{
return $this->hardClosed;
}
/**
* Forced establishes a connection (if its is not establishes)
*
* @return bool
* a connection has been established at this time
* @throws goDBExceptionsConnect
* a connection error
* @throws goDBExceptionsClosed
* a connection has been closed (hard)
*/
final public function forcedConnect()
{
if ($this->hardClosed) {
throw new Closed();
}
if ($this->connected) {
return false;
}
$res = $this->connector->connect();
$this->connected = true;
$this->execPre();
return $res;
}
/**
* Closes a connection
*
* @param boolean $soft [optional]
* uses "soft" closing (it is possible to restore)
* @return boolean
* a connection has been closed at this time
*/
final public function close($soft = false)
{
if ($this->hardClosed) {
return false;
}
$this->hardClosed = !$soft;
if (!$this->connected) {
return false;
}
$this->connected = false;
return $this->connector->close();
}
/**
* Sets the prefix for tables
*
* @param string $prefix
*/
final public function setPrefix($prefix)
{
$this->prefix = $prefix;
}
/**
* Returns the prefix for tables
*
* @return string
*/
final public function getPrefix()
{
return $this->prefix;
}
/**
* Sets a handler for the debug info
*
* @param callable|bool $callback [optional]
* a callback($query, $duration, $info) or TRUE for the standard handler or NULL for disable
*/
final public function setDebug($callback = true)
{
if ($callback === true) {
if (php_sapi_name() === 'cli') {
$callback = new DebuggerOutConsole();
} else {
$callback = new DebuggerOutHtml();
}
}
$this->debugCallback = $callback;
}
/**
* Returns a debug handler
*
* @return callback
*/
final public function getDebug()
{
return $this->debugCallback;
}
/**
* Disables the debug info output
*/
final public function disableDebug()
{
$this->debugCallback = null;
}
/**
* Returns a low-level connection implementation (adapter depended)
*
* @param bool $connect
* force connect
* @return mixed
* an implementation or FALSE if it is not created
* @throws goDBExceptionsConnect
* @throws goDBExceptionsClosed
*/
final public function getImplementationConnection($connect = true)
{
if ($connect && (!$this->connector->isConnected())) {
$this->forcedConnect();
}
return $this->connector->getConnection();
}
/**
* Creates a query by a pattern and an incoming data
*
* @param string $pattern
* a query pattern
* @param array $data
* an incoming data for the pattern
* @param string $prefix
* a prefix for tables
* @return string
* the plain query
* @throws goDBExceptionsTemplater
* an error of templating system
*/
public function makeQuery($pattern, $data, $prefix = null)
{
Compat::setCurrentOpts($this->paramsSys['compat']);
$this->forcedConnect();
if ($prefix === null) {
$prefix = $this->prefix;
}
$templater = $this->createTemplater($pattern, $data, $prefix);
$templater->parse();
return $templater->getQuery();
}
/**
* Returns an object for a table access
*
* @param string $tableName
* a table name
* @param array $map [optional]
* a map (a key => a real table column)
* @return goDBTable
* a "table" instance
*/
public function getTable($tableName, array $map = null)
{
if (($map === null) && (is_string($tableName))) {
if (isset($this->cacheTables[$tableName])) {
$table = $this->cacheTables[$tableName];
} else {
$table = new Table($this, $tableName);
$this->cacheTables[$tableName] = $table;
}
} else {
$table = new Table($this, $tableName, $map);
}
return $table;
}
/**
* The hidden constructor (for instance create use a static method create())
*
* @param array $params
* a database configuration
* @throws goDBExceptionsConnect
* @throws goDBExceptionsConfigConnect
*/
protected function __construct($params)
{
$this->separateParams($params);
$this->connector = $this->createConnector();
$this->setPrefix($this->paramsSys['prefix']);
$this->setDebug($this->paramsSys['debug']);
if (is_array($this->paramsSys['pre'])) {
$this->preQueries = $this->paramsSys['pre'];
}
if (!$this->paramsSys['lazy']) {
$this->connector->connect();
$this->connected = true;
$this->execPre();
}
}
/**
* The destructor
*/
final public function __destruct()
{
$this->connector->close();
$this->connector->removeLink();
$this->connector = null;
}
/**
* Cloning the instance
*/
public function __clone()
{
$this->connector->addLink($this->connected);
}
/**
* Creates an instance for a database connect
*
* @return goDBHelpersConnector
*/
protected function createConnector()
{
return (new Connector($this->paramsSys['adapter'], $this->paramsDB));
}
/**
* Creates a templating for a query
*
* @param string $pattern
* @param array $data
* @param string $prefix
* @return goDBHelpersTemplater
*/
protected function createTemplater($pattern, $data, $prefix)
{
return (new Templater($this->connector, $pattern, $data, $prefix));
}
/**
* Creates an instance for a result representation
*
* @param mixed $cursor
* @return goDBResult
*/
protected function createFetcher($cursor)
{
return (new CursorFetcher($this->connector, $cursor));
}
/**
* Analysis parameters and separates its to system and adapter-depending
*
* @param array $params
* @throws goDBExceptionsConfigSys
*/
protected function separateParams($params)
{
$this->paramsDB = array();
$this->paramsSys = Config::get('configsys');
foreach ($params as $name => $value) {
if (substr($name, 0, 1) === '_') {
$name = substr($name, 1);
if (!array_key_exists($name, $this->paramsSys)) {
throw new ConfigSys('Unknown system param "'.$name.'"');
}
$this->paramsSys[$name] = $value;
} else {
$this->paramsDB[$name] = $value;
}
}
}
/**
* Sends a debug info to the handler
*
* @param string $query
* @param float $duration
* @param mixed $info
*/
protected function debugLog($query, $duration, $info)
{
if ($this->debugCallback) {
call_user_func($this->debugCallback, $query, $duration, $info);
}
}
/**
* Executes all pre-queries
*
* @throws goDBExceptionsException
*/
protected function execPre()
{
foreach ($this->preQueries as $pq) {
if (is_array($pq)) {
$pattern = $pq[0];
$data = isset($pq[1]) ? $pq[1] : null;
} else {
$pattern = $pq;
$data = null;
}
$this->query($pattern, $data);
}
$this->preQueries = array();
}
public $tpassed = 0;
public $query_number=0;
/**
* The list of avaliable adapters (cache)
*
* @var array
*/
private static $availableAdapters;
/**
* The connection instance
*
* @var goDBHelpersConnector
*/
protected $connector;
/**
* System parameters
*
* @var array
*/
protected $paramsSys;
/**
* Connection parameters
*
* @var array
*/
protected $paramsDB;
/**
* The current table prefix
*
* @var string
*/
protected $prefix;
/**
* The debug handler
*
* @var callback
*/
protected $debugCallback;
/**
* Flag: the connection is established
*
* @var bool
*/
protected $connected = false;
/**
* Flag: the connection is closed (hard)
*
* @var bool
*/
protected $hardClosed = false;
/**
* The list of pre-queries
*
* @var array
*/
protected $preQueries = array();
/**
* @var array
*/
protected $cacheTables = array();
}
/**
* @alias goDBDB::create
*
* @param array $params
* @param string $adapter [optional]
* @return goDBDB
* @throws goDBExceptionsConfig
* @throws goDBExceptionsConnect
*/
function create(array $params, $adapter = null)
{
return DB::create($params, $adapter);
}
/**
* @alias goDBStorage::query
*
* @param string $pattern
* @param array $data [optional]
* @param string $fetch [optional]
* @param string $prefix [optional]
* @return goDBResult
* @throws goDBExceptionsStorageDBCentral
* @throws goDBExceptionsConnect
* @throws goDBExceptionsClosed
* @throws goDBExceptionsTemplater
* @throws goDBExceptionsQuery
* @throws goDBExceptionsFetch
*/
function query($pattern, $data = null, $fetch = null, $prefix = null)
{
return Storage::getInstance()->query($pattern, $data, $fetch, $prefix);
}