miniflux-legacy/vendor/fguillot/picodb/lib/PicoDb/StatementHandler.php

354 lines
6.9 KiB
PHP

<?php
namespace PicoDb;
use PDO;
use PDOException;
use PDOStatement;
/**
* Statement Handler
*
* @package PicoDb
* @author Frederic Guillot
*/
class StatementHandler
{
/**
* Database instance
*
* @access protected
* @var Database
*/
protected $db = null;
/**
* Flag to calculate query time
*
* @access protected
* @var boolean
*/
protected $stopwatch = false;
/**
* Start time
*
* @access protected
* @var float
*/
protected $startTime = 0;
/**
* Execution time of all queries
*
* @access protected
* @var float
*/
protected $executionTime = 0;
/**
* Flag to log generated SQL queries
*
* @access protected
* @var boolean
*/
protected $logQueries = false;
/**
* Run explain command on each query
*
* @access protected
* @var boolean
*/
protected $explain = false;
/**
* Number of SQL queries executed
*
* @access protected
* @var integer
*/
protected $nbQueries = 0;
/**
* SQL query
*
* @access protected
* @var string
*/
protected $sql = '';
/**
* Positional SQL parameters
*
* @access protected
* @var array
*/
protected $positionalParams = array();
/**
* Named SQL parameters
*
* @access protected
* @var array
*/
protected $namedParams = array();
/**
* Flag to use named params
*
* @access protected
* @var boolean
*/
protected $useNamedParams = false;
/**
* LOB params
*
* @access protected
* @var array
*/
protected $lobParams = array();
/**
* Constructor
*
* @access public
* @param Database $db
*/
public function __construct(Database $db)
{
$this->db = $db;
}
/**
* Enable query logging
*
* @access public
* @return $this
*/
public function withLogging()
{
$this->logQueries = true;
return $this;
}
/**
* Record query execution time
*
* @access public
* @return $this
*/
public function withStopWatch()
{
$this->stopwatch = true;
return $this;
}
/**
* Execute explain command on query
*
* @access public
* @return $this
*/
public function withExplain()
{
$this->explain = true;
return $this;
}
/**
* Set SQL query
*
* @access public
* @param string $sql
* @return $this
*/
public function withSql($sql)
{
$this->sql = $sql;
return $this;
}
/**
* Set positional parameters
*
* @access public
* @param array $params
* @return $this
*/
public function withPositionalParams(array $params)
{
$this->positionalParams = $params;
return $this;
}
/**
* Set named parameters
*
* @access public
* @param array $params
* @return $this
*/
public function withNamedParams(array $params)
{
$this->namedParams = $params;
$this->useNamedParams = true;
return $this;
}
/**
* Bind large object parameter
*
* @access public
* @param $name
* @param $fp
* @return $this
*/
public function withLobParam($name, &$fp)
{
$this->lobParams[$name] =& $fp;
return $this;
}
/**
* Get number of queries executed
*
* @access public
* @return int
*/
public function getNbQueries()
{
return $this->nbQueries;
}
/**
* Execute a prepared statement
*
* Note: returns false on duplicate keys instead of SQLException
*
* @access public
* @return PDOStatement|false
*/
public function execute()
{
try {
$this->beforeExecute();
$pdoStatement = $this->db->getConnection()->prepare($this->sql);
$this->bindParams($pdoStatement);
$pdoStatement->execute();
$this->afterExecute();
return $pdoStatement;
} catch (PDOException $e) {
return $this->handleSqlError($e);
}
}
/**
* Bind parameters to PDOStatement
*
* @access protected
* @param PDOStatement $pdoStatement
*/
protected function bindParams(PDOStatement $pdoStatement)
{
$i = 1;
foreach ($this->lobParams as $name => $variable) {
if (! $this->useNamedParams) {
$parameter = $i;
$i++;
} else {
$parameter = $name;
}
$pdoStatement->bindParam($parameter, $variable, PDO::PARAM_LOB);
}
foreach ($this->positionalParams as $value) {
$pdoStatement->bindValue($i, $value, PDO::PARAM_STR);
$i++;
}
foreach ($this->namedParams as $name => $value) {
$pdoStatement->bindValue($name, $value, PDO::PARAM_STR);
}
}
/**
* Method executed before query execution
*
* @access protected
*/
protected function beforeExecute()
{
if ($this->logQueries) {
$this->db->setLogMessage($this->sql);
}
if ($this->stopwatch) {
$this->startTime = microtime(true);
}
}
/**
* Method executed after query execution
*
* @access protected
*/
protected function afterExecute()
{
if ($this->stopwatch) {
$duration = microtime(true) - $this->startTime;
$this->executionTime += $duration;
$this->db->setLogMessage('query_duration='.$duration);
$this->db->setLogMessage('total_execution_time='.$this->executionTime);
}
if ($this->explain) {
$this->db->setLogMessages($this->db->getDriver()->explain($this->sql, $this->positionalParams));
}
$this->nbQueries++;
$this->cleanup();
}
/**
* Reset internal properties after execution
* The same object instance is used
*
* @access protected
*/
protected function cleanup()
{
$this->sql = '';
$this->useNamedParams = false;
$this->positionalParams = array();
$this->namedParams = array();
$this->lobParams = array();
}
/**
* Handle PDOException
*
* @access public
* @param PDOException $e
* @return bool
* @throws SQLException
*/
public function handleSqlError(PDOException $e)
{
$this->cleanup();
$this->db->cancelTransaction();
$this->db->setLogMessage($e->getMessage());
if ($this->db->getDriver()->isDuplicateKeyError($e->getCode())) {
return false;
}
throw new SQLException('SQL error'.($this->logQueries ? ': '.$e->getMessage() : ''));
}
}