// lib/DatabaseException.php
<?php
namespace Database;
/**
* Class DatabaseException
* This is the exception class that will be thrown whenever a PDOException occurs.
* The exception will have the prepared statement with the values inside.
*
* @package Database
*/
class DatabaseException extends \PDOException {
protected $sql_code = null;
/**
*
* @param string $message
* @param null $sql_code
* @param null $prepare
* @param null|\PDOException $previous_exception
*/
public function __construct($message = "", $sql_code = null, $prepare = null, \PDOException $previous_exception = null){
if(is_array($prepare)){
foreach($prepare as $key => $value) {
$sql_code = str_replace($key, "'".addslashes($value)."'", $sql_code);
}
}
$this->sql_code = $sql_code;
parent::__construct($message.$this->getTrace()." --- \n [ Query: [ ".$this->sql_code." ] ]", ($previous_exception && is_int($previous_exception->getCode()) ? $previous_exception->getCode() : 0));
}
}
?>
// lib/QueryStatement.php
<?php
namespace Database;
/**
* Class QueryStatement
* This class's purpose is to extend \PDOStatement, save the prepared statement's data
* and trigger DatabaseException exceptions on errors.
*
* @package Database
*/
class QueryStatement extends \PDOStatement {
/**
* This variable holds all of the bindParam/bindColumn/bindValue values
* @var array
*/
protected $values = array();
protected function __construct() {
// Set the default fetch mode to \PDO::FETCH_ASSOC
$this->setFetchMode( \PDO::FETCH_ASSOC );
}
/**
* Overwrite the default \PDOStatement::bindParam so that the param & variables are stored in $this->values
*
* @param mixed $parameter
* @param mixed $value
* @param int $data_type
*
* @return bool|void
* @throws DatabaseException
*/
public function bindValue($parameter, $value, $data_type = \PDO::PARAM_STR){
try {
$this->values[$parameter] = $value;
parent::bindValue($parameter, $value, $data_type);
} catch(\PDOException $e) {
throw new DatabaseException($e->getMessage(), $this->queryString, $this->values, $e);
}
}
public function execute(array $input_parameters = null){
try {
if($input_parameters != null)
$this->values = array_merge($input_parameters, $this->values);
parent::execute($input_parameters);
} catch(\PDOException $e) {
throw new DatabaseException($e->getMessage(), $this->queryString, $this->values, $e);
}
}
}
?>
// lib/Database.php
<?php
namespace Database;
require_once('DatabaseException.php');
require_once('QueryStatement.php');
use Database\QueryStatement;
use Database\DatabaseException;
/**
* Class Database
* This is a wrapper class - it's like a proxy to the default PDO methods, but
* the methods have a try/catch block. In case a PDOException is thrown, this will
* trigger a DatabaseException where the prepared query will be visible with the
* prepared values.
*
* @package Database
*/
class Database extends \PDO {
/**
* Initialize database connection
*
* @param $dsn
* @param $user (optional - in some drivers you can define the user&pass within the dsn string)
* @param $pass (optional - in some drivers you can define the user&pass within the dsn string)
*
* @throws DatabaseException
*/
public function __construct( $dsn, $user = null, $pass = null ) {
if ( $dsn ) {
try {
parent::__construct( $dsn, $user, $pass );
$this->setAttribute( \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION );
$this->setAttribute( \PDO::ATTR_STATEMENT_CLASS, array('Database\QueryStatement', array($this)) );
} catch ( \PDOException $e ) {
throw new DatabaseException( "Could not connect to db!", 'Not available!', null, $e );
}
}
else {
throw new DatabaseException( 'Connection to database cannot be established! Missing parameters!' );
}
}
public function prepare($statement, array $driver_options = array()){
try {
return parent::prepare( $statement, $driver_options );
} catch ( \PDOException $e ) {
throw new DatabaseException( $e->getMessage(), $statement, null, $e );
}
}
public function exec($statement){
try {
return parent::exec( $statement );
} catch ( \PDOException $e ) {
throw new DatabaseException( $e->getMessage(), $statement, null, $e );
}
}
}
?>
这是一个示例用法:
// a_script_that_uses_db_connection.php
<?php
// Example usage.
require_once('lib/Database.php');
use Database\Database;
/**
* Enter a valid DSN connection string. The strings for all supported databases
* can be found at http://php.net/manual/en/pdo.drivers.php
*
* Most used DSN strings
*
* MySQL DSN string:
* mysql:host=localhost;port=3306;dbname=testdb
* OR
* mysql:unix_socket=/tmp/mysql.sock;dbname=testdb
* (http://php.net/manual/en/ref.pdo-mysql.connection.php)
*
* PostgreSQL DSN string:
* pgsql:host=localhost;port=5432;dbname=testdb;user=bruce;password=mypass
* (http://php.net/manual/en/ref.pdo-pgsql.connection.php)
*
* SQLite DSN string:
* sqlite:/opt/databases/mydb.sq3
* sqlite::memory:
* sqlite2:/opt/databases/mydb.sq2
* sqlite2::memory:
* (http://php.net/manual/en/ref.pdo-sqlite.connection.php)
*
*/
$connect_string = "pgsql:host=127.0.0.1;port=5432;dbname=testing_database";
$db = new Database($connect_string, 'my_username', 'my_password');
// Create a prepared statement
$ps = $db->prepare('SELECT * FROM users WHERE username = :username');
$ps->bindValue(':username', 'admin');
$ps->execute();
$ps->fetchAll(PDO::FETCH_ASSOC);
// An invalid query (i.e. the query throws PDOException)
$ps = $db->prepare('SELECT * FROM users WHERE usernamee = :username');
$ps->bindValue(':username', 'admin');
$ps->execute();
$ps->fetchAll(PDO::FETCH_ASSOC);
// The result will be a DatabaseException with the following message
// Database\DatabaseException: SQLSTATE[42703]: Undefined column: 7 ERROR: column "usernamee" does not exist LINE 1: SELECT * FROM users WHERE usernamee = $1 ^ --- [ Query: [ SELECT * FROM users WHERE usernamee = 'admin' ] ] in /path/to/lib/QueryStatement.php on line 51
// and a stack trace which will show you exactly where the query was executed.
?>
几乎这是一个扩展类的包装PDO
类。如果 aPDOException
在thrown
准备好的语句中,则新DatabaseException
的带有实际查询。
你可以从这里用 git 克隆代码
希望这可以帮助您和所有其他试图解决这个问题的开发人员。