PageRenderTime 64ms CodeModel.GetById 39ms RepoModel.GetById 0ms app.codeStats 0ms

/library/database/class.database.php

https://github.com/abhishekmica/Garden
PHP | 357 lines | 214 code | 54 blank | 89 comment | 47 complexity | 07fea140986f428db2e59d27e37f92da 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. class Gdn_Database {
  21. /// CONSTRUCTOR ///
  22. /** @param mixed $Config The configuration settings for this object.
  23. * @see Database::Init()
  24. */
  25. public function __construct($Config = NULL) {
  26. $this->ClassName = get_class($this);
  27. $this->Init($Config);
  28. }
  29. /// PROPERTIES ///
  30. /** @var string The instance name of this class or the class that inherits from this class. */
  31. public $ClassName;
  32. private $_CurrentResultSet;
  33. /** @var PDO The connectio to the database. */
  34. protected $_Connection = NULL;
  35. protected $_SQL = NULL;
  36. protected $_Structure = NULL;
  37. protected $_IsPersistent = FALSE;
  38. /** Get the PDO connection to the database.
  39. * @return PDO The connection to the database.
  40. */
  41. public function Connection() {
  42. $this->_IsPersistent = GetValue(PDO::ATTR_PERSISTENT, $this->ConnectionOptions, FALSE);
  43. if(!is_object($this->_Connection)) {
  44. try {
  45. $this->_Connection = new PDO(strtolower($this->Engine) . ':' . $this->Dsn, $this->User, $this->Password, $this->ConnectionOptions);
  46. if($this->ConnectionOptions[1002])
  47. $this->Query($this->ConnectionOptions[1002]);
  48. } catch (Exception $ex) {
  49. trigger_error(ErrorMessage('An error occurred while attempting to connect to the database', $this->ClassName, 'Connection', $ex->getMessage()), E_USER_ERROR);
  50. }
  51. }
  52. return $this->_Connection;
  53. }
  54. /** @var array The connection options passed to the PDO constructor **/
  55. public $ConnectionOptions;
  56. /** @var string The prefix to all database tables. */
  57. public $DatabasePrefix;
  58. /** @var array Extented properties that a specific driver can use. **/
  59. public $ExtendedProperties;
  60. /** $var bool Whether or not the connection is in a transaction. **/
  61. protected $_InTransaction = FALSE;
  62. /** @var string The PDO dsn for the database connection.
  63. * Note: This does NOT include the engine before the dsn.
  64. */
  65. public $Dsn;
  66. /** @var string The name of the database engine for this class. */
  67. public $Engine;
  68. /** @var string The password to the database. */
  69. public $Password;
  70. /** @var string The username connecting to the database. */
  71. public $User;
  72. /// METHODS ///
  73. /**
  74. * Begin a transaction on the database.
  75. */
  76. public function BeginTransaction() {
  77. if(!$this->_InTransaction)
  78. $this->_InTransaction = $this->Connection()->beginTransaction();
  79. }
  80. public function CloseConnection() {
  81. if (!$this->_IsPersistent) {
  82. $this->CommitTransaction();
  83. $this->_Connection = NULL;
  84. }
  85. }
  86. /**
  87. * Commit a transaction on the database.
  88. */
  89. public function CommitTransaction() {
  90. if($this->_InTransaction) {
  91. $this->_InTransaction = !$this->Connection()->commit();
  92. }
  93. }
  94. /**
  95. * Properly quotes and escapes a expression for an sql string.
  96. * @param mixed $Expr The expression to quote.
  97. * @return string The quoted expression.
  98. */
  99. public function QuoteExpression($Expr) {
  100. if(is_null($Expr)) {
  101. return 'NULL';
  102. } elseif(is_string($Expr)) {
  103. return '\''.str_replace('\'', '\\\'', $Expr).'\'';
  104. } elseif(is_object($Expr)) {
  105. return '?OBJECT?';
  106. } else {
  107. return $Expr;
  108. }
  109. }
  110. /**
  111. * Initialize the properties of this object.
  112. * @param mixed $Config The database is instantiated differently depending on the type of $Config:
  113. * - <b>null</b>: The database stored in the factory location Gdn:AliasDatabase will be used.
  114. * - <b>string</b>: The name of the configuration section to get the connection information from.
  115. * - <b>array</b>: The database properties will be set from the array. The following items can be in the array:
  116. * - <b>Engine</b>: Required. The name of the database engine (MySQL, pgsql, sqlite, odbc, etc.
  117. * - <b>Dsn</b>: Optional. The dsn for the connection. If the dsn is not supplied then the connectio information below must be supplied.
  118. * - <b>Host, Dbname</b>: Optional. The individual database connection options that will be build into a dsn.
  119. * - <b>User</b>: The username to connect to the datbase.
  120. * - <b>Password</b>: The password to connect to the database.
  121. * - <b>ConnectionOptions</b>: Other PDO connection attributes.
  122. */
  123. public function Init($Config = NULL) {
  124. if(is_null($Config))
  125. $Config = Gdn::Config('Database');
  126. elseif(is_string($Config))
  127. $Config = Gdn::Config($Config);
  128. $DefaultConfig = Gdn::Config('Database');
  129. $this->Engine = ArrayValue('Engine', $Config, $DefaultConfig['Engine']);
  130. $this->User = ArrayValue('User', $Config, $DefaultConfig['User']);
  131. $this->Password = ArrayValue('Password', $Config, $DefaultConfig['Password']);
  132. $this->ConnectionOptions = ArrayValue('ConnectionOptions', $Config, $DefaultConfig['ConnectionOptions']);
  133. $this->DatabasePrefix = ArrayValue('DatabasePrefix', $Config, ArrayValue('Prefix', $Config, $DefaultConfig['DatabasePrefix']));
  134. $this->ExtendedProperties = ArrayValue('ExtendedProperties', $Config, array());
  135. if(array_key_exists('Dsn', $Config)) {
  136. // Get the dsn from the property.
  137. $Dsn = $Config['Dsn'];
  138. } else {
  139. $Host = ArrayValue('Host', $Config, ArrayValue('Host', $DefaultConfig, ''));
  140. if(array_key_exists('Dbname', $Config))
  141. $Dbname = $Config['Dbname'];
  142. elseif(array_key_exists('Name', $Config))
  143. $Dbname = $Config['Name'];
  144. elseif(array_key_exists('Dbname', $DefaultConfig))
  145. $Dbname = $DefaultConfig['Dbname'];
  146. elseif(array_key_exists('Name', $DefaultConfig))
  147. $Dbname = $DefaultConfig['Name'];
  148. // Was the port explicitly defined in the config?
  149. $Port = ArrayValue('Port', $Config, ArrayValue('Port', $DefaultConfig, ''));
  150. if(!isset($Dbname)) {
  151. $Dsn = $DefaultConfig['Dsn'];
  152. } else {
  153. if(empty($Port)) {
  154. // Was the port explicitly defined with the host name? (ie. 127.0.0.1:3306)
  155. $Host = explode(':', $Host);
  156. $Port = count($Host) == 2 ? $Host[1] : '';
  157. $Host = $Host[0];
  158. }
  159. if(empty($Port)) {
  160. $Dsn = sprintf('host=%s;dbname=%s;', $Host, $Dbname);
  161. } else {
  162. $Dsn = sprintf('host=%s;port=%s;dbname=%s;', $Host, $Port, $Dbname);
  163. }
  164. }
  165. }
  166. $this->Dsn = $Dsn;
  167. }
  168. /**
  169. * Executes a string of SQL. Returns a @@DataSet object.
  170. *
  171. * @param string $Sql A string of SQL to be executed.
  172. * @param array $InputParameters An array of values with as many elements as there are bound parameters in the SQL statement being executed.
  173. */
  174. public function Query($Sql, $InputParameters = NULL, $Options = array()) {
  175. if ($Sql == '')
  176. trigger_error(ErrorMessage('Database was queried with an empty string.', $this->ClassName, 'Query'), E_USER_ERROR);
  177. // Get the return type.
  178. if (isset($Options['ReturnType']))
  179. $ReturnType = $Options['ReturnType'];
  180. elseif (preg_match('/^\s*"?(insert)\s+/i', $Sql))
  181. $ReturnType = 'ID';
  182. elseif (!preg_match('/^\s*"?(update|delete|replace|create|drop|load data|copy|alter|grant|revoke|lock|unlock)\s+/i', $Sql))
  183. $ReturnType = 'DataSet';
  184. else
  185. $ReturnType = NULL;
  186. if (isset($Options['Cache'])) {
  187. // Check to see if the query is cached.
  188. $CacheKeys = (array)GetValue('Cache',$Options,NULL);
  189. $CacheOperation = GetValue('CacheOperation',$Options,NULL);
  190. if (is_null($CacheOperation))
  191. switch ($ReturnType) {
  192. case 'DataSet':
  193. $CacheOperation = 'get';
  194. break;
  195. case 'ID':
  196. case NULL:
  197. $CacheOperation = 'remove';
  198. break;
  199. }
  200. switch ($CacheOperation) {
  201. case 'get':
  202. foreach ($CacheKeys as $CacheKey) {
  203. $Data = Gdn::Cache()->Get($CacheKey);
  204. }
  205. // Cache hit. Return.
  206. if ($Data !== Gdn_Cache::CACHEOP_FAILURE)
  207. return new Gdn_DataSet($Data);
  208. // Cache miss. Save later.
  209. $StoreCacheKey = $CacheKey;
  210. break;
  211. case 'increment':
  212. case 'decrement':
  213. $CacheMethod = ucfirst($CacheOperation);
  214. foreach ($CacheKeys as $CacheKey) {
  215. $CacheResult = Gdn::Cache()->$CacheMethod($CacheKey);
  216. }
  217. break;
  218. case 'remove':
  219. foreach ($CacheKeys as $CacheKey) {
  220. Gdn::Cache()->Remove($CacheKey);
  221. }
  222. break;
  223. }
  224. }
  225. // Run the Query
  226. if (!is_null($InputParameters) && count($InputParameters) > 0) {
  227. // Make sure other unbufferred queries are not open
  228. if (is_object($this->_CurrentResultSet)) {
  229. $this->_CurrentResultSet->Result();
  230. $this->_CurrentResultSet->FreePDOStatement(FALSE);
  231. }
  232. $PDOStatement = $this->Connection()->prepare($Sql);
  233. if (!is_object($PDOStatement)) {
  234. trigger_error(ErrorMessage('PDO Statement failed to prepare', $this->ClassName, 'Query', $this->GetPDOErrorMessage($this->Connection()->errorInfo())), E_USER_ERROR);
  235. } else if ($PDOStatement->execute($InputParameters) === FALSE) {
  236. trigger_error(ErrorMessage($this->GetPDOErrorMessage($PDOStatement->errorInfo()), $this->ClassName, 'Query', $Sql), E_USER_ERROR);
  237. }
  238. } else {
  239. $PDOStatement = $this->Connection()->query($Sql);
  240. }
  241. if ($PDOStatement === FALSE) {
  242. trigger_error(ErrorMessage($this->GetPDOErrorMessage($this->Connection()->errorInfo()), $this->ClassName, 'Query', $Sql), E_USER_ERROR);
  243. }
  244. // Did this query modify data in any way?
  245. if ($ReturnType == 'ID') {
  246. $this->_CurrentResultSet = $this->Connection()->lastInsertId();
  247. } else {
  248. if ($ReturnType == 'DataSet') {
  249. // Create a DataSet to manage the resultset
  250. $this->_CurrentResultSet = new Gdn_DataSet();
  251. $this->_CurrentResultSet->Connection = $this->Connection();
  252. $this->_CurrentResultSet->PDOStatement($PDOStatement);
  253. }
  254. }
  255. if (isset($StoreCacheKey)) {
  256. if ($CacheOperation == 'get')
  257. Gdn::Cache()->Store($StoreCacheKey, (($this->_CurrentResultSet instanceof Gdn_DataSet) ? $this->_CurrentResultSet->ResultArray() : $this->_CurrentResultSet));
  258. }
  259. return $this->_CurrentResultSet;
  260. }
  261. public function RollbackTransaction() {
  262. if($this->_InTransaction) {
  263. $this->_InTransaction = !$this->Connection()->rollBack();
  264. }
  265. }
  266. public function GetPDOErrorMessage($ErrorInfo) {
  267. $ErrorMessage = '';
  268. if (is_array($ErrorInfo)) {
  269. if (count($ErrorInfo) >= 2)
  270. $ErrorMessage = $ErrorInfo[2];
  271. elseif (count($ErrorInfo) >= 1)
  272. $ErrorMessage = $ErrorInfo[0];
  273. } elseif (is_string($ErrorInfo)) {
  274. $ErrorMessage = $ErrorInfo;
  275. }
  276. return $ErrorMessage;
  277. }
  278. /**
  279. * Get the database driver class for the database.
  280. * @return Gdn_SQLDriver The database driver class associated with this database.
  281. */
  282. public function SQL() {
  283. if(is_null($this->_SQL)) {
  284. $Name = $this->Engine . 'Driver';
  285. $this->_SQL = Gdn::Factory($Name);
  286. $this->_SQL->Database = $this;
  287. }
  288. return $this->_SQL;
  289. }
  290. /**
  291. * Get the database structure class for this database.
  292. *
  293. * @return Gdn_DatabaseStructure The database structure class for this database.
  294. */
  295. public function Structure() {
  296. if(is_null($this->_Structure)) {
  297. $Name = $this->Engine . 'Structure';
  298. $this->_Structure = Gdn::Factory($Name);
  299. $this->_Structure->Database = $this;
  300. }
  301. return $this->_Structure;
  302. }
  303. }