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.

471 lines
11KB

  1. <?php
  2. require_once("dbm.php");
  3. # if PDO is avaible for mysql, use the new database abstraction
  4. # TODO: PDO is disabled for release 3.3. We need to investigate why
  5. # the update from 3.2 fails with pdo
  6. /*
  7. if (class_exists('\PDO') && in_array('mysql', PDO::getAvailableDrivers())) {
  8. require_once("library/dddbl2/dddbl.php");
  9. require_once("include/dba_pdo.php");
  10. }
  11. */
  12. require_once('include/datetime.php');
  13. /**
  14. * @class MySQL database class
  15. *
  16. * For debugging, insert 'dbg(1);' anywhere in the program flow.
  17. * dbg(0); will turn it off. Logging is performed at LOGGER_DATA level.
  18. * When logging, all binary info is converted to text and html entities are escaped so that
  19. * the debugging stream is safe to view within both terminals and web pages.
  20. *
  21. */
  22. if (! class_exists('dba')) {
  23. class dba {
  24. private $debug = 0;
  25. private $db;
  26. private $result;
  27. public $mysqli = true;
  28. public $connected = false;
  29. public $error = false;
  30. function __construct($server, $user, $pass, $db, $install = false) {
  31. $a = get_app();
  32. $stamp1 = microtime(true);
  33. $server = trim($server);
  34. $user = trim($user);
  35. $pass = trim($pass);
  36. $db = trim($db);
  37. if (!(strlen($server) && strlen($user))) {
  38. $this->connected = false;
  39. $this->db = null;
  40. return;
  41. }
  42. if ($install) {
  43. if (strlen($server) && ($server !== 'localhost') && ($server !== '127.0.0.1')) {
  44. if (! dns_get_record($server, DNS_A + DNS_CNAME + DNS_PTR)) {
  45. $this->error = sprintf( t('Cannot locate DNS info for database server \'%s\''), $server);
  46. $this->connected = false;
  47. $this->db = null;
  48. return;
  49. }
  50. }
  51. }
  52. if (class_exists('mysqli')) {
  53. $this->db = @new mysqli($server,$user,$pass,$db);
  54. if (! mysqli_connect_errno()) {
  55. $this->connected = true;
  56. }
  57. if (isset($a->config["system"]["db_charset"])) {
  58. $this->db->set_charset($a->config["system"]["db_charset"]);
  59. }
  60. } else {
  61. $this->mysqli = false;
  62. $this->db = mysql_connect($server,$user,$pass);
  63. if ($this->db && mysql_select_db($db,$this->db)) {
  64. $this->connected = true;
  65. }
  66. if (isset($a->config["system"]["db_charset"]))
  67. mysql_set_charset($a->config["system"]["db_charset"], $this->db);
  68. }
  69. if (!$this->connected) {
  70. $this->db = null;
  71. if (!$install) {
  72. system_unavailable();
  73. }
  74. }
  75. $a->save_timestamp($stamp1, "network");
  76. }
  77. public function getdb() {
  78. return $this->db;
  79. }
  80. /**
  81. * @brief Returns the MySQL server version string
  82. *
  83. * This function discriminate between the deprecated mysql API and the current
  84. * object-oriented mysqli API. Example of returned string: 5.5.46-0+deb8u1
  85. *
  86. * @return string
  87. */
  88. public function server_info() {
  89. if ($this->mysqli) {
  90. $return = $this->db->server_info;
  91. } else {
  92. $return = mysql_get_server_info($this->db);
  93. }
  94. return $return;
  95. }
  96. /**
  97. * @brief Returns the selected database name
  98. *
  99. * @return string
  100. */
  101. public function database_name() {
  102. $r = $this->q("SELECT DATABASE() AS `db`");
  103. return $r[0]['db'];
  104. }
  105. /**
  106. * @brief Returns the number of rows
  107. *
  108. * @return integer
  109. */
  110. public function num_rows() {
  111. if (!$this->result) {
  112. return 0;
  113. }
  114. if ($this->mysqli) {
  115. $return = $this->result->num_rows;
  116. } else {
  117. $return = mysql_num_rows($this->result);
  118. }
  119. return $return;
  120. }
  121. public function q($sql, $onlyquery = false) {
  122. $a = get_app();
  123. if (!$this->db || !$this->connected) {
  124. return false;
  125. }
  126. $this->error = '';
  127. // Check the connection (This can reconnect the connection - if configured)
  128. if ($this->mysqli) {
  129. $connected = $this->db->ping();
  130. } else {
  131. $connected = mysql_ping($this->db);
  132. }
  133. $connstr = ($connected ? "Connected" : "Disonnected");
  134. $stamp1 = microtime(true);
  135. $orig_sql = $sql;
  136. if (x($a->config,'system') && x($a->config['system'], 'db_callstack')) {
  137. $sql = "/*".$a->callstack()." */ ".$sql;
  138. }
  139. if ($this->mysqli) {
  140. $result = @$this->db->query($sql);
  141. } else {
  142. $result = @mysql_query($sql,$this->db);
  143. }
  144. $stamp2 = microtime(true);
  145. $duration = (float)($stamp2-$stamp1);
  146. $a->save_timestamp($stamp1, "database");
  147. if (strtolower(substr($orig_sql, 0, 6)) != "select") {
  148. $a->save_timestamp($stamp1, "database_write");
  149. }
  150. if (x($a->config,'system') && x($a->config['system'],'db_log')) {
  151. if (($duration > $a->config["system"]["db_loglimit"])) {
  152. $duration = round($duration, 3);
  153. $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
  154. @file_put_contents($a->config["system"]["db_log"], datetime_convert()."\t".$duration."\t".
  155. basename($backtrace[1]["file"])."\t".
  156. $backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t".
  157. substr($sql, 0, 2000)."\n", FILE_APPEND);
  158. }
  159. }
  160. if ($this->mysqli) {
  161. if ($this->db->errno) {
  162. $this->error = $this->db->error;
  163. $this->errorno = $this->db->errno;
  164. }
  165. } elseif (mysql_errno($this->db)) {
  166. $this->error = mysql_error($this->db);
  167. $this->errorno = mysql_errno($this->db);
  168. }
  169. if (strlen($this->error)) {
  170. logger('DB Error ('.$connstr.') '.$this->errorno.': '.$this->error);
  171. }
  172. if ($this->debug) {
  173. $mesg = '';
  174. if ($result === false) {
  175. $mesg = 'false';
  176. } elseif ($result === true) {
  177. $mesg = 'true';
  178. } else {
  179. if ($this->mysqli) {
  180. $mesg = $result->num_rows . ' results' . EOL;
  181. } else {
  182. $mesg = mysql_num_rows($result) . ' results' . EOL;
  183. }
  184. }
  185. $str = 'SQL = ' . printable($sql) . EOL . 'SQL returned ' . $mesg
  186. . (($this->error) ? ' error: ' . $this->error : '')
  187. . EOL;
  188. logger('dba: ' . $str );
  189. }
  190. /**
  191. * If dbfail.out exists, we will write any failed calls directly to it,
  192. * regardless of any logging that may or may nor be in effect.
  193. * These usually indicate SQL syntax errors that need to be resolved.
  194. */
  195. if ($result === false) {
  196. logger('dba: ' . printable($sql) . ' returned false.' . "\n" . $this->error);
  197. if (file_exists('dbfail.out')) {
  198. file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . ' returned false' . "\n" . $this->error . "\n", FILE_APPEND);
  199. }
  200. }
  201. if (($result === true) || ($result === false)) {
  202. return $result;
  203. }
  204. if ($onlyquery) {
  205. $this->result = $result;
  206. return true;
  207. }
  208. $r = array();
  209. if ($this->mysqli) {
  210. if ($result->num_rows) {
  211. while($x = $result->fetch_array(MYSQLI_ASSOC))
  212. $r[] = $x;
  213. $result->free_result();
  214. }
  215. } else {
  216. if (mysql_num_rows($result)) {
  217. while($x = mysql_fetch_array($result, MYSQL_ASSOC))
  218. $r[] = $x;
  219. mysql_free_result($result);
  220. }
  221. }
  222. //$a->save_timestamp($stamp1, "database");
  223. if ($this->debug) {
  224. logger('dba: ' . printable(print_r($r, true)));
  225. }
  226. return($r);
  227. }
  228. public function qfetch() {
  229. $x = false;
  230. if ($this->result) {
  231. if ($this->mysqli) {
  232. if ($this->result->num_rows)
  233. $x = $this->result->fetch_array(MYSQLI_ASSOC);
  234. } else {
  235. if (mysql_num_rows($this->result))
  236. $x = mysql_fetch_array($this->result, MYSQL_ASSOC);
  237. }
  238. }
  239. return($x);
  240. }
  241. public function qclose() {
  242. if ($this->result) {
  243. if ($this->mysqli) {
  244. $this->result->free_result();
  245. } else {
  246. mysql_free_result($this->result);
  247. }
  248. }
  249. }
  250. public function dbg($dbg) {
  251. $this->debug = $dbg;
  252. }
  253. public function escape($str) {
  254. if ($this->db && $this->connected) {
  255. if ($this->mysqli) {
  256. return @$this->db->real_escape_string($str);
  257. } else {
  258. return @mysql_real_escape_string($str,$this->db);
  259. }
  260. }
  261. }
  262. function connected() {
  263. if ($this->mysqli) {
  264. $connected = $this->db->ping();
  265. } else {
  266. $connected = mysql_ping($this->db);
  267. }
  268. return $connected;
  269. }
  270. function __destruct() {
  271. if ($this->db) {
  272. if ($this->mysqli) {
  273. $this->db->close();
  274. } else {
  275. mysql_close($this->db);
  276. }
  277. }
  278. }
  279. }}
  280. if (! function_exists('printable')) {
  281. function printable($s) {
  282. $s = preg_replace("~([\x01-\x08\x0E-\x0F\x10-\x1F\x7F-\xFF])~",".", $s);
  283. $s = str_replace("\x00",'.',$s);
  284. if (x($_SERVER,'SERVER_NAME')) {
  285. $s = escape_tags($s);
  286. }
  287. return $s;
  288. }}
  289. // Procedural functions
  290. if (! function_exists('dbg')) {
  291. function dbg($state) {
  292. global $db;
  293. if ($db) {
  294. $db->dbg($state);
  295. }
  296. }}
  297. if (! function_exists('dbesc')) {
  298. function dbesc($str) {
  299. global $db;
  300. if ($db && $db->connected) {
  301. return($db->escape($str));
  302. } else {
  303. return(str_replace("'","\\'",$str));
  304. }
  305. }}
  306. // Function: q($sql,$args);
  307. // Description: execute SQL query with printf style args.
  308. // Example: $r = q("SELECT * FROM `%s` WHERE `uid` = %d",
  309. // 'user', 1);
  310. if (! function_exists('q')) {
  311. function q($sql) {
  312. global $db;
  313. $args = func_get_args();
  314. unset($args[0]);
  315. if ($db && $db->connected) {
  316. $stmt = @vsprintf($sql,$args); // Disabled warnings
  317. //logger("dba: q: $stmt", LOGGER_ALL);
  318. if ($stmt === false)
  319. logger('dba: vsprintf error: ' . print_r(debug_backtrace(),true), LOGGER_DEBUG);
  320. return $db->q($stmt);
  321. }
  322. /**
  323. *
  324. * This will happen occasionally trying to store the
  325. * session data after abnormal program termination
  326. *
  327. */
  328. logger('dba: no database: ' . print_r($args,true));
  329. return false;
  330. }}
  331. /**
  332. * @brief Performs a query with "dirty reads"
  333. *
  334. * By doing dirty reads (reading uncommitted data) no locks are performed
  335. * This function can be used to fetch data that doesn't need to be reliable.
  336. *
  337. * @param $args Query parameters (1 to N parameters of different types)
  338. * @return array Query array
  339. */
  340. function qu($sql) {
  341. global $db;
  342. $args = func_get_args();
  343. unset($args[0]);
  344. if ($db && $db->connected) {
  345. $stmt = @vsprintf($sql,$args); // Disabled warnings
  346. if ($stmt === false)
  347. logger('dba: vsprintf error: ' . print_r(debug_backtrace(),true), LOGGER_DEBUG);
  348. $db->q("SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
  349. $retval = $db->q($stmt);
  350. $db->q("SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;");
  351. return $retval;
  352. }
  353. /**
  354. *
  355. * This will happen occasionally trying to store the
  356. * session data after abnormal program termination
  357. *
  358. */
  359. logger('dba: no database: ' . print_r($args,true));
  360. return false;
  361. }
  362. /**
  363. *
  364. * Raw db query, no arguments
  365. *
  366. */
  367. if (! function_exists('dbq')) {
  368. function dbq($sql) {
  369. global $db;
  370. if ($db && $db->connected) {
  371. $ret = $db->q($sql);
  372. } else {
  373. $ret = false;
  374. }
  375. return $ret;
  376. }}
  377. // Caller is responsible for ensuring that any integer arguments to
  378. // dbesc_array are actually integers and not malformed strings containing
  379. // SQL injection vectors. All integer array elements should be specifically
  380. // cast to int to avoid trouble.
  381. if (! function_exists('dbesc_array_cb')) {
  382. function dbesc_array_cb(&$item, $key) {
  383. if (is_string($item))
  384. $item = dbesc($item);
  385. }}
  386. if (! function_exists('dbesc_array')) {
  387. function dbesc_array(&$arr) {
  388. if (is_array($arr) && count($arr)) {
  389. array_walk($arr,'dbesc_array_cb');
  390. }
  391. }}
  392. function dba_timer() {
  393. return microtime(true);
  394. }