Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

342 lines
8.7KB

  1. <?php
  2. require_once('include/datetime.php');
  3. $objDDDBLResultHandler = new \DDDBL\DataObjectPool('Result-Handler');
  4. /**
  5. * create handler, which returns just the PDOStatement object
  6. * this allows usage of the cursor to scroll through
  7. * big result-sets
  8. *
  9. **/
  10. $cloPDOStatementResultHandler = function(\DDDBL\Queue $objQueue) {
  11. $objPDO = $objQueue->getState()->get('PDOStatement');
  12. $objQueue->getState()->update(array('result' => $objPDO));
  13. # delete handler which closes the PDOStatement-cursor
  14. # this will be done manual if using this handler
  15. $objQueue->deleteHandler(QUEUE_CLOSE_CURSOR_POSITION);
  16. };
  17. $objDDDBLResultHandler->add('PDOStatement', array('HANDLER' => $cloPDOStatementResultHandler));
  18. /**
  19. *
  20. * MySQL database class
  21. *
  22. * For debugging, insert 'dbg(1);' anywhere in the program flow.
  23. * dbg(0); will turn it off. Logging is performed at LOGGER_DATA level.
  24. * When logging, all binary info is converted to text and html entities are escaped so that
  25. * the debugging stream is safe to view within both terminals and web pages.
  26. *
  27. */
  28. if(! class_exists('dba')) {
  29. class dba {
  30. private $debug = 0;
  31. private $db;
  32. private $result;
  33. public $connected = false;
  34. public $error = false;
  35. function __construct($server,$user,$pass,$db,$install = false) {
  36. $a = get_app();
  37. # work around, to store the database - configuration in DDDBL
  38. $objDataObjectPool = new \DDDBL\DataObjectPool('Database-Definition');
  39. $objDataObjectPool->add('DEFAULT', array('CONNECTION' => "mysql:host=$server;dbname=$db",
  40. 'USER' => $user,
  41. 'PASS' => $pass,
  42. 'DEFAULT' => true));
  43. $stamp1 = microtime(true);
  44. $server = trim($server);
  45. $user = trim($user);
  46. $pass = trim($pass);
  47. $db = trim($db);
  48. if (!(strlen($server) && strlen($user))){
  49. $this->connected = false;
  50. $this->db = null;
  51. return;
  52. }
  53. if($install) {
  54. if(strlen($server) && ($server !== 'localhost') && ($server !== '127.0.0.1')) {
  55. if(! dns_get_record($server, DNS_A + DNS_CNAME + DNS_PTR)) {
  56. $this->error = sprintf( t('Cannot locate DNS info for database server \'%s\''), $server);
  57. $this->connected = false;
  58. $this->db = null;
  59. return;
  60. }
  61. }
  62. }
  63. # etablish connection to database and store PDO object
  64. \DDDBL\connect();
  65. $this->db = \DDDBL\getDB();
  66. if(\DDDBL\isConnected()) {
  67. $this->connected = true;
  68. }
  69. if(! $this->connected) {
  70. $this->db = null;
  71. if(! $install)
  72. system_unavailable();
  73. }
  74. $a->save_timestamp($stamp1, "network");
  75. }
  76. public function getdb() {
  77. return $this->db;
  78. }
  79. public function q($sql, $onlyquery = false) {
  80. $a = get_app();
  81. $strHandler = (true === $onlyquery) ? 'PDOStatement' : 'MULTI';
  82. $strQueryAlias = md5($sql);
  83. $strSQLType = strtoupper(strstr($sql, ' ', true));
  84. $objPreparedQueryPool = new \DDDBL\DataObjectPool('Query-Definition');
  85. # check if query do not exists till now, if so create its definition
  86. if(!$objPreparedQueryPool->exists($strQueryAlias))
  87. $objPreparedQueryPool->add($strQueryAlias, array('QUERY' => $sql,
  88. 'HANDLER' => $strHandler));
  89. if((! $this->db) || (! $this->connected))
  90. return false;
  91. $this->error = '';
  92. $stamp1 = microtime(true);
  93. try {
  94. $r = \DDDBL\get($strQueryAlias);
  95. # bad workaround to emulate the bizzare behavior of mysql_query
  96. if(in_array($strSQLType, array('INSERT', 'UPDATE', 'DELETE', 'CREATE', 'DROP', 'SET')))
  97. $result = true;
  98. $intErrorCode = false;
  99. } catch (\Exception $objException) {
  100. $result = false;
  101. $intErrorCode = $objPreparedQueryPool->get($strQueryAlias)->get('PDOStatement')->errorCode();
  102. }
  103. $stamp2 = microtime(true);
  104. $duration = (float)($stamp2-$stamp1);
  105. $a->save_timestamp($stamp1, "database");
  106. if(x($a->config,'system') && x($a->config['system'],'db_log')) {
  107. if (($duration > $a->config["system"]["db_loglimit"])) {
  108. $duration = round($duration, 3);
  109. $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
  110. @file_put_contents($a->config["system"]["db_log"], datetime_convert()."\t".$duration."\t".
  111. basename($backtrace[1]["file"])."\t".
  112. $backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t".
  113. substr($sql, 0, 2000)."\n", FILE_APPEND);
  114. }
  115. }
  116. if($intErrorCode)
  117. $this->error = $intErrorCode;
  118. if(strlen($this->error)) {
  119. logger('dba: ' . $this->error);
  120. }
  121. if($this->debug) {
  122. $mesg = '';
  123. if($result === false)
  124. $mesg = 'false';
  125. elseif($result === true)
  126. $mesg = 'true';
  127. else {
  128. # this needs fixing, but is a bug itself
  129. #$mesg = mysql_num_rows($result) . ' results' . EOL;
  130. }
  131. $str = 'SQL = ' . printable($sql) . EOL . 'SQL returned ' . $mesg
  132. . (($this->error) ? ' error: ' . $this->error : '')
  133. . EOL;
  134. logger('dba: ' . $str );
  135. }
  136. /**
  137. * If dbfail.out exists, we will write any failed calls directly to it,
  138. * regardless of any logging that may or may nor be in effect.
  139. * These usually indicate SQL syntax errors that need to be resolved.
  140. */
  141. if(isset($result) AND ($result === false)) {
  142. logger('dba: ' . printable($sql) . ' returned false.' . "\n" . $this->error);
  143. if(file_exists('dbfail.out'))
  144. file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . ' returned false' . "\n" . $this->error . "\n", FILE_APPEND);
  145. }
  146. if(isset($result) AND (($result === true) || ($result === false)))
  147. return $result;
  148. if ($onlyquery) {
  149. $this->result = $r; # this will store an PDOStatement Object in result
  150. $this->result->execute(); # execute the Statement, to get its result
  151. return true;
  152. }
  153. //$a->save_timestamp($stamp1, "database");
  154. if($this->debug)
  155. logger('dba: ' . printable(print_r($r, true)));
  156. return($r);
  157. }
  158. public function qfetch() {
  159. if (false === $this->result)
  160. return false;
  161. return $this->result->fetch();
  162. }
  163. public function qclose() {
  164. if ($this->result)
  165. return $this->result->closeCursor();
  166. }
  167. public function dbg($dbg) {
  168. $this->debug = $dbg;
  169. }
  170. public function escape($str) {
  171. if($this->db && $this->connected) {
  172. $strQuoted = $this->db->quote($str);
  173. # this workaround is needed, because quote creates "'" and the beginning and the end
  174. # of the string, which is correct. but until now the queries set this delimiter manually,
  175. # so we must remove them from here and wait until everything uses prepared statements
  176. return mb_substr($strQuoted, 1, mb_strlen($strQuoted) - 2);
  177. }
  178. }
  179. function __destruct() {
  180. if ($this->db)
  181. \DDDBL\disconnect();
  182. }
  183. }}
  184. if(! function_exists('printable')) {
  185. function printable($s) {
  186. $s = preg_replace("~([\x01-\x08\x0E-\x0F\x10-\x1F\x7F-\xFF])~",".", $s);
  187. $s = str_replace("\x00",'.',$s);
  188. if(x($_SERVER,'SERVER_NAME'))
  189. $s = escape_tags($s);
  190. return $s;
  191. }}
  192. // Procedural functions
  193. if(! function_exists('dbg')) {
  194. function dbg($state) {
  195. global $db;
  196. if($db)
  197. $db->dbg($state);
  198. }}
  199. if(! function_exists('dbesc')) {
  200. function dbesc($str) {
  201. global $db;
  202. if($db && $db->connected)
  203. return($db->escape($str));
  204. else
  205. return(str_replace("'","\\'",$str));
  206. }}
  207. // Function: q($sql,$args);
  208. // Description: execute SQL query with printf style args.
  209. // Example: $r = q("SELECT * FROM `%s` WHERE `uid` = %d",
  210. // 'user', 1);
  211. if(! function_exists('q')) {
  212. function q($sql) {
  213. global $db;
  214. $args = func_get_args();
  215. unset($args[0]);
  216. if($db && $db->connected) {
  217. $stmt = @vsprintf($sql,$args); // Disabled warnings
  218. //logger("dba: q: $stmt", LOGGER_ALL);
  219. if($stmt === false)
  220. logger('dba: vsprintf error: ' . print_r(debug_backtrace(),true), LOGGER_DEBUG);
  221. return $db->q($stmt);
  222. }
  223. /**
  224. *
  225. * This will happen occasionally trying to store the
  226. * session data after abnormal program termination
  227. *
  228. */
  229. logger('dba: no database: ' . print_r($args,true));
  230. return false;
  231. }}
  232. /**
  233. *
  234. * Raw db query, no arguments
  235. *
  236. */
  237. if(! function_exists('dbq')) {
  238. function dbq($sql) {
  239. global $db;
  240. if($db && $db->connected)
  241. $ret = $db->q($sql);
  242. else
  243. $ret = false;
  244. return $ret;
  245. }}
  246. // Caller is responsible for ensuring that any integer arguments to
  247. // dbesc_array are actually integers and not malformed strings containing
  248. // SQL injection vectors. All integer array elements should be specifically
  249. // cast to int to avoid trouble.
  250. if(! function_exists('dbesc_array_cb')) {
  251. function dbesc_array_cb(&$item, $key) {
  252. if(is_string($item))
  253. $item = dbesc($item);
  254. }}
  255. if(! function_exists('dbesc_array')) {
  256. function dbesc_array(&$arr) {
  257. if(is_array($arr) && count($arr)) {
  258. array_walk($arr,'dbesc_array_cb');
  259. }
  260. }}
  261. if(! function_exists('dba_timer')) {
  262. function dba_timer() {
  263. return microtime(true);
  264. }}