PageRenderTime 45ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/library/database/class.database.php

https://gitlab.com/sheldonels/Garden
PHP | 303 lines | 168 code | 48 blank | 87 comment | 37 complexity | e9d426a916f0ed7d1b6226ddee4fcf6e MD5 | raw file
  1. <?php if (!defined('APPLICATION')) exit();
  2. /*
  3. Copyright 2008, 2009 Vanilla Forums Inc.
  4. This file is part of Garden.
  5. 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.
  6. 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.
  7. You should have received a copy of the GNU General Public License along with Garden. If not, see <http://www.gnu.org/licenses/>.
  8. Contact Vanilla Forums Inc. at support [at] vanillaforums [dot] com
  9. */
  10. /**
  11. * The Database object contains connection and engine information for a single database.
  12. * It also allows a database to execute string sql statements against that database.
  13. *
  14. * @author Todd Burry
  15. * @copyright 2003 Mark O'Sullivan
  16. * @license http://www.opensource.org/licenses/gpl-2.0.php GPL
  17. * @version @@GARDEN-VERSION@@
  18. * @namespace Garden.Database
  19. */
  20. require_once(dirname(__FILE__).DS.'class.dataset.php');
  21. class Gdn_Database {
  22. /// CONSTRUCTOR ///
  23. /** @param mixed $Config The configuration settings for this object.
  24. * @see Database::Init()
  25. */
  26. public function __construct($Config = NULL) {
  27. $this->ClassName = get_class($this);
  28. $this->Init($Config);
  29. }
  30. /// PROPERTIES ///
  31. /** @var string The instance name of this class or the class that inherits from this class. */
  32. public $ClassName;
  33. private $_CurrentResultSet;
  34. /** @var PDO The connectio to the database. */
  35. protected $_Connection = NULL;
  36. protected $_SQL = NULL;
  37. protected $_Structure = NULL;
  38. /** Get the PDO connection to the database.
  39. * @return PDO The connection to the database.
  40. */
  41. public function Connection() {
  42. if(!is_object($this->_Connection)) {
  43. try {
  44. $this->_Connection = new PDO(strtolower($this->Engine) . ':' . $this->Dsn, $this->User, $this->Password, $this->ConnectionOptions);
  45. if($this->ConnectionOptions[1002])
  46. $this->Query($this->ConnectionOptions[1002]);
  47. } catch (Exception $ex) {
  48. trigger_error(ErrorMessage('An error occurred while attempting to connect to the database', $this->ClassName, 'Connection', $ex->getMessage()), E_USER_ERROR);
  49. }
  50. }
  51. return $this->_Connection;
  52. }
  53. /** @var array The connection options passed to the PDO constructor **/
  54. public $ConnectionOptions;
  55. /** @var string The prefix to all database tables. */
  56. public $DatabasePrefix;
  57. /** @var array Extented properties that a specific driver can use. **/
  58. public $ExtendedProperties;
  59. /** $var bool Whether or not the connection is in a transaction. **/
  60. protected $_InTransaction = FALSE;
  61. /** @var string The PDO dsn for the database connection.
  62. * Note: This does NOT include the engine before the dsn.
  63. */
  64. public $Dsn;
  65. /** @var string The name of the database engine for this class. */
  66. public $Engine;
  67. /** @var string The password to the database. */
  68. public $Password;
  69. /** @var string The username connecting to the database. */
  70. public $User;
  71. /// METHODS ///
  72. /**
  73. * Begin a transaction on the database.
  74. */
  75. public function BeginTransaction() {
  76. if($this->_InTransaction)
  77. $this->_InTransaction = $this->Connection()->beginTransaction();
  78. }
  79. public function CloseConnection() {
  80. if (!Gdn::Config('Database.PersistentConnection')) {
  81. $this->CommitTransaction();
  82. $this->_Connection = NULL;
  83. }
  84. }
  85. /**
  86. * Commit a transaction on the database.
  87. */
  88. public function CommitTransaction() {
  89. if($this->_InTransaction) {
  90. $this->_InTransaction = !$this->Connection()->commit();
  91. }
  92. }
  93. /**
  94. * Properly quotes and escapes a expression for an sql string.
  95. * @param mixed $Expr The expression to quote.
  96. * @return string The quoted expression.
  97. */
  98. public function QuoteExpression($Expr) {
  99. if(is_null($Expr)) {
  100. return 'NULL';
  101. } elseif(is_string($Expr)) {
  102. return '\''.str_replace('\'', '\\\'', $Expr).'\'';
  103. } elseif(is_object($Expr)) {
  104. return '?OBJECT?';
  105. } else {
  106. return $Expr;
  107. }
  108. }
  109. /**
  110. * Initialize the properties of this object.
  111. * @param mixed $Config The database is instantiated differently depending on the type of $Config:
  112. * - <b>null</b>: The database stored in the factory location Gdn:AliasDatabase will be used.
  113. * - <b>string</b>: The name of the configuration section to get the connection information from.
  114. * - <b>array</b>: The database properties will be set from the array. The following items can be in the array:
  115. * - <b>Engine</b>: Required. The name of the database engine (MySQL, pgsql, sqlite, odbc, etc.
  116. * - <b>Dsn</b>: Optional. The dsn for the connection. If the dsn is not supplied then the connectio information below must be supplied.
  117. * - <b>Host, Dbname</b>: Optional. The individual database connection options that will be build into a dsn.
  118. * - <b>User</b>: The username to connect to the datbase.
  119. * - <b>Password</b>: The password to connect to the database.
  120. * - <b>ConnectionOptions</b>: Other PDO connection attributes.
  121. */
  122. public function Init($Config = NULL) {
  123. if(is_null($Config))
  124. $Config = Gdn::Config('Database');
  125. elseif(is_string($Config))
  126. $Config = Gdn::Config($Config);
  127. $DefaultConfig = Gdn::Config('Database');
  128. $this->Engine = ArrayValue('Engine', $Config, $DefaultConfig['Engine']);
  129. $this->User = ArrayValue('User', $Config, $DefaultConfig['User']);
  130. $this->Password = ArrayValue('Password', $Config, $DefaultConfig['Password']);
  131. $this->ConnectionOptions = ArrayValue('ConnectionOptions', $Config, $DefaultConfig['ConnectionOptions']);
  132. $this->DatabasePrefix = ArrayValue('DatabasePrefix', $Config, ArrayValue('Prefix', $Config, $DefaultConfig['DatabasePrefix']));
  133. $this->ExtendedProperties = ArrayValue('ExtendedProperties', $Config, array());
  134. if(array_key_exists('Dsn', $Config)) {
  135. // Get the dsn from the property.
  136. $Dsn = $Config['Dsn'];
  137. } else {
  138. $Host = ArrayValue('Host', $Config, ArrayValue('Host', $DefaultConfig, ''));
  139. if(array_key_exists('Dbname', $Config))
  140. $Dbname = $Config['Dbname'];
  141. elseif(array_key_exists('Name', $Config))
  142. $Dbname = $Config['Name'];
  143. elseif(array_key_exists('Dbname', $DefaultConfig))
  144. $Dbname = $DefaultConfig['Dbname'];
  145. elseif(array_key_exists('Name', $DefaultConfig))
  146. $Dbname = $DefaultConfig['Name'];
  147. // Was the port explicitly defined in the config?
  148. $Port = ArrayValue('Port', $Config, ArrayValue('Port', $DefaultConfig, ''));
  149. if(!isset($Dbname)) {
  150. $Dsn = $DefaultConfig['Dsn'];
  151. } else {
  152. if(empty($Port)) {
  153. // Was the port explicitly defined with the host name? (ie. 127.0.0.1:3306)
  154. $Host = explode(':', $Host);
  155. $Port = count($Host) == 2 ? $Host[1] : '';
  156. $Host = $Host[0];
  157. }
  158. if(empty($Port)) {
  159. $Dsn = sprintf('host=%s;dbname=%s;', $Host, $Dbname);
  160. } else {
  161. $Dsn = sprintf('host=%s;port=%s;dbname=%s;', $Host, $Port, $Dbname);
  162. }
  163. }
  164. }
  165. $this->Dsn = $Dsn;
  166. }
  167. /**
  168. * Executes a string of SQL. Returns a @@DataSet object.
  169. *
  170. * @param string $Sql A string of SQL to be executed.
  171. * @param array $InputParameters An array of values with as many elements as there are bound parameters in the SQL statement being executed.
  172. */
  173. public function Query($Sql, $InputParameters = NULL, $Event = '') {
  174. if($Event) {
  175. // TODO: Raise an event so the query can be overridden.
  176. }
  177. if ($Sql == '')
  178. trigger_error(ErrorMessage('Database was queried with an empty string.', $this->ClassName, 'Query'), E_USER_ERROR);
  179. // Run the Query
  180. if (!is_null($InputParameters) && count($InputParameters) > 0) {
  181. // Make sure other unbufferred queries are not open
  182. if (is_object($this->_CurrentResultSet)) {
  183. $this->_CurrentResultSet->Result();
  184. $this->_CurrentResultSet->FreePDOStatement(FALSE);
  185. }
  186. $PDOStatement = $this->Connection()->prepare($Sql);
  187. if (!is_object($PDOStatement)) {
  188. trigger_error(ErrorMessage('PDO Statement failed to prepare', $this->ClassName, 'Query', $this->GetPDOErrorMessage($this->Connection()->errorInfo())), E_USER_ERROR);
  189. } else if ($PDOStatement->execute($InputParameters) === FALSE) {
  190. trigger_error(ErrorMessage($this->GetPDOErrorMessage($PDOStatement->errorInfo()), $this->ClassName, 'Query', $Sql), E_USER_ERROR);
  191. }
  192. } else {
  193. $PDOStatement = $this->Connection()->query($Sql);
  194. }
  195. if ($PDOStatement === FALSE) {
  196. trigger_error(ErrorMessage($this->GetPDOErrorMessage($this->Connection()->errorInfo()), $this->ClassName, 'Query', $Sql), E_USER_ERROR);
  197. }
  198. $Result = TRUE;
  199. // Did this query modify data in any way?
  200. if (preg_match('/^\s*"?(insert)\s+/i', $Sql)) {
  201. $this->_CurrentResultSet = $this->Connection()->lastInsertId(); // TODO: APPARENTLY THIS IS NOT RELIABLE WITH DB'S OTHER THAN MYSQL
  202. } else {
  203. // TODO: LOOK INTO SEEING IF AN UPDATE QUERY CAN RETURN # OF AFFECTED RECORDS?
  204. if (!preg_match('/^\s*"?(update|delete|replace|create|drop|load data|copy|alter|grant|revoke|lock|unlock)\s+/i', $Sql)) {
  205. // Create a DataSet to manage the resultset
  206. $this->_CurrentResultSet = new Gdn_DataSet();
  207. $this->_CurrentResultSet->Connection = $this->Connection();
  208. $this->_CurrentResultSet->PDOStatement($PDOStatement);
  209. }
  210. }
  211. return $this->_CurrentResultSet;
  212. }
  213. public function RollbackTransaction() {
  214. if($this->_InTransaction) {
  215. $this->_InTransaction = !$this->Connection()->rollBack();
  216. }
  217. }
  218. public function GetPDOErrorMessage($ErrorInfo) {
  219. $ErrorMessage = '';
  220. if (is_array($ErrorInfo)) {
  221. if (count($ErrorInfo) >= 2)
  222. $ErrorMessage = $ErrorInfo[2];
  223. elseif (count($ErrorInfo) >= 1)
  224. $ErrorMessage = $ErrorInfo[0];
  225. } elseif (is_string($ErrorInfo)) {
  226. $ErrorMessage = $ErrorInfo;
  227. }
  228. return $ErrorMessage;
  229. }
  230. /**
  231. * Get the database driver class for the database.
  232. * @return Gdn_SQLDriver The database driver class associated with this database.
  233. */
  234. public function SQL() {
  235. if(is_null($this->_SQL)) {
  236. $Name = $this->Engine . 'Driver';
  237. $this->_SQL = Gdn::Factory($Name);
  238. $this->_SQL->Database = $this;
  239. }
  240. return $this->_SQL;
  241. }
  242. /**
  243. * Get the database structure class for this database.
  244. *
  245. * @return Gdn_DatabaseStructure The database structure class for this database.
  246. */
  247. public function Structure() {
  248. if(is_null($this->_Structure)) {
  249. $Name = $this->Engine . 'Structure';
  250. $this->_Structure = Gdn::Factory($Name);
  251. $this->_Structure->Database = $this;
  252. }
  253. return $this->_Structure;
  254. }
  255. }