library/PDO.class.php
<?php
/*
* PHP-PDO-MySQL-Class
* https://github.com/lincanbin/PHP-PDO-MySQL-Class
*
* Copyright 2015 Canbin Lin (lincanbin@hotmail.com)
* http://www.94cb.com/
*
* Licensed under the Apache License, Version 2.0:
* http://www.apache.org/licenses/LICENSE-2.0
*
* A PHP MySQL PDO class similar to the the Python MySQLdb.
*/
require(__DIR__ . "/PDO.Log.class.php");
/** Class DB
* @property PDO pdo PDO object
* @property PDOStatement sQuery PDOStatement
* @property logObject PDOLog logObject
*/
class DB
{
private $Host;
private $DBPort;
private $DBName;
private $DBUser;
private $DBPassword;
private $pdo;
private $sQuery;
private $connectionStatus = false;
private $logObject;
private $parameters;
public $rowCount = 0;
public $columnCount = 0;
public $querycount = 0;
private $retryAttempt = 0; // 失败重试次数
const AUTO_RECONNECT = true;
const RETRY_ATTEMPTS = 3; // 最大失败重试次数
public function __construct($Host, $DBPort, $DBName, $DBUser, $DBPassword)
{
$this->logObject = new PDOLog();
$this->Host = $Host;
$this->DBPort = $DBPort;
$this->DBName = $DBName;
$this->DBUser = $DBUser;
$this->DBPassword = $DBPassword;
$this->parameters = array();
$this->Connect();
}
private function Connect()
{
try {
$this->pdo = new PDO('mysql:host=' . $this->Host . ';port=' . $this->DBPort . ';dbname=' . $this->DBName . ';charset=utf8',
$this->DBUser,
$this->DBPassword,
array(
//For PHP 5.3.6 or lower
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8",
PDO::ATTR_EMULATE_PREPARES => false,
//长连接
//PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true
)
);
$this->connectionStatus = true;
}
catch (PDOException $e) {
$this->ExceptionLog($e, '', 'Connect');
}
}
private function SetFailureFlag()
{
$this->pdo = null;
$this->connectionStatus = false;
}
public function CloseConnection()
{
$this->pdo = null;
}
private function Init($query, $parameters = null)
{
if (!$this->connectionStatus) {
$this->Connect();
}
try {
$this->parameters = $parameters;
$this->sQuery = $this->pdo->prepare($this->BuildParams($query, $this->parameters));
if (!empty($this->parameters)) {
if (array_key_exists(0, $parameters)) {
$parametersType = true;
array_unshift($this->parameters, "");
unset($this->parameters[0]);
} else {
$parametersType = false;
}
foreach ($this->parameters as $column => $value) {
$this->sQuery->bindParam($parametersType ? intval($column) : ":" . $column, $this->parameters[$column]); //It would be query after loop end(before 'sQuery->execute()').It is wrong to use $value.
}
}
$this->sQuery->execute();
$this->querycount++;
}
catch (PDOException $e) {
$this->ExceptionLog($e, $this->BuildParams($query), 'Init', array('query' => $query, 'parameters' => $parameters));
}
$this->parameters = array();
}
private function BuildParams($query, $params = null)
{
if (!empty($params)) {
$rawStatement = explode(" ", $query);
foreach ($rawStatement as $value) {
if (strtolower($value) == 'in') {
return str_replace("(?)", "(" . implode(",", array_fill(0, count($params), "?")) . ")", $query);
}
}
}
return $query;
}
public function beginTransaction()
{
return $this->pdo->beginTransaction();
}
public function commit()
{
return $this->pdo->commit();
}
public function rollBack()
{
return $this->pdo->rollBack();
}
public function inTransaction()
{
return $this->pdo->inTransaction();
}
public function query($query, $params = null, $fetchmode = PDO::FETCH_ASSOC)
{
$query = trim($query);
$rawStatement = explode(" ", $query);
$this->Init($query, $params);
$statement = strtolower($rawStatement[0]);
if ($statement === 'select' || $statement === 'show') {
return $this->sQuery->fetchAll($fetchmode);
} elseif ($statement === 'insert' || $statement === 'update' || $statement === 'delete') {
return $this->sQuery->rowCount();
} else {
return NULL;
}
}
public function lastInsertId()
{
return $this->pdo->lastInsertId();
}
public function column($query, $params = null)
{
$this->Init($query, $params);
$resultColumn = $this->sQuery->fetchAll(PDO::FETCH_COLUMN);
$this->sQuery->closeCursor();
return $resultColumn;
}
public function row($query, $params = null, $fetchmode = PDO::FETCH_ASSOC)
{
$this->Init($query, $params);
$resultRow = $this->sQuery->fetch($fetchmode);
$this->sQuery->closeCursor();
return $resultRow;
}
public function single($query, $params = null)
{
$this->Init($query, $params);
return $this->sQuery->fetchColumn();
}
private function ExceptionLog(PDOException $e, $sql = "", $method = '', $parameters = array())
{
$message = $e->getMessage();
$exception = 'Unhandled Exception. <br />';
$exception .= $message;
$exception .= "<br /> You can find the error back in the log.";
if (!empty($sql)) {
$message .= "\r\nRaw SQL : " . $sql;
}
$this->logObject->write($message, $this->DBName . md5($this->DBPassword));
if (
self::AUTO_RECONNECT
&& $this->retryAttempt < self::RETRY_ATTEMPTS
&& stripos($message, 'server has gone away') !== false
&& !empty($method)
&& !$this->inTransaction()
) {
$this->SetFailureFlag();
$this->retryAttempt ++;
$this->logObject->write('Retry ' . $this->retryAttempt . ' times', $this->DBName . md5($this->DBPassword));
call_user_func_array(array($this, $method), $parameters);
} else {
if ($this->pdo === null || !$this->inTransaction()) {
//Prevent search engines to crawl
header("HTTP/1.1 500 Internal Server Error");
header("Status: 500 Internal Server Error");
echo $exception;
exit();
} else {
throw $e;
}
}
}
}