+++ /dev/null
-<?php
-
-namespace RedBeanPHP\Driver;
-
-use RedBeanPHP\Driver as Driver;
-use RedBeanPHP\Logger as Logger;
-use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
-use RedBeanPHP\RedException as RedException;
-use RedBeanPHP\RedException\SQL as SQL;
-use RedBeanPHP\Logger\RDefault as RDefault;
-use RedBeanPHP\PDOCompatible as PDOCompatible;
-use RedBeanPHP\Cursor\PDOCursor as PDOCursor;
-
-/**
- * PDO Driver
- * This Driver implements the RedBean Driver API.
- * for RedBeanPHP. This is the standard / default database driver
- * for RedBeanPHP.
- *
- * @file RedBeanPHP/PDO.php
- * @author Gabor de Mooij and the RedBeanPHP Community, Desfrenes
- * @license BSD/GPLv2
- *
- * @copyright
- * copyright (c) Desfrenes & Gabor de Mooij and the RedBeanPHP community
- * This source file is subject to the BSD/GPLv2 License that is bundled
- * with this source code in the file license.txt.
- */
-class RPDO implements Driver
-{
- /**
- * @var integer
- */
- protected $max;
-
- /**
- * @var string
- */
- protected $dsn;
-
- /**
- * @var boolean
- */
- protected $loggingEnabled = FALSE;
-
- /**
- * @var Logger
- */
- protected $logger = NULL;
-
- /**
- * @var PDO
- */
- protected $pdo;
-
- /**
- * @var integer
- */
- protected $affectedRows;
-
- /**
- * @var integer
- */
- protected $resultArray;
-
- /**
- * @var array
- */
- protected $connectInfo = array();
-
- /**
- * @var boolean
- */
- protected $isConnected = FALSE;
-
- /**
- * @var bool
- */
- protected $flagUseStringOnlyBinding = FALSE;
-
- /**
- * @var integer
- */
- protected $queryCounter = 0;
-
- /**
- * @var string
- */
- protected $mysqlEncoding = '';
-
- /**
- * Binds parameters. This method binds parameters to a PDOStatement for
- * Query Execution. This method binds parameters as NULL, INTEGER or STRING
- * and supports both named keys and question mark keys.
- *
- * @param PDOStatement $statement PDO Statement instance
- * @param array $bindings values that need to get bound to the statement
- *
- * @return void
- */
- protected function bindParams( $statement, $bindings )
- {
- foreach ( $bindings as $key => &$value ) {
- if ( is_integer( $key ) ) {
- if ( is_null( $value ) ) {
- $statement->bindValue( $key + 1, NULL, \PDO::PARAM_NULL );
- } elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && abs( $value ) <= $this->max ) {
- $statement->bindParam( $key + 1, $value, \PDO::PARAM_INT );
- } else {
- $statement->bindParam( $key + 1, $value, \PDO::PARAM_STR );
- }
- } else {
- if ( is_null( $value ) ) {
- $statement->bindValue( $key, NULL, \PDO::PARAM_NULL );
- } elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && abs( $value ) <= $this->max ) {
- $statement->bindParam( $key, $value, \PDO::PARAM_INT );
- } else {
- $statement->bindParam( $key, $value, \PDO::PARAM_STR );
- }
- }
- }
- }
-
- /**
- * This method runs the actual SQL query and binds a list of parameters to the query.
- * slots. The result of the query will be stored in the protected property
- * $rs (always array). The number of rows affected (result of rowcount, if supported by database)
- * is stored in protected property $affectedRows. If the debug flag is set
- * this function will send debugging output to screen buffer.
- *
- * @param string $sql the SQL string to be send to database server
- * @param array $bindings the values that need to get bound to the query slots
- * @param array $options
- *
- * @return mixed
- * @throws SQL
- */
- protected function runQuery( $sql, $bindings, $options = array() )
- {
- $this->connect();
- if ( $this->loggingEnabled && $this->logger ) {
- $this->logger->log( $sql, $bindings );
- }
- try {
- if ( strpos( 'pgsql', $this->dsn ) === 0 ) {
- if ( defined( '\PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT' ) ) {
- $statement = $this->pdo->prepare( $sql, array( \PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => TRUE ) );
- } else {
- $statement = $this->pdo->prepare( $sql );
- }
- } else {
- $statement = $this->pdo->prepare( $sql );
- }
- $this->bindParams( $statement, $bindings );
- $statement->execute();
- $this->queryCounter ++;
- $this->affectedRows = $statement->rowCount();
- if ( $statement->columnCount() ) {
- $fetchStyle = ( isset( $options['fetchStyle'] ) ) ? $options['fetchStyle'] : NULL;
- if ( isset( $options['noFetch'] ) && $options['noFetch'] ) {
- $this->resultArray = array();
- return $statement;
- }
- $this->resultArray = $statement->fetchAll( $fetchStyle );
- if ( $this->loggingEnabled && $this->logger ) {
- $this->logger->log( 'resultset: ' . count( $this->resultArray ) . ' rows' );
- }
- } else {
- $this->resultArray = array();
- }
- } catch ( \PDOException $e ) {
- //Unfortunately the code field is supposed to be int by default (php)
- //So we need a property to convey the SQL State code.
- $err = $e->getMessage();
- if ( $this->loggingEnabled && $this->logger ) $this->logger->log( 'An error occurred: ' . $err );
- $exception = new SQL( $err, 0 );
- $exception->setSQLState( $e->getCode() );
- throw $exception;
- }
- }
-
- /**
- * Try to fix MySQL character encoding problems.
- * MySQL < 5.5 does not support proper 4 byte unicode but they
- * seem to have added it with version 5.5 under a different label: utf8mb4.
- * We try to select the best possible charset based on your version data.
- *
- * @return void
- */
- protected function setEncoding()
- {
- $driver = $this->pdo->getAttribute( \PDO::ATTR_DRIVER_NAME );
- $version = floatval( $this->pdo->getAttribute( \PDO::ATTR_SERVER_VERSION ) );
- if ($driver === 'mysql') {
- $encoding = ($version >= 5.5) ? 'utf8mb4' : 'utf8';
- $this->pdo->setAttribute(\PDO::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES '.$encoding ); //on every re-connect
- $this->pdo->exec(' SET NAMES '. $encoding); //also for current connection
- $this->mysqlEncoding = $encoding;
- }
- }
-
- /**
- * Constructor. You may either specify dsn, user and password or
- * just give an existing PDO connection.
- *
- * Examples:
- * $driver = new RPDO($dsn, $user, $password);
- * $driver = new RPDO($existingConnection);
- *
- * @param string|object $dsn database connection string
- * @param string $user optional, usename to sign in
- * @param string $pass optional, password for connection login
- *
- * @return void
- */
- public function __construct( $dsn, $user = NULL, $pass = NULL )
- {
- if ( is_object( $dsn ) ) {
- $this->pdo = $dsn;
- $this->isConnected = TRUE;
- $this->setEncoding();
- $this->pdo->setAttribute( \PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION );
- $this->pdo->setAttribute( \PDO::ATTR_DEFAULT_FETCH_MODE,\PDO::FETCH_ASSOC );
- // make sure that the dsn at least contains the type
- $this->dsn = $this->getDatabaseType();
- } else {
- $this->dsn = $dsn;
- $this->connectInfo = array( 'pass' => $pass, 'user' => $user );
- }
-
- //PHP 5.3 PDO SQLite has a bug with large numbers:
- if ( ( strpos( $this->dsn, 'sqlite' ) === 0 && PHP_MAJOR_VERSION === 5 && PHP_MINOR_VERSION === 3 ) || defined('HHVM_VERSION') || $this->dsn === 'test-sqlite-53' ) {
- $this->max = 2147483647; //otherwise you get -2147483648 ?! demonstrated in build #603 on Travis.
- } elseif ( strpos( $this->dsn, 'cubrid' ) === 0 ) {
- $this->max = 2147483647; //bindParam in pdo_cubrid also fails...
- } else {
- $this->max = PHP_INT_MAX; //the normal value of course (makes it possible to use large numbers in LIMIT clause)
- }
- }
-
- /**
- * Returns the best possible encoding for MySQL based on version data.
- *
- * @return string
- */
- public function getMysqlEncoding()
- {
- return $this->mysqlEncoding;
- }
-
- /**
- * Whether to bind all parameters as strings.
- * If set to TRUE this will cause all integers to be bound as STRINGS.
- * This will NOT affect NULL values.
- *
- * @param boolean $yesNo pass TRUE to bind all parameters as strings.
- *
- * @return void
- */
- public function setUseStringOnlyBinding( $yesNo )
- {
- $this->flagUseStringOnlyBinding = (boolean) $yesNo;
- }
-
- /**
- * Sets the maximum value to be bound as integer, normally
- * this value equals PHP's MAX INT constant, however sometimes
- * PDO driver bindings cannot bind large integers as integers.
- * This method allows you to manually set the max integer binding
- * value to manage portability/compatibility issues among different
- * PHP builds. This method will return the old value.
- *
- * @param integer $max maximum value for integer bindings
- *
- * @return integer
- */
- public function setMaxIntBind( $max )
- {
- if ( !is_integer( $max ) ) throw new RedException( 'Parameter has to be integer.' );
- $oldMax = $this->max;
- $this->max = $max;
- return $oldMax;
- }
-
- /**
- * Establishes a connection to the database using PHP\PDO
- * functionality. If a connection has already been established this
- * method will simply return directly. This method also turns on
- * UTF8 for the database and PDO-ERRMODE-EXCEPTION as well as
- * PDO-FETCH-ASSOC.
- *
- * @return void
- */
- public function connect()
- {
- if ( $this->isConnected ) return;
- try {
- $user = $this->connectInfo['user'];
- $pass = $this->connectInfo['pass'];
- $this->pdo = new \PDO(
- $this->dsn,
- $user,
- $pass
- );
- $this->setEncoding();
- $this->pdo->setAttribute( \PDO::ATTR_STRINGIFY_FETCHES, TRUE );
- //cant pass these as argument to constructor, CUBRID driver does not understand...
- $this->pdo->setAttribute( \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION );
- $this->pdo->setAttribute( \PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC );
- $this->isConnected = TRUE;
- } catch ( \PDOException $exception ) {
- $matches = array();
- $dbname = ( preg_match( '/dbname=(\w+)/', $this->dsn, $matches ) ) ? $matches[1] : '?';
- throw new \PDOException( 'Could not connect to database (' . $dbname . ').', $exception->getCode() );
- }
- }
-
- /**
- * Directly sets PDO instance into driver.
- * This method might improve performance, however since the driver does
- * not configure this instance terrible things may happen... only use
- * this method if you are an expert on RedBeanPHP, PDO and UTF8 connections and
- * you know your database server VERY WELL.
- *
- * @param PDO $pdo PDO instance
- *
- * @return void
- */
- public function setPDO( \PDO $pdo ) {
- $this->pdo = $pdo;
- }
-
- /**
- * @see Driver::GetAll
- */
- public function GetAll( $sql, $bindings = array() )
- {
- $this->runQuery( $sql, $bindings );
- return $this->resultArray;
- }
-
- /**
- * @see Driver::GetAssocRow
- */
- public function GetAssocRow( $sql, $bindings = array() )
- {
- $this->runQuery( $sql, $bindings, array(
- 'fetchStyle' => \PDO::FETCH_ASSOC
- )
- );
- return $this->resultArray;
- }
-
- /**
- * @see Driver::GetCol
- */
- public function GetCol( $sql, $bindings = array() )
- {
- $rows = $this->GetAll( $sql, $bindings );
- $cols = array();
- if ( $rows && is_array( $rows ) && count( $rows ) > 0 ) {
- foreach ( $rows as $row ) {
- $cols[] = array_shift( $row );
- }
- }
-
- return $cols;
- }
-
- /**
- * @see Driver::GetOne
- */
- public function GetOne( $sql, $bindings = array() )
- {
- $arr = $this->GetAll( $sql, $bindings );
- $res = NULL;
- if ( !is_array( $arr ) ) return NULL;
- if ( count( $arr ) === 0 ) return NULL;
- $row1 = array_shift( $arr );
- if ( !is_array( $row1 ) ) return NULL;
- if ( count( $row1 ) === 0 ) return NULL;
- $col1 = array_shift( $row1 );
- return $col1;
- }
-
- /**
- * Alias for getOne().
- * Backward compatibility.
- *
- * @param string $sql SQL
- * @param array $bindings bindings
- *
- * @return mixed
- */
- public function GetCell( $sql, $bindings = array() )
- {
- return $this->GetOne( $sql, $bindings );
- }
-
- /**
- * @see Driver::GetRow
- */
- public function GetRow( $sql, $bindings = array() )
- {
- $arr = $this->GetAll( $sql, $bindings );
- return array_shift( $arr );
- }
-
- /**
- * @see Driver::Excecute
- */
- public function Execute( $sql, $bindings = array() )
- {
- $this->runQuery( $sql, $bindings );
- return $this->affectedRows;
- }
-
- /**
- * @see Driver::GetInsertID
- */
- public function GetInsertID()
- {
- $this->connect();
-
- return (int) $this->pdo->lastInsertId();
- }
-
- /**
- * @see Driver::GetCursor
- */
- public function GetCursor( $sql, $bindings = array() )
- {
- $statement = $this->runQuery( $sql, $bindings, array( 'noFetch' => TRUE ) );
- $cursor = new PDOCursor( $statement, \PDO::FETCH_ASSOC );
- return $cursor;
- }
-
- /**
- * @see Driver::Affected_Rows
- */
- public function Affected_Rows()
- {
- $this->connect();
- return (int) $this->affectedRows;
- }
-
- /**
- * Toggles debug mode. In debug mode the driver will print all
- * SQL to the screen together with some information about the
- * results.
- *
- * @param boolean $trueFalse turn on/off
- * @param Logger $logger logger instance
- *
- * @return void
- */
- public function setDebugMode( $tf, $logger = NULL )
- {
- $this->connect();
- $this->loggingEnabled = (bool) $tf;
- if ( $this->loggingEnabled and !$logger ) {
- $logger = new RDefault();
- }
- $this->setLogger( $logger );
- }
-
- /**
- * Injects Logger object.
- * Sets the logger instance you wish to use.
- *
- * @param Logger $logger the logger instance to be used for logging
- *
- * @return void
- */
- public function setLogger( Logger $logger )
- {
- $this->logger = $logger;
- }
-
- /**
- * Gets Logger object.
- * Returns the currently active Logger instance.
- *
- * @return Logger
- */
- public function getLogger()
- {
- return $this->logger;
- }
-
- /**
- * @see Driver::StartTrans
- */
- public function StartTrans()
- {
- $this->connect();
- $this->pdo->beginTransaction();
- }
-
- /**
- * @see Driver::CommitTrans
- */
- public function CommitTrans()
- {
- $this->connect();
- $this->pdo->commit();
- }
-
- /**
- * @see Driver::FailTrans
- */
- public function FailTrans()
- {
- $this->connect();
- $this->pdo->rollback();
- }
-
- /**
- * Returns the name of database driver for PDO.
- * Uses the PDO attribute DRIVER NAME to obtain the name of the
- * PDO driver.
- *
- * @return string
- */
- public function getDatabaseType()
- {
- $this->connect();
-
- return $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME );
- }
-
- /**
- * Returns the version number of the database.
- *
- * @return mixed
- */
- public function getDatabaseVersion()
- {
- $this->connect();
- return $this->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION );
- }
-
- /**
- * Returns the underlying PHP PDO instance.
- *
- * @return PDO
- */
- public function getPDO()
- {
- $this->connect();
- return $this->pdo;
- }
-
- /**
- * Closes database connection by destructing PDO.
- *
- * @return void
- */
- public function close()
- {
- $this->pdo = NULL;
- $this->isConnected = FALSE;
- }
-
- /**
- * Returns TRUE if the current PDO instance is connected.
- *
- * @return boolean
- */
- public function isConnected()
- {
- return $this->isConnected && $this->pdo;
- }
-
- /**
- * Toggles logging, enables or disables logging.
- *
- * @param boolean $enable TRUE to enable logging
- *
- * @return self
- */
- public function setEnableLogging( $enable )
- {
- $this->loggingEnabled = (boolean) $enable;
- }
-
- /**
- * Resets the internal Query Counter.
- *
- * @return self
- */
- public function resetCounter()
- {
- $this->queryCounter = 0;
- return $this;
- }
-
- /**
- * Returns the number of SQL queries processed.
- *
- * @return integer
- */
- public function getQueryCount()
- {
- return $this->queryCounter;
- }
-
- /**
- * Returns the maximum value treated as integer parameter
- * binding.
- *
- * This method is mainly for testing purposes but it can help
- * you solve some issues relating to integer bindings.
- *
- * @return integer
- */
- public function getIntegerBindingMax()
- {
- return $this->max;
- }
-}