qcminecraft/Carbon-Forum-F

View on GitHub
library/PDO.class.php

Summary

Maintainability
A
3 hrs
Test Coverage
<?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;
            }
        }
    }
}