/library/database/class.database.php
PHP | 357 lines | 214 code | 54 blank | 89 comment | 47 complexity | 07fea140986f428db2e59d27e37f92da MD5 | raw file
- <?php if (!defined('APPLICATION')) exit();
- /*
- Copyright 2008, 2009 Vanilla Forums Inc.
- This file is part of Garden.
- Garden is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
- Garden is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- You should have received a copy of the GNU General Public License along with Garden. If not, see <http://www.gnu.org/licenses/>.
- Contact Vanilla Forums Inc. at support [at] vanillaforums [dot] com
- */
- /**
- * The Database object contains connection and engine information for a single database.
- * It also allows a database to execute string sql statements against that database.
- *
- * @author Todd Burry
- * @copyright 2003 Mark O'Sullivan
- * @license http://www.opensource.org/licenses/gpl-2.0.php GPL
- * @version @@GARDEN-VERSION@@
- * @namespace Garden.Database
- */
- class Gdn_Database {
- /// CONSTRUCTOR ///
- /** @param mixed $Config The configuration settings for this object.
- * @see Database::Init()
- */
- public function __construct($Config = NULL) {
- $this->ClassName = get_class($this);
- $this->Init($Config);
- }
-
- /// PROPERTIES ///
-
- /** @var string The instance name of this class or the class that inherits from this class. */
- public $ClassName;
-
- private $_CurrentResultSet;
-
- /** @var PDO The connectio to the database. */
- protected $_Connection = NULL;
-
-
- protected $_SQL = NULL;
-
- protected $_Structure = NULL;
-
- protected $_IsPersistent = FALSE;
-
- /** Get the PDO connection to the database.
- * @return PDO The connection to the database.
- */
- public function Connection() {
- $this->_IsPersistent = GetValue(PDO::ATTR_PERSISTENT, $this->ConnectionOptions, FALSE);
- if(!is_object($this->_Connection)) {
- try {
- $this->_Connection = new PDO(strtolower($this->Engine) . ':' . $this->Dsn, $this->User, $this->Password, $this->ConnectionOptions);
- if($this->ConnectionOptions[1002])
- $this->Query($this->ConnectionOptions[1002]);
- } catch (Exception $ex) {
- trigger_error(ErrorMessage('An error occurred while attempting to connect to the database', $this->ClassName, 'Connection', $ex->getMessage()), E_USER_ERROR);
- }
- }
-
- return $this->_Connection;
- }
-
- /** @var array The connection options passed to the PDO constructor **/
- public $ConnectionOptions;
-
- /** @var string The prefix to all database tables. */
- public $DatabasePrefix;
-
- /** @var array Extented properties that a specific driver can use. **/
- public $ExtendedProperties;
-
- /** $var bool Whether or not the connection is in a transaction. **/
- protected $_InTransaction = FALSE;
-
- /** @var string The PDO dsn for the database connection.
- * Note: This does NOT include the engine before the dsn.
- */
- public $Dsn;
-
- /** @var string The name of the database engine for this class. */
- public $Engine;
-
- /** @var string The password to the database. */
- public $Password;
-
- /** @var string The username connecting to the database. */
- public $User;
-
- /// METHODS ///
-
- /**
- * Begin a transaction on the database.
- */
- public function BeginTransaction() {
- if(!$this->_InTransaction)
- $this->_InTransaction = $this->Connection()->beginTransaction();
- }
-
- public function CloseConnection() {
- if (!$this->_IsPersistent) {
- $this->CommitTransaction();
- $this->_Connection = NULL;
- }
- }
-
- /**
- * Commit a transaction on the database.
- */
- public function CommitTransaction() {
- if($this->_InTransaction) {
- $this->_InTransaction = !$this->Connection()->commit();
- }
- }
-
- /**
- * Properly quotes and escapes a expression for an sql string.
- * @param mixed $Expr The expression to quote.
- * @return string The quoted expression.
- */
- public function QuoteExpression($Expr) {
- if(is_null($Expr)) {
- return 'NULL';
- } elseif(is_string($Expr)) {
- return '\''.str_replace('\'', '\\\'', $Expr).'\'';
- } elseif(is_object($Expr)) {
- return '?OBJECT?';
- } else {
- return $Expr;
- }
- }
-
- /**
- * Initialize the properties of this object.
- * @param mixed $Config The database is instantiated differently depending on the type of $Config:
- * - <b>null</b>: The database stored in the factory location Gdn:AliasDatabase will be used.
- * - <b>string</b>: The name of the configuration section to get the connection information from.
- * - <b>array</b>: The database properties will be set from the array. The following items can be in the array:
- * - <b>Engine</b>: Required. The name of the database engine (MySQL, pgsql, sqlite, odbc, etc.
- * - <b>Dsn</b>: Optional. The dsn for the connection. If the dsn is not supplied then the connectio information below must be supplied.
- * - <b>Host, Dbname</b>: Optional. The individual database connection options that will be build into a dsn.
- * - <b>User</b>: The username to connect to the datbase.
- * - <b>Password</b>: The password to connect to the database.
- * - <b>ConnectionOptions</b>: Other PDO connection attributes.
- */
- public function Init($Config = NULL) {
- if(is_null($Config))
- $Config = Gdn::Config('Database');
- elseif(is_string($Config))
- $Config = Gdn::Config($Config);
-
- $DefaultConfig = Gdn::Config('Database');
-
- $this->Engine = ArrayValue('Engine', $Config, $DefaultConfig['Engine']);
- $this->User = ArrayValue('User', $Config, $DefaultConfig['User']);
- $this->Password = ArrayValue('Password', $Config, $DefaultConfig['Password']);
- $this->ConnectionOptions = ArrayValue('ConnectionOptions', $Config, $DefaultConfig['ConnectionOptions']);
- $this->DatabasePrefix = ArrayValue('DatabasePrefix', $Config, ArrayValue('Prefix', $Config, $DefaultConfig['DatabasePrefix']));
- $this->ExtendedProperties = ArrayValue('ExtendedProperties', $Config, array());
-
- if(array_key_exists('Dsn', $Config)) {
- // Get the dsn from the property.
- $Dsn = $Config['Dsn'];
- } else {
- $Host = ArrayValue('Host', $Config, ArrayValue('Host', $DefaultConfig, ''));
- if(array_key_exists('Dbname', $Config))
- $Dbname = $Config['Dbname'];
- elseif(array_key_exists('Name', $Config))
- $Dbname = $Config['Name'];
- elseif(array_key_exists('Dbname', $DefaultConfig))
- $Dbname = $DefaultConfig['Dbname'];
- elseif(array_key_exists('Name', $DefaultConfig))
- $Dbname = $DefaultConfig['Name'];
- // Was the port explicitly defined in the config?
- $Port = ArrayValue('Port', $Config, ArrayValue('Port', $DefaultConfig, ''));
-
- if(!isset($Dbname)) {
- $Dsn = $DefaultConfig['Dsn'];
- } else {
- if(empty($Port)) {
- // Was the port explicitly defined with the host name? (ie. 127.0.0.1:3306)
- $Host = explode(':', $Host);
- $Port = count($Host) == 2 ? $Host[1] : '';
- $Host = $Host[0];
- }
-
- if(empty($Port)) {
- $Dsn = sprintf('host=%s;dbname=%s;', $Host, $Dbname);
- } else {
- $Dsn = sprintf('host=%s;port=%s;dbname=%s;', $Host, $Port, $Dbname);
- }
- }
- }
-
- $this->Dsn = $Dsn;
- }
-
- /**
- * Executes a string of SQL. Returns a @@DataSet object.
- *
- * @param string $Sql A string of SQL to be executed.
- * @param array $InputParameters An array of values with as many elements as there are bound parameters in the SQL statement being executed.
- */
- public function Query($Sql, $InputParameters = NULL, $Options = array()) {
- if ($Sql == '')
- trigger_error(ErrorMessage('Database was queried with an empty string.', $this->ClassName, 'Query'), E_USER_ERROR);
- // Get the return type.
- if (isset($Options['ReturnType']))
- $ReturnType = $Options['ReturnType'];
- elseif (preg_match('/^\s*"?(insert)\s+/i', $Sql))
- $ReturnType = 'ID';
- elseif (!preg_match('/^\s*"?(update|delete|replace|create|drop|load data|copy|alter|grant|revoke|lock|unlock)\s+/i', $Sql))
- $ReturnType = 'DataSet';
- else
- $ReturnType = NULL;
- if (isset($Options['Cache'])) {
- // Check to see if the query is cached.
- $CacheKeys = (array)GetValue('Cache',$Options,NULL);
- $CacheOperation = GetValue('CacheOperation',$Options,NULL);
- if (is_null($CacheOperation))
- switch ($ReturnType) {
- case 'DataSet':
- $CacheOperation = 'get';
- break;
- case 'ID':
- case NULL:
- $CacheOperation = 'remove';
- break;
- }
-
- switch ($CacheOperation) {
- case 'get':
- foreach ($CacheKeys as $CacheKey) {
- $Data = Gdn::Cache()->Get($CacheKey);
- }
- // Cache hit. Return.
- if ($Data !== Gdn_Cache::CACHEOP_FAILURE)
- return new Gdn_DataSet($Data);
-
- // Cache miss. Save later.
- $StoreCacheKey = $CacheKey;
- break;
-
- case 'increment':
- case 'decrement':
- $CacheMethod = ucfirst($CacheOperation);
- foreach ($CacheKeys as $CacheKey) {
- $CacheResult = Gdn::Cache()->$CacheMethod($CacheKey);
- }
- break;
-
- case 'remove':
- foreach ($CacheKeys as $CacheKey) {
- Gdn::Cache()->Remove($CacheKey);
- }
- break;
- }
- }
- // Run the Query
- if (!is_null($InputParameters) && count($InputParameters) > 0) {
- // Make sure other unbufferred queries are not open
- if (is_object($this->_CurrentResultSet)) {
- $this->_CurrentResultSet->Result();
- $this->_CurrentResultSet->FreePDOStatement(FALSE);
- }
- $PDOStatement = $this->Connection()->prepare($Sql);
- if (!is_object($PDOStatement)) {
- trigger_error(ErrorMessage('PDO Statement failed to prepare', $this->ClassName, 'Query', $this->GetPDOErrorMessage($this->Connection()->errorInfo())), E_USER_ERROR);
- } else if ($PDOStatement->execute($InputParameters) === FALSE) {
- trigger_error(ErrorMessage($this->GetPDOErrorMessage($PDOStatement->errorInfo()), $this->ClassName, 'Query', $Sql), E_USER_ERROR);
- }
- } else {
- $PDOStatement = $this->Connection()->query($Sql);
- }
- if ($PDOStatement === FALSE) {
- trigger_error(ErrorMessage($this->GetPDOErrorMessage($this->Connection()->errorInfo()), $this->ClassName, 'Query', $Sql), E_USER_ERROR);
- }
-
- // Did this query modify data in any way?
- if ($ReturnType == 'ID') {
- $this->_CurrentResultSet = $this->Connection()->lastInsertId();
- } else {
- if ($ReturnType == 'DataSet') {
- // Create a DataSet to manage the resultset
- $this->_CurrentResultSet = new Gdn_DataSet();
- $this->_CurrentResultSet->Connection = $this->Connection();
- $this->_CurrentResultSet->PDOStatement($PDOStatement);
- }
- }
-
- if (isset($StoreCacheKey)) {
- if ($CacheOperation == 'get')
- Gdn::Cache()->Store($StoreCacheKey, (($this->_CurrentResultSet instanceof Gdn_DataSet) ? $this->_CurrentResultSet->ResultArray() : $this->_CurrentResultSet));
- }
-
- return $this->_CurrentResultSet;
- }
-
- public function RollbackTransaction() {
- if($this->_InTransaction) {
- $this->_InTransaction = !$this->Connection()->rollBack();
- }
- }
- public function GetPDOErrorMessage($ErrorInfo) {
- $ErrorMessage = '';
- if (is_array($ErrorInfo)) {
- if (count($ErrorInfo) >= 2)
- $ErrorMessage = $ErrorInfo[2];
- elseif (count($ErrorInfo) >= 1)
- $ErrorMessage = $ErrorInfo[0];
- } elseif (is_string($ErrorInfo)) {
- $ErrorMessage = $ErrorInfo;
- }
- return $ErrorMessage;
- }
-
- /**
- * Get the database driver class for the database.
- * @return Gdn_SQLDriver The database driver class associated with this database.
- */
- public function SQL() {
- if(is_null($this->_SQL)) {
- $Name = $this->Engine . 'Driver';
- $this->_SQL = Gdn::Factory($Name);
- $this->_SQL->Database = $this;
- }
-
- return $this->_SQL;
- }
-
- /**
- * Get the database structure class for this database.
- *
- * @return Gdn_DatabaseStructure The database structure class for this database.
- */
- public function Structure() {
- if(is_null($this->_Structure)) {
- $Name = $this->Engine . 'Structure';
- $this->_Structure = Gdn::Factory($Name);
- $this->_Structure->Database = $this;
- }
-
- return $this->_Structure;
- }
- }