Файл: core/database.php
Строк: 488
<?php
final class PEEP_Database
{
//const DEFAULT_CACHE_LIFETIME = false;
const NO_CACHE_ENTRY = "peep_db_no_cache_entry";
/**
* @var array
*/
private static $classInstances;
/**
* Mysql connection object
*
* @var PDO
*/
private $connection;
/**
* Number of rows affected by the last SQL statement
*
* @var int
*/
private $affectedRows;
/**
* Logger data
*
* @var array
*/
private $queryLog;
/**
* Debug option
*
* @var boolean
*/
private $debugMode;
/**
* Enter description here...
*
* @var boolean
*/
private $isProfilerEnabled;
/**
* Enter description here...
*
* @var UTIL_Profiler
*/
private $profiler;
/**
* Last executed query
*
* @var int
*/
private $queryExecTime;
/**
* Enter description here...
*
* @var int
*/
private $totalQueryExecTime;
/**
*
* @var int
*/
private $queryCount;
/**
* @var boolean
*/
private $useCashe;
/**
* Getter for $log property
*
* @return array
*/
public function getQueryLog()
{
return $this->queryLog;
}
/**
* Getter for $queryExecTime property
*
* @return int
*/
public function getQueryExecTime()
{
return $this->queryExecTime;
}
/**
* @return int
*/
public function getTotalQueryExecTime()
{
return $this->totalQueryExecTime;
}
/**
* @return int
*/
public function getQueryCount()
{
return $this->queryCount;
}
/**
* @return string
*/
public function getQueryString()
{
return $this->queryString;
}
/**
* @return bool
*/
public function getUseCashe()
{
return $this->useCashe;
}
/**
* @param bool $useCashe
*/
public function setUseCashe( $useCashe )
{
$this->useCashe = (bool) $useCashe;
}
/**
* Constructor.
*
* @param array $params
*/
private function __construct( $params )
{
$port = isset($params['port']) ? (int) $params['port'] : null;
$socket = isset($params['socket']) ? $params['socket'] : null;
try
{
if ( $socket === null )
{
$dsn = "mysql:host={$params['host']};";
if ( $port !== null )
{
$dsn .= "port={$params['port']};";
}
}
else
{
$dsn = "mysql:unix_socket={$socket};";
}
$dsn .= "dbname={$params['dbname']}";
$this->connection = new PDO($dsn, $params['username'], $params['password'], array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8',
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true));
if ( !$this->isMysqlValidVersion() )
{
throw new InvalidArgumentException("Cant connect to database. Connection needs MySQL version 5.0 + !");
}
if ( !empty($params['profilerEnable']) )
{
$this->isProfilerEnabled = true;
$this->profiler = UTIL_Profiler::getInstance('db');
$this->queryCount = 0;
$this->queryExecTime = 0;
$this->totalQueryExecTime = 0;
$this->queryLog = array();
}
if ( !empty($params['debugMode']) )
{
$this->debugMode = true;
}
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->useCashe = false;
}
catch ( PDOException $e )
{
throw new InvalidArgumentException($e->getMessage());
}
}
/**
* Returns the instance of class for $params
*
* @param array $params ( host, username, password, dbname, [socket], [port] )
*
* @return PEEP_Database
*
*/
public static function getInstance( $params )
{
if ( !isset(self::$classInstances) )
{
self::$classInstances = array();
}
ksort($params);
$connectionKey = serialize($params);
if ( empty(self::$classInstances[$connectionKey]) )
{
if ( !isset($params['host']) || !isset($params['username']) || !isset($params['password']) || !isset($params['dbname']) )
{
throw new InvalidArgumentException("Can't connect to database. Please provide valid connection attributes.");
}
self::$classInstances[$connectionKey] = new self($params);
}
return self::$classInstances[$connectionKey];
}
/**
* @param string $sql
* @param array $params
* @param int $cacheLifeTime
* @param array $tags
* @return mixed
*/
public function queryForColumn( $sql, array $params = null, $cacheLifeTime = 0, $tags = array() )
{
$dataFromCache = $this->getFromCache($sql, $params, $cacheLifeTime);
if ( $dataFromCache !== self::NO_CACHE_ENTRY )
{
return $dataFromCache;
}
$stmt = $this->execute($sql, $params);
$result = $stmt->fetchColumn(); // (PDO::FETCH_COLUMN);
$stmt->closeCursor();
if ( $result === false )
{
$result = null;
}
$this->saveToCache($result, $sql, $params, $cacheLifeTime, $tags);
return $result;
}
/**
* Enter description here...
*
* @param string $sql
* @param unknown_type $className
* @param array $params
* @return mixed
*/
public function queryForObject( $sql, $className, array $params = null, $cacheLifeTime = 0, $tags = array() )
{
$dataFromCache = $this->getFromCache($sql, $params, $cacheLifeTime);
if ( $dataFromCache !== self::NO_CACHE_ENTRY )
{
return $dataFromCache;
}
$stmt = $this->execute($sql, $params);
$stmt->setFetchMode(PDO::FETCH_CLASS, $className);
$result = $stmt->fetch();
$stmt->closeCursor();
if ( $result === false )
{
$result = null;
}
else
{
$result->generateFieldsHash();
}
$this->saveToCache($result, $sql, $params, $cacheLifeTime, $tags);
return $result;
}
/**
*
* @param string $sql
* @param string $className
* @param array $params
* @param int $cacheLifeTime
* @param array $tags
* @return array
*/
public function queryForObjectList( $sql, $className, array $params = null, $cacheLifeTime = 0, $tags = array() )
{
$dataFromCache = $this->getFromCache($sql, $params, $cacheLifeTime);
if ( $dataFromCache !== self::NO_CACHE_ENTRY )
{
return $dataFromCache;
}
$stmt = $this->execute($sql, $params);
$result = $stmt->fetchAll(PDO::FETCH_CLASS, $className);
foreach ( $result as $item )
{
$item->generateFieldsHash();
}
$this->saveToCache($result, $sql, $params, $cacheLifeTime, $tags);
return $result;
}
/**
* @param string $sql
* @param array $params
* @param int $cacheLifeTime
* @param array $tags
* @return array
*/
public function queryForRow( $sql, array $params = null, $cacheLifeTime = 0, $tags = array() )
{
$dataFromCache = $this->getFromCache($sql, $params, $cacheLifeTime);
if ( $dataFromCache !== self::NO_CACHE_ENTRY )
{
return $dataFromCache;
}
$stmt = $this->execute($sql, $params);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
if ( $result === false )
{
$result = array();
}
$this->saveToCache($result, $sql, $params, $cacheLifeTime, $tags);
return $result;
}
/**
* Enter description here...
*
* @param string $sql
* @param array $params
* @return array
*/
public function queryForList( $sql, array $params = null, $cacheLifeTime = 0, $tags = array() )
{
$dataFromCache = $this->getFromCache($sql, $params, $cacheLifeTime);
if ( $dataFromCache !== self::NO_CACHE_ENTRY )
{
return $dataFromCache;
}
$stmt = $this->execute($sql, $params);
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
$this->saveToCache($result, $sql, $params, $cacheLifeTime, $tags);
return $result;
}
/**
* @param string $sql
* @param array $params
* @param int $cacheLifeTime
* @param array $tags
* @return type
*/
public function queryForColumnList( $sql, array $params = null, $cacheLifeTime = 0, $tags = array() )
{
$dataFromCache = $this->getFromCache($sql, $params, $cacheLifeTime);
if ( $dataFromCache !== self::NO_CACHE_ENTRY )
{
return $dataFromCache;
}
$stmt = $this->execute($sql, $params);
$result = $stmt->fetchAll(PDO::FETCH_COLUMN);
$this->saveToCache($result, $sql, $params, $cacheLifeTime, $tags);
return $result;
}
/**
* Enter description here...
*
* @param string $sql
* @param array $params
* @return int
*/
public function query( $sql, array $params = null )
{
$stmt = $this->execute($sql, $params);
$rowCount = $stmt->rowCount();
$stmt->closeCursor();
return $rowCount;
}
/**
* @param type $sql
* @param array $params
* @return int
*/
public function delete( $sql, array $params = null )
{
return $this->query($sql, $params);
}
/**
* insert data and return last insert id
*
* @param string $sql
* @param array $params
* @return int last_insert_id
*/
public function insert( $sql, array $params = null )
{
$stmt = $this->execute($sql, $params);
$lastInsertId = $this->connection->lastInsertId();
$stmt->closeCursor();
return $lastInsertId;
}
/**
* Enter description here...
*
* @param unknown_type $sql
* @param array $params
* @return unknown
*/
public function update( $sql, array $params = null )
{
return $this->query($sql, $params);
}
/**
* Insert object $obj to table $tableName. Returns last_insert_id
* throws InvalidArgumentException
*
* @param string $tableName
* @param object $obj
* @return int
*/
public function insertObject( $tableName, $obj, $delayed = false )
{
if ( $obj != null && is_object($obj) )
{
$params = get_object_vars($obj);
$paramNames = array_keys($params);
$columns = UTIL_String::arrayToDelimitedString($paramNames, ',', '`', '`');
$values = UTIL_String::arrayToDelimitedString($paramNames, ',', ':');
$sql = "INSERT" . ($delayed ? " DELAYED" : "") . " INTO `{$tableName}` ({$columns}) VALUES ({$values})";
return $this->insert($sql, $params);
}
else
{
throw new InvalidArgumentException('object expected');
}
}
/**
* @param string $tableName
* @param PEEP_Entity $obj
* @param string $primaryKeyName
* @return type
* @throws InvalidArgumentException
*/
public function updateObject( $tableName, $obj, $primaryKeyName = 'id', $lowPriority = false )
{
if ( $obj != null && is_object($obj) )
{
$params = get_object_vars($obj);
if ( !array_key_exists($primaryKeyName, $params) )
{
throw new InvalidArgumentException('object property not found');
}
$fieldsToUpdate = $obj->getEntinyUpdatedFields();
if ( empty($fieldsToUpdate) )
{
return true;
}
$updateArray = array();
foreach ( $params as $key => $value )
{
if ( $key !== $primaryKeyName )
{
if ( in_array($key, $fieldsToUpdate) )
{
$updateArray[] = '`' . $key . '`=:' . $key;
}
else
{
unset($params[$key]);
}
}
}
$updateStmt = UTIL_String::arrayToDelimitedString($updateArray);
$sql = "UPDATE" . ($lowPriority ? " LOW_PRIORITY" : "") . " `{$tableName}` SET {$updateStmt} WHERE {$primaryKeyName}=:{$primaryKeyName}";
return $this->update($sql, $params);
}
else
{
throw new InvalidArgumentException('object expected');
}
}
public function mergeInClause( array $valueList )
{
if ( $valueList === null )
{
return '';
}
$result = '';
foreach ( $valueList as $value )
{
$result .= ( ''' . $this->escapeString($value) . '',' ); //"'$value',"
}
$result = mb_substr($result, 0, mb_strlen($result) - 1);
return $result;
}
public function batchInsertOrUpdateObjectList( $tableName, $objects, $batchSize = 50 )
{
if ( $objects != null && is_array($objects) )
{
if ( count($objects) > 0 )
{
$columns = '';
$paramNames = array();
if ( is_object($objects[0]) )
{
$params = get_object_vars($objects[0]);
$paramNames = array_keys($params);
$columns = UTIL_String::arrayToDelimitedString($paramNames, ',', '`', '`');
}
else
{
throw new InvalidArgumentException('Array of objects expected');
}
$i = 0;
$totalInsertsCount = 0;
$objectsCount = count($objects);
$batchSize = (int) $batchSize;
$inserts = array();
foreach ( $objects as $obj )
{
$values = '(';
foreach ( $paramNames as $property )
{
if ( $obj->$property !== null )
{
$values .= ( ''' . $this->escapeString($obj->$property) . '',' );
}
else
{
$values .= 'NULL,';
}
}
$values = mb_substr($values, 0, mb_strlen($values) - 1);
$values .= ')';
$inserts[] = $values;
$i++;
$totalInsertsCount++;
if ( $i === $batchSize || $totalInsertsCount === $objectsCount )
{
$sql = "REPLACE INTO `{$tableName}` ({$columns}) VALUES" . implode(',', $inserts);
$inserts = array();
$i = 0;
$this->execute($sql)->closeCursor();
//$this->connection->query($sql)->closeCursor();
}
}
}
}
else
{
throw new InvalidArgumentException('Array expected');
}
}
/**
* Escapes SQL string
*
* @param string $string
* @return string
*/
public function escapeString( $string )
{
$quotedString = $this->connection->quote($string); // real_escape_string( $string );
return mb_substr($quotedString, 1, mb_strlen($quotedString) - 2); //dirty hack to delete quotes
}
/* * 206.123.0
* Returns affected rows
*
* @return integer
*/
public function getAffectedRows()
{
return $this->affectedRows;
}
/**
* Returns last insert id
*
* @return integer
*/
public function getInsertId( $seqname = null )
{
return $this->connection->lastInsertId($seqname);
}
/**
* Class destruct actions
*/
public function __destruct()
{
if ( isset($this->connection) )
{
$this->connection = null;
}
}
/**
* Returns current PDOStatement
*
* @return PDOStatement
*/
private function execute( $sql, array $params = null )
{
if ( $this->isProfilerEnabled )
{
$this->profiler->reset();
}
/* @var $stmt PDOStatement */
$stmt = $this->connection->prepare($sql);
if ( $params !== null )
{
foreach ( $params as $key => $value )
{
$paramType = PDO::PARAM_STR;
if ( is_int($value) )
$paramType = PDO::PARAM_INT;
elseif ( is_bool($value) )
$paramType = PDO::PARAM_BOOL;
$stmt->bindValue(is_int($key) ? $key + 1 : $key, $value, $paramType);
}
}
PEEP::getEventManager()->trigger(new PEEP_Event("core.sql.exec_query", array("sql" => $sql, "params" => $params)));
$stmt->execute(); //TODO setup profiler
$this->affectedRows = $stmt->rowCount();
if ( $this->isProfilerEnabled )
{
$this->queryExecTime = $this->profiler->getTotalTime();
$this->totalQueryExecTime += $this->queryExecTime;
$this->queryCount++;
$this->queryLog[] = array('query' => $sql, 'execTime' => $this->queryExecTime, 'params' => $params);
}
return $stmt;
}
/**
* Check if MySQL version is 5+
*
* @return boolean
*/
private function isMysqlValidVersion()
{
$verArray = explode('.', $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION));
return $verArray[0] == 5;
}
private function getCacheKeyForQuery( $query, $params )
{
return 'core.sql.' . md5(trim($query) . serialize($params));
}
private function cacheEnabled( $expTime )
{
return !PEEP_DEV_MODE && $this->useCashe && ( $expTime === false || $expTime > 0 );
}
/**
* @return PEEP_CacheManager
*/
private function getCacheManager()
{
return PEEP::getCacheManager();
}
private function getFromCache( $sql, $params, $cacheLifeTime )
{
if ( $this->cacheEnabled($cacheLifeTime) )
{
$cacheKey = $this->getCacheKeyForQuery($sql, $params ? $params : array());
$cacheData = $this->getCacheManager()->load($cacheKey);
if ( $cacheData !== null )
{
return unserialize($cacheData);
}
}
$data = PEEP::getEventManager()->call("core.sql.get_query_result", array("sql" => $sql, "params" => $params));
if ( is_array($data) && isset($data["result"]) && $data["result"] === true )
{
return $data["value"];
}
return self::NO_CACHE_ENTRY;
}
private function saveToCache( $result, $sql, $params, $cacheLifeTime, $tags )
{
if ( $this->cacheEnabled($cacheLifeTime) )
{
$cacheKey = $this->getCacheKeyForQuery($sql, $params ? $params : array());
$this->getCacheManager()->save(serialize($result), $cacheKey, $tags, $cacheLifeTime);
}
PEEP::getEventManager()->trigger(new PEEP_Event("core.sql.set_query_result", array("sql" => $sql, "params" => $params, "result" => $result)));
}
}