PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/dao/class.PDODAO.php

http://github.com/ginatrapani/isosceles
PHP | 358 lines | 196 code | 19 blank | 143 comment | 24 complexity | ffda27928919ab064b2d7f39e486e39c MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1, LGPL-3.0, Apache-2.0, BSD-3-Clause
  1. <?php
  2. /**
  3. * LICENSE:
  4. *
  5. * This file is part of Isosceles (http://ginatrapani.github.io/isosceles/).
  6. *
  7. * Isosceles is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
  8. * License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any
  9. * later version.
  10. *
  11. * Isosceles is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
  12. * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  13. * details.
  14. *
  15. * You should have received a copy of the GNU General Public License along with Isosceles. If not, see
  16. * <http://www.gnu.org/licenses/>.
  17. *
  18. * @license http://www.gnu.org/licenses/gpl.html
  19. */
  20. abstract class PDODAO {
  21. /**
  22. * Configuration
  23. * @var Config Object
  24. */
  25. var $config;
  26. /**
  27. * PDO instance
  28. * @var PDO Object
  29. */
  30. static $PDO = null;
  31. /**
  32. * Table Prefix
  33. * @var str
  34. */
  35. static $prefix;
  36. /**
  37. * GMT offset
  38. * @var int
  39. */
  40. static $gmt_offset;
  41. /**
  42. *
  43. * @var bool
  44. */
  45. protected $profiler_enabled = false;
  46. /**
  47. * Constructor
  48. * @param array $cfg_vals Optionally override config.inc.php vals; needs 'table_prefix', 'GMT_offset', 'db_type',
  49. * 'db_socket', 'db_name', 'db_host', 'db_user', 'db_password'
  50. * @return PDODAO
  51. */
  52. public function __construct($cfg_vals=null){
  53. $this->config = Config::getInstance($cfg_vals);
  54. if (is_null(self::$PDO)) {
  55. $this->connect();
  56. }
  57. self::$prefix = $this->config->getValue('table_prefix');
  58. self::$gmt_offset = $this->config->getGMTOffset();
  59. $this->profiler_enabled = Profiler::isEnabled();
  60. }
  61. /**
  62. * Connection initiator
  63. */
  64. public final function connect(){
  65. if (is_null(self::$PDO)) {
  66. self::$PDO = new PDO(
  67. self::getConnectString($this->config),
  68. $this->config->getValue('db_user'),
  69. $this->config->getValue('db_password')
  70. );
  71. self::$PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  72. // if THINKUP_CFG var 'set_pdo_charset' is set to true, set the connection charset to utf8
  73. if ($this->config->getValue('set_pdo_charset')) {
  74. self::$PDO->exec('SET CHARACTER SET utf8');
  75. }
  76. $timezone = $this->config->getValue('timezone');
  77. $time = new DateTime("now", new DateTimeZone($timezone) );
  78. $tz_offset = $time->format('P');
  79. try {
  80. self::$PDO->exec("SET time_zone = '$timezone'");
  81. } catch (PDOException $e) {
  82. // Time zone couldn't be set; use offset instead, but if the timezone is one for which offset changes
  83. // throughout year, such as during daylight saving time, dates converted from/to UTC by the database
  84. // from a different offset will be incorrect.
  85. self::$PDO->exec("SET time_zone = '$tz_offset'");
  86. }
  87. }
  88. }
  89. /**
  90. * Generates a connect string to use when creating a PDO object.
  91. * @param Config $config
  92. * @return string PDO connect string
  93. */
  94. public static function getConnectString($config) {
  95. //set default db type to mysql if not set
  96. $db_type = $config->getValue('db_type');
  97. if (! $db_type) { $db_type = 'mysql'; }
  98. $db_socket = $config->getValue('db_socket');
  99. if ( !$db_socket) {
  100. $db_port = $config->getValue('db_port');
  101. if (!$db_port) {
  102. $db_socket = '';
  103. } else {
  104. $db_socket = ";port=".$config->getValue('db_port');
  105. }
  106. } else {
  107. $db_socket=";unix_socket=".$db_socket;
  108. }
  109. $db_string = sprintf(
  110. "%s:dbname=%s;host=%s%s",
  111. $db_type,
  112. $config->getValue('db_name'),
  113. $config->getValue('db_host'),
  114. $db_socket
  115. );
  116. return $db_string;
  117. }
  118. /**
  119. * Disconnector
  120. * Caution! This will disconnect for ALL DAOs
  121. */
  122. protected final function disconnect(){
  123. self::$PDO = null;
  124. }
  125. /**
  126. * Executes the query, with the bound values
  127. * @param str $sql
  128. * @param array $binds
  129. * @return PDOStatement
  130. */
  131. protected final function execute($sql, $binds = array()) {
  132. if ($this->profiler_enabled) {
  133. $start_time = microtime(true);
  134. }
  135. $sql = preg_replace("/#prefix#/", self::$prefix, $sql);
  136. $sql = preg_replace("/#gmt_offset#/", self::$gmt_offset, $sql);
  137. $stmt = self::$PDO->prepare($sql);
  138. if (is_array($binds) and count($binds) >= 1) {
  139. foreach ($binds as $key => $value) {
  140. if (is_int($value)) {
  141. $stmt->bindValue($key, $value, PDO::PARAM_INT);
  142. } else {
  143. $stmt->bindValue($key, $value, PDO::PARAM_STR);
  144. }
  145. }
  146. }
  147. try {
  148. $stmt->execute();
  149. } catch (PDOException $e) {
  150. $config = Config::getInstance();
  151. $exception_details = 'Database error! ';
  152. if ($config->getValue('debug')) {
  153. $exception_details .= 'Could not execute the following query: '.
  154. str_replace(chr(10), "", $stmt->queryString) . ' PDOException: '. $e->getMessage();
  155. } else {
  156. error_log('Could not execute the following query:
  157. '.
  158. self::mergeSQLVars($stmt->queryString, $binds) . '
  159. PDOException: '. $e->getMessage());
  160. }
  161. throw new PDOException ($exception_details);
  162. }
  163. if ($this->profiler_enabled) {
  164. $end_time = microtime(true);
  165. $total_time = $end_time - $start_time;
  166. $profiler = Profiler::getInstance();
  167. $sql_with_params = self::mergeSQLVars($stmt->queryString, $binds);
  168. $profiler->add($total_time, $sql_with_params, true, $stmt->rowCount());
  169. }
  170. return $stmt;
  171. }
  172. /**
  173. * Proxy for getUpdateCount
  174. * @param PDOStatement $ps
  175. * @return int Update Count
  176. */
  177. protected final function getDeleteCount($ps){
  178. //Alias for getUpdateCount
  179. return $this->getUpdateCount($ps);
  180. }
  181. /**
  182. * Gets a single row and closes cursor.
  183. * @param PDOStatement $ps
  184. * @return various array,object depending on context
  185. */
  186. protected final function fetchAndClose($ps){
  187. $row = $ps->fetch();
  188. $ps->closeCursor();
  189. return $row;
  190. }
  191. /**
  192. * Gets a multiple rows and closes cursor.
  193. * @param PDOStatement $ps
  194. * @return array of arrays/objects depending on context
  195. */
  196. protected final function fetchAllAndClose($ps){
  197. $rows = $ps->fetchAll();
  198. $ps->closeCursor();
  199. return $rows;
  200. }
  201. /**
  202. * Gets the rows returned by a statement as array of objects.
  203. * @param PDOStatement $ps
  204. * @param str $obj
  205. * @return array numbered keys, with objects
  206. */
  207. protected final function getDataRowAsObject($ps, $obj){
  208. $ps->setFetchMode(PDO::FETCH_CLASS,$obj);
  209. $row = $this->fetchAndClose($ps);
  210. if (!$row){
  211. $row = null;
  212. }
  213. return $row;
  214. }
  215. /**
  216. * Gets the first returned row as array
  217. * @param PDOStatement $ps
  218. * @return array named keys
  219. */
  220. protected final function getDataRowAsArray($ps){
  221. $ps->setFetchMode(PDO::FETCH_ASSOC);
  222. $row = $this->fetchAndClose($ps);
  223. if (!$row){
  224. $row = null;
  225. }
  226. return $row;
  227. }
  228. /**
  229. * Returns the first row as an object
  230. * @param PDOStatement $ps
  231. * @param str $obj
  232. * @return array numbered keys, with Objects
  233. */
  234. protected final function getDataRowsAsObjects($ps, $obj){
  235. $ps->setFetchMode(PDO::FETCH_CLASS,$obj);
  236. $data = $this->fetchAllAndClose($ps);
  237. return $data;
  238. }
  239. /**
  240. * Gets the rows returned by a statement as array with arrays
  241. * @param PDOStatement $ps
  242. * @return array numbered keys, with array named keys
  243. */
  244. protected final function getDataRowsAsArrays($ps){
  245. $ps->setFetchMode(PDO::FETCH_ASSOC);
  246. $data = $this->fetchAllAndClose($ps);
  247. return $data;
  248. }
  249. /**
  250. * Gets the result returned by a count query
  251. * (value of col count on first row)
  252. * @param PDOStatement $ps
  253. * @param int Count
  254. */
  255. protected final function getDataCountResult($ps){
  256. $ps->setFetchMode(PDO::FETCH_ASSOC);
  257. $row = $this->fetchAndClose($ps);
  258. if (!$row or !isset($row['count'])){
  259. $count = 0;
  260. } else {
  261. $count = (int) $row['count'];
  262. }
  263. return $count;
  264. }
  265. /**
  266. * Gets whether a statement returned anything
  267. * @param PDOStatement $ps
  268. * @return bool True if row(s) are returned
  269. */
  270. protected final function getDataIsReturned($ps){
  271. $row = $this->fetchAndClose($ps);
  272. $ret = false;
  273. if ($row && count($row) > 0) {
  274. $ret = true;
  275. }
  276. return $ret;
  277. }
  278. /**
  279. * Gets data "insert ID" from a statement
  280. * @param PDOStatement $ps
  281. * @return int|bool Inserted ID or false if there is none.
  282. */
  283. protected final function getInsertId($ps){
  284. $rc = $this->getUpdateCount($ps);
  285. $id = self::$PDO->lastInsertId();
  286. if ($rc > 0 and $id > 0) {
  287. return $id;
  288. } else {
  289. return false;
  290. }
  291. }
  292. /**
  293. * Proxy for getUpdateCount
  294. * @param PDOStatement $ps
  295. * @return int Insert count
  296. */
  297. protected final function getInsertCount($ps){
  298. //Alias for getUpdateCount
  299. return $this->getUpdateCount($ps);
  300. }
  301. /**
  302. * Get the number of updated rows
  303. * @param PDOStatement $ps
  304. * @return int Update Count
  305. */
  306. protected final function getUpdateCount($ps){
  307. $num = $ps->rowCount();
  308. $ps->closeCursor();
  309. return $num;
  310. }
  311. /**
  312. * Converts any form of "boolean" value to a Database usable one
  313. * @internal
  314. * @param mixed $val
  315. * @return int 0 or 1 (false or true)
  316. */
  317. protected final function convertBoolToDB($val){
  318. return $val ? 1 : 0;
  319. }
  320. /**
  321. * Converts a Database boolean to a PHP boolean
  322. * @param int $val
  323. * @return bool
  324. */
  325. public final static function convertDBToBool($val){
  326. return $val == 0 ? false : true;
  327. }
  328. private static function mergeSQLVars($sql, $vars) {
  329. foreach ($vars as $k => $v) {
  330. $sql = str_replace($k, (is_int($v))?$v:"'".$v."'", $sql);
  331. }
  332. $config = Config::getInstance();
  333. $prefix = $config->getValue('table_prefix');
  334. $gmt_offset = $config->getGMTOffset();
  335. $sql = str_replace('#gmt_offset#', $gmt_offset, $sql);
  336. $sql = str_replace('#prefix#', $prefix, $sql);
  337. return $sql;
  338. }
  339. }