<?php
/**
* CodeNetwork WebUtilities.
*
* Please note: this is shareware package.
*
* Permission is hereby granted to use and modify this package, provided that:
* - copyright notices are retained unchanged
* - you cannot to redistribute package in any kind, even as free version
*
* THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT WARRANTY. ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE TO ANY PARTY FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THIS PACKAGE.
*
* @author Yumitsu <yumitsu@codenetwork.ru>
* @license CodeNetwork WU SHARE-DEV License
* @package CodeNetwork WebUtilities.
*/
class Application_Database implements Application_Database_Interface
{
const ADAPTER_IBM = 'Pdo_Ibm';
const ADAPTER_MYSQL = 'Pdo_Mysql';
const ADAPTER_MSSQL = 'Pdo_Mssql';
const ADAPTER_ORACLE = 'Pdo_Oci';
const ADAPTER_ORACLE_OCI8 = 'oci8';
const ADAPTER_MYSQLI = 'Mysqli';
private static $__default = 'Zend_Config';
private static $__xml = 'Zend_Config_Xml';
private static $__ini = 'Zend_Config_Ini';
private static $__cachedir = null;
protected $_cache = null;
public static $defaultTableClass = 'Application_Database_Table';
protected static $fetchedTablesList = array();
/**
* @var Application_Config_Default|Application_Config_Xml
*/
private $__config = null;
/**
* @var Zend_Db_Adapter_Abstract
*/
private $__db = null;
/**
* Singleton handler.
*
* @var Application_Db_Driver
*/
private static $instance = null;
private static $__tables = array();
/**
* Constructor.
*
* @param mixed $config_file OPTIONAL; mixed(array, object or string) variable with configuration
* @return void
* @throws Application_Database_Exception
*/
public function __construct($config_file=null)
{
if(defined('PROJECT_TMP_DIR')) {
self::$__cachedir = PROJECT_TMP_DIR;
}
try {
$config = $this->detectAdapter($config_file);
} catch (Factory_Exception $e) {
//Exception class must do anything on his own.
}
try {
$this->__db = Zend_Db::factory($config);
$this->__db->getConnection();
} catch (Zend_Db_Adapter_Exception $e) {
throw new Application_Database_Exception($e);
} catch (Zend_Db_Exception $e) {
throw new Application_Database_Exception($e);
}
if(isset($config->cache) && (int)$config->cache) {
Application_Database_Table::setDefaultMetadataCache(self::createCacheObject());
}
if(isset($config->profiler) && (int)$config->profiler) {
$this->__db->getProfiler()->setEnabled(true);
}
$this->__config = $config;
if(!count(self::$__tables)) {
$this->loadTables();
}
}
/**
* Loads configuration and detects a type of adapter.
*
* @param mixed $config_file
* @return mixed
* @throws Application_Database_Exception
*/
protected function detectAdapter($config_file=null)
{
if(is_null($config_file)) {
$config_file = PROJECT_CONFIG_DIR .'/dbconnect.xml';
$config = new self::$__xml($config_file,'database');
} elseif(is_array($config_file)) {
$config = new self::$__default($config_file);
} elseif($config_file instanceof Application_Config_Default || $config_file instanceof Application_Config_Xml || $config_file instanceof Zend_Config_Xml) {
return $config_file;
} else {
switch (strtolower(substr($config_file,-3))) {
case 'xml':
$config = new self::$__xml($config_file,'database');
break;
case 'ini':
$config = new self::$__ini($config_file,'database');
break;
default:
throw new Application_Database_Exception("Wrong config type",1);
break;
}
}
switch ($config->adapter) {
case self::ADAPTER_IBM:
case self::ADAPTER_MSSQL:
case self::ADAPTER_MYSQL:
case self::ADAPTER_MYSQLI:
case self::ADAPTER_ORACLE:
case self::ADAPTER_ORACLE_OCI8:
break;
default:
throw new Application_Database_Exception("This adapter type not supported",3);
break;
}
return $config;
}
/**
* Fetches table names in variable
* @param none
* @return none
* @throws Application_Database_Exception
*/
protected function loadTables()
{
$cache = $this->getCache();
if(!($tables = $cache->load('db_tables')) || !$cache->test('db_tables')) {
$this->fetchMode(Zend_Db::FETCH_NUM);
$fetch = $this->__db->fetchAll("SHOW TABLES");
if(!count($fetch)) {
throw new Application_Database_Exception("No tables presents.", 4);
}
foreach ($fetch as $value) {
$tables[] = $value[0];
}
$cache->save($tables, 'db_tables');
unset($value);
}
self::$__tables = $tables;
}
/**
* Switches fetch mode.
*
* @param string $mode
*/
public function fetchMode($mode)
{
$this->__db->setFetchMode($mode);
}
/**
* Parses query list from DBProfiler into HTML.
*
* @return string
*/
public function formateProfiledQueries()
{
$string = "";
if(!$this->__db->getProfiler()->getTotalNumQueries(Zend_Db_Profiler::SELECT)) {
return "No queries.";
}
$string .= "Total queries: " . $this->__db->getProfiler()->getTotalNumQueries(Zend_Db_Profiler::SELECT) . "<br /><br />";
foreach($this->__db->getProfiler()->getQueryProfiles(Zend_Db_Profiler::SELECT) as $val) {
$string .= $val->getQuery() . "<br />";
}
return $string;
}
/**
* Searchs for table and returns abstract object
*
* @param mixed $table
* @return Application_Database_Table
* @throws Application_Database_Exception
*/
public function getTable($table)
{
if($table instanceof Zend_Config) $table = $table->persistentName;
$table = $this->makePrefixReplacement($table);
if(isset(self::$fetchedTablesList[$table])) {
return self::$fetchedTablesList[$table];
} else {
$initiate = array(
'db' => $this->__db,
'driver' => $this,
'name' => $table,
);
if(in_array($table, self::$__tables)) {
$table = new self::$defaultTableClass($initiate);
self::$fetchedTablesList[$initiate['name']] = $table;
return $table;
} else {
throw new Application_Database_Exception("Table $table not found in tables list",4);
}
}
}
public function hasTable($table)
{
$table = $this->makePrefixReplacement($table);
return in_array($table, self::$__tables);
}
/**
* @see http://www.php.net/__set
*/
public function __set($name,$value)
{
if(property_exists($this,$name)) {
$this->$name = $value;
} elseif(property_exists($this->__db->$name)) {
$this->__db->$name = $value;
}
}
/**
* @see http://www.php.net/__get
*/
public function __get($name)
{
if(property_exists($this->__db->$name)) {
return $this->__db->$name ;
}
return false;
}
/**
* @see http://www.php.net/__call
* @throws Application_Database_Exception
*/
public function __call($name,$arguments)
{
if(method_exists($this->_getAdapter(),$name)) {
switch($name) {
case 'delete':
$cache = self::getCache();
$cache->getBackend()->___expire('db_tables');
return $this->_getAdapter()->$name($arguments);
break;
default:
return $this->_getAdapter()->$name($arguments);
break;
}
}
throw new Application_Database_Exception("Method '$name' not exists");
}
/**
* Destructor
*/
public function __destruct()
{
$this->__db->closeConnection();
}
/**
* Reloads active connection.
*
*/
public function reloadConnection()
{
$this->__db->closeConnection();
$this->__db->getConnection();
}
/**
* Changes active connection settings.
*
* @param mixed $config_file
* @throws Application_Database_Exception
*/
public function changeConnection($config_file)
{
if($this->__db instanceof Zend_Db_Adapter_Abstract) {
$this->__db->closeConnection();
}
try {
$config = $this->detectAdapter($config_file);
} catch (Exception $e) {
throw new Application_Database_Exception($e);
}
$this->__db = Zend_Db::factory($config);
}
/**
* Creates cache object
*
* @return Zend_Cache_Core
* @throws Application_Database_Exception
*/
public static function createCacheObject()
{
if(is_null(self::$__cachedir)) {
try {
self::redefineCacheDir();
} catch(Exception $e) {
throw new Application_Database_Exception($e);
}
}
return Zend_Cache::factory('Core', 'File', array('automatic_serialization' => true), array('cache_dir' => self::$__cachedir, 'file_name_prefix' => 'DbDriver'));
}
/**
* Redefines cache dir for static callbacks.
*
* @param string $dir
* @throws Application_Database_Exception
*/
private static function redefineCacheDir($dir=null)
{
if(is_null($dir)) {
if(defined('PROJECT_TMP_DIR')) {
self::$__cachedir = PROJECT_TMP_DIR;
} else {
throw new Application_Database_Exception("Cannot redefine cache dir - 'PROJECT_TMP_DIR' not defined and input variable is empty");
}
} else {
self::$__cachedir = $dir;
}
}
/**
* Singleton retriever.
*
* @return Application_Database
*/
public static function getInstance()
{
if(null == self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Returns DB adapter.
*
* @return Zend_Db_Adapter_Abstract
*/
protected function _getAdapter()
{
return $this->__db;
}
/**
* Detects tableprefix.
*
* @return string
* @deprecated
*/
public function getPossiblePrefix()
{
$table = current(self::$__tables);
$table = explode('_',$table);
if(count($table) > 1) {
return $table[0].'_';
}
return false;
}
/**
* Replaces pseudo-prefix(#__) with actual table prefix.
*
* @param string $table
* @return string
* @deprecated
*/
public function makePrefixReplacement($table)
{
if(preg_match("~\#\_\_[a-z0-9]*?~i", $table)) {
$prefix = (false != $this->getPossiblePrefix() ? $this->getPossiblePrefix() : '');
$table = str_replace("#__", $prefix, $table);
}
return $table;
}
/**
* Loads cache object into driver namespace.
* If cache object already loaded returs static object.
*
* @return Zend_Cache_Core
*/
public function getCache()
{
if(!($this->_cache instanceof Zend_Cache_Core)) {
$this->_cache = self::createCacheObject();
}
return $this->_cache;
}
/**
* Return status of cache.
*
* @return integer
*/
public function getCacheStatus()
{
return (int)$this->__config->get('cache');
}
public static function setDefaultTableClass($class)
{
self::$defaultTableClass = $class;
}
public function __sleep()
{
return array('__config');
}
public function __wakeup()
{
$this->changeConnection($this->__config);
}
}