]> git.mxchange.org Git - friendica.git/blob - include/dba.php
New function to calculate rows
[friendica.git] / include / dba.php
1 <?php
2 require_once("dbm.php");
3 require_once('include/datetime.php');
4
5 /**
6  * @class MySQL database class
7  *
8  * For debugging, insert 'dbg(1);' anywhere in the program flow.
9  * dbg(0); will turn it off. Logging is performed at LOGGER_DATA level.
10  * When logging, all binary info is converted to text and html entities are escaped so that
11  * the debugging stream is safe to view within both terminals and web pages.
12  *
13  */
14
15 class dba {
16
17         private $debug = 0;
18         private $db;
19         private $result;
20         private $driver;
21         public  $connected = false;
22         public  $error = false;
23         private $_server_info = '';
24         private static $dbo;
25
26         function __construct($server, $user, $pass, $db, $install = false) {
27                 $a = get_app();
28
29                 $stamp1 = microtime(true);
30
31                 $server = trim($server);
32                 $user = trim($user);
33                 $pass = trim($pass);
34                 $db = trim($db);
35
36                 if (!(strlen($server) && strlen($user))) {
37                         $this->connected = false;
38                         $this->db = null;
39                         return;
40                 }
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
53                 if (class_exists('\PDO') && in_array('mysql', PDO::getAvailableDrivers())) {
54                         $this->driver = 'pdo';
55                         $connect = "mysql:host=".$server.";dbname=".$db;
56                         if (isset($a->config["system"]["db_charset"])) {
57                                 $connect .= ";charset=".$a->config["system"]["db_charset"];
58                         }
59                         $this->db = @new PDO($connect, $user, $pass);
60                         if (!$this->db->errorCode()) {
61                                 $this->connected = true;
62                         }
63                 } elseif (class_exists('mysqli')) {
64                         $this->driver = 'mysqli';
65                         $this->db = @new mysqli($server,$user,$pass,$db);
66                         if (!mysqli_connect_errno()) {
67                                 $this->connected = true;
68
69                                 if (isset($a->config["system"]["db_charset"])) {
70                                         $this->db->set_charset($a->config["system"]["db_charset"]);
71                                 }
72                         }
73                 } elseif (function_exists('mysql_connect')) {
74                         $this->driver = 'mysql';
75                         $this->db = mysql_connect($server,$user,$pass);
76                         if ($this->db && mysql_select_db($db,$this->db)) {
77                                 $this->connected = true;
78
79                                 if (isset($a->config["system"]["db_charset"])) {
80                                         mysql_set_charset($a->config["system"]["db_charset"], $this->db);
81                                 }
82                         }
83                 } else {
84                         // No suitable SQL driver was found.
85                         if (!$install) {
86                                 system_unavailable();
87                         }
88                 }
89
90                 if (!$this->connected) {
91                         $this->db = null;
92                         if (!$install) {
93                                 system_unavailable();
94                         }
95                 }
96                 $a->save_timestamp($stamp1, "network");
97
98                 self::$dbo = $this;
99         }
100
101         /**
102          * @brief Returns the MySQL server version string
103          * 
104          * This function discriminate between the deprecated mysql API and the current
105          * object-oriented mysqli API. Example of returned string: 5.5.46-0+deb8u1
106          *
107          * @return string
108          */
109         public function server_info() {
110                 if ($this->_server_info == '') {
111                         switch ($this->driver) {
112                                 case 'pdo':
113                                         $this->_server_info = $this->db->getAttribute(PDO::ATTR_SERVER_VERSION);
114                                         break;
115                                 case 'mysqli':
116                                         $this->_server_info = $this->db->server_info;
117                                         break;
118                                 case 'mysql':
119                                         $this->_server_info = mysql_get_server_info($this->db);
120                                         break;
121                         }
122                 }
123                 return $this->_server_info;
124         }
125
126         /**
127          * @brief Returns the selected database name
128          *
129          * @return string
130          */
131         public function database_name() {
132                 $r = $this->q("SELECT DATABASE() AS `db`");
133
134                 return $r[0]['db'];
135         }
136
137         /**
138          * @brief Returns the number of rows
139          *
140          * @return integer
141          */
142         public function num_rows() {
143                 if (!$this->result) {
144                         return 0;
145                 }
146
147                 switch ($this->driver) {
148                         case 'pdo':
149                                 $rows = $this->result->rowCount();
150                                 break;
151                         case 'mysqli':
152                                 $rows = $this->result->num_rows;
153                                 break;
154                         case 'mysql':
155                                 $rows = mysql_num_rows($this->result);
156                                 break;
157                 }
158                 return $rows;
159         }
160
161         /**
162          * @brief Analyze a database query and log this if some conditions are met.
163          *
164          * @param string $query The database query that will be analyzed
165          */
166         public function log_index($query) {
167                 $a = get_app();
168
169                 if ($a->config["system"]["db_log_index"] == "") {
170                         return;
171                 }
172
173                 // Don't explain an explain statement
174                 if (strtolower(substr($query, 0, 7)) == "explain") {
175                         return;
176                 }
177
178                 // Only do the explain on "select", "update" and "delete"
179                 if (!in_array(strtolower(substr($query, 0, 6)), array("select", "update", "delete"))) {
180                         return;
181                 }
182
183                 $r = $this->q("EXPLAIN ".$query);
184                 if (!dbm::is_result($r)) {
185                         return;
186                 }
187
188                 $watchlist = explode(',', $a->config["system"]["db_log_index_watch"]);
189                 $blacklist = explode(',', $a->config["system"]["db_log_index_blacklist"]);
190
191                 foreach ($r AS $row) {
192                         if ((intval($a->config["system"]["db_loglimit_index"]) > 0)) {
193                                 $log = (in_array($row['key'], $watchlist) AND
194                                         ($row['rows'] >= intval($a->config["system"]["db_loglimit_index"])));
195                         } else {
196                                 $log = false;
197                         }
198
199                         if ((intval($a->config["system"]["db_loglimit_index_high"]) > 0) AND ($row['rows'] >= intval($a->config["system"]["db_loglimit_index_high"]))) {
200                                 $log = true;
201                         }
202
203                         if (in_array($row['key'], $blacklist) OR ($row['key'] == "")) {
204                                 $log = false;
205                         }
206
207                         if ($log) {
208                                 $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
209                                 @file_put_contents($a->config["system"]["db_log_index"], datetime_convert()."\t".
210                                                 $row['key']."\t".$row['rows']."\t".$row['Extra']."\t".
211                                                 basename($backtrace[1]["file"])."\t".
212                                                 $backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t".
213                                                 substr($query, 0, 2000)."\n", FILE_APPEND);
214                         }
215                 }
216         }
217
218         public function q($sql, $onlyquery = false) {
219                 $a = get_app();
220
221                 if (!$this->db || !$this->connected) {
222                         return false;
223                 }
224
225                 $this->error = '';
226
227                 $connstr = ($this->connected() ? "Connected" : "Disonnected");
228
229                 $stamp1 = microtime(true);
230
231                 $orig_sql = $sql;
232
233                 if (x($a->config,'system') && x($a->config['system'], 'db_callstack')) {
234                         $sql = "/*".$a->callstack()." */ ".$sql;
235                 }
236
237                 $columns = 0;
238
239                 switch ($this->driver) {
240                         case 'pdo':
241                                 $result = @$this->db->query($sql);
242                                 // Is used to separate between queries that returning data - or not
243                                 if (!is_bool($result)) {
244                                         $columns = $result->columnCount();
245                                 }
246                                 break;
247                         case 'mysqli':
248                                 $result = @$this->db->query($sql);
249                                 break;
250                         case 'mysql':
251                                 $result = @mysql_query($sql,$this->db);
252                                 break;
253                 }
254                 $stamp2 = microtime(true);
255                 $duration = (float)($stamp2 - $stamp1);
256
257                 $a->save_timestamp($stamp1, "database");
258
259                 if (strtolower(substr($orig_sql, 0, 6)) != "select") {
260                         $a->save_timestamp($stamp1, "database_write");
261                 }
262                 if (x($a->config,'system') && x($a->config['system'],'db_log')) {
263                         if (($duration > $a->config["system"]["db_loglimit"])) {
264                                 $duration = round($duration, 3);
265                                 $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
266                                 @file_put_contents($a->config["system"]["db_log"], datetime_convert()."\t".$duration."\t".
267                                                 basename($backtrace[1]["file"])."\t".
268                                                 $backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t".
269                                                 substr($sql, 0, 2000)."\n", FILE_APPEND);
270                         }
271                 }
272
273                 switch ($this->driver) {
274                         case 'pdo':
275                                 $errorInfo = $this->db->errorInfo();
276                                 if ($errorInfo) {
277                                         $this->error = $errorInfo[2];
278                                         $this->errorno = $errorInfo[1];
279                                 }
280                                 break;
281                         case 'mysqli':
282                                 if ($this->db->errno) {
283                                         $this->error = $this->db->error;
284                                         $this->errorno = $this->db->errno;
285                                 }
286                                 break;
287                         case 'mysql':
288                                 if (mysql_errno($this->db)) {
289                                         $this->error = mysql_error($this->db);
290                                         $this->errorno = mysql_errno($this->db);
291                                 }
292                                 break;
293                 }
294                 if (strlen($this->error)) {
295                         logger('DB Error ('.$connstr.') '.$this->errorno.': '.$this->error);
296                 }
297
298                 if ($this->debug) {
299
300                         $mesg = '';
301
302                         if ($result === false) {
303                                 $mesg = 'false';
304                         } elseif ($result === true) {
305                                 $mesg = 'true';
306                         } else {
307                                 switch ($this->driver) {
308                                         case 'pdo':
309                                                 $mesg = $result->rowCount().' results'.EOL;
310                                                 break;
311                                         case 'mysqli':
312                                                 $mesg = $result->num_rows.' results'.EOL;
313                                                 break;
314                                         case 'mysql':
315                                                 $mesg = mysql_num_rows($result).' results'.EOL;
316                                                 break;
317                                 }
318                         }
319
320                         $str =  'SQL = ' . printable($sql) . EOL . 'SQL returned ' . $mesg
321                                 . (($this->error) ? ' error: ' . $this->error : '')
322                                 . EOL;
323
324                         logger('dba: ' . $str );
325                 }
326
327                 /**
328                  * If dbfail.out exists, we will write any failed calls directly to it,
329                  * regardless of any logging that may or may nor be in effect.
330                  * These usually indicate SQL syntax errors that need to be resolved.
331                  */
332
333                 if ($result === false) {
334                         logger('dba: ' . printable($sql) . ' returned false.' . "\n" . $this->error);
335                         if (file_exists('dbfail.out')) {
336                                 file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . ' returned false' . "\n" . $this->error . "\n", FILE_APPEND);
337                         }
338                 }
339
340                 if (is_bool($result)) {
341                         return $result;
342                 }
343                 if ($onlyquery) {
344                         $this->result = $result;
345                         return true;
346                 }
347
348                 $r = array();
349                 switch ($this->driver) {
350                         case 'pdo':
351                                 while ($x = $result->fetch(PDO::FETCH_ASSOC)) {
352                                         $r[] = $x;
353                                 }
354                                 $result->closeCursor();
355                                 break;
356                         case 'mysqli':
357                                 while ($x = $result->fetch_array(MYSQLI_ASSOC)) {
358                                         $r[] = $x;
359                                 }
360                                 $result->free_result();
361                                 break;
362                         case 'mysql':
363                                 while ($x = mysql_fetch_array($result, MYSQL_ASSOC)) {
364                                         $r[] = $x;
365                                 }
366                                 mysql_free_result($result);
367                                 break;
368                 }
369
370                 // PDO doesn't return "true" on successful operations - like mysqli does
371                 // Emulate this behaviour by checking if the query returned data and had columns
372                 // This should be reliable enough
373                 if (($this->driver == 'pdo') AND (count($r) == 0) AND ($columns == 0)) {
374                         return true;
375                 }
376
377                 //$a->save_timestamp($stamp1, "database");
378
379                 if ($this->debug) {
380                         logger('dba: ' . printable(print_r($r, true)));
381                 }
382                 return($r);
383         }
384
385         public function qfetch() {
386                 $x = false;
387
388                 if ($this->result) {
389                         switch ($this->driver) {
390                                 case 'pdo':
391                                         $x = $this->result->fetch(PDO::FETCH_ASSOC);
392                                         break;
393                                 case 'mysqli':
394                                         $x = $this->result->fetch_array(MYSQLI_ASSOC);
395                                         break;
396                                 case 'mysql':
397                                         $x = mysql_fetch_array($this->result, MYSQL_ASSOC);
398                                         break;
399                         }
400                 }
401                 return($x);
402         }
403
404         public function qclose() {
405                 if ($this->result) {
406                         switch ($this->driver) {
407                                 case 'pdo':
408                                         $this->result->closeCursor();
409                                         break;
410                                 case 'mysqli':
411                                         $this->result->free_result();
412                                         break;
413                                 case 'mysql':
414                                         mysql_free_result($this->result);
415                                         break;
416                         }
417                 }
418         }
419
420         public function dbg($dbg) {
421                 $this->debug = $dbg;
422         }
423
424         public function escape($str) {
425                 if ($this->db && $this->connected) {
426                         switch ($this->driver) {
427                                 case 'pdo':
428                                         return substr(@$this->db->quote($str, PDO::PARAM_STR), 1, -1);
429                                 case 'mysqli':
430                                         return @$this->db->real_escape_string($str);
431                                 case 'mysql':
432                                         return @mysql_real_escape_string($str,$this->db);
433                         }
434                 }
435         }
436
437         function connected() {
438                 switch ($this->driver) {
439                         case 'pdo':
440                                 // Not sure if this really is working like expected
441                                 $connected = ($this->db->getAttribute(PDO::ATTR_CONNECTION_STATUS) != "");
442                                 break;
443                         case 'mysqli':
444                                 $connected = $this->db->ping();
445                                 break;
446                         case 'mysql':
447                                 $connected = mysql_ping($this->db);
448                                 break;
449                 }
450                 return $connected;
451         }
452
453         function insert_id() {
454                 switch ($this->driver) {
455                         case 'pdo':
456                                 $id = $this->db->lastInsertId();
457                                 break;
458                         case 'mysqli':
459                                 $id = $this->db->insert_id;
460                                 break;
461                         case 'mysql':
462                                 $id = mysql_insert_id($this->db);
463                                 break;
464                 }
465                 return $id;
466         }
467
468         function __destruct() {
469                 if ($this->db) {
470                         switch ($this->driver) {
471                                 case 'pdo':
472                                         $this->db = null;
473                                         break;
474                                 case 'mysqli':
475                                         $this->db->close();
476                                         break;
477                                 case 'mysql':
478                                         mysql_close($this->db);
479                                         break;
480                         }
481                 }
482         }
483
484         /**
485          * @brief Replaces ANY_VALUE() function by MIN() function,
486          *  if the database server does not support ANY_VALUE().
487          *
488          * Considerations for Standard SQL, or MySQL with ONLY_FULL_GROUP_BY (default since 5.7.5).
489          * ANY_VALUE() is available from MySQL 5.7.5 https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
490          * A standard fall-back is to use MIN().
491          *
492          * @param string $sql An SQL string without the values
493          * @return string The input SQL string modified if necessary.
494          */
495         public function any_value_fallback($sql) {
496                 $server_info = $this->server_info();
497                 if (version_compare($server_info, '5.7.5', '<') ||
498                         (stripos($server_info, 'MariaDB') !== false)) {
499                         $sql = str_ireplace('ANY_VALUE(', 'MIN(', $sql);
500                 }
501                 return $sql;
502         }
503
504         /**
505          * @brief Executes a prepared statement
506          *
507          * @param string $sql SQL statement
508          * @return object statement object
509          */
510         static public function p($sql) {
511                 $a = get_app();
512
513                 $stamp1 = microtime(true);
514
515                 $args = func_get_args();
516                 unset($args[0]);
517
518                 if (!self::$dbo OR !self::$dbo->connected) {
519                         return false;
520                 }
521
522                 $sql = self::$dbo->any_value_fallback($sql);
523
524                 $orig_sql = $sql;
525
526                 if (x($a->config,'system') && x($a->config['system'], 'db_callstack')) {
527                         $sql = "/*".$a->callstack()." */ ".$sql;
528                 }
529
530                 switch (self::$dbo->driver) {
531                         case 'pdo':
532                                 if (!$stmt = self::$dbo->db->prepare($sql)) {
533                                         $errorInfo = self::$dbo->db->errorInfo();
534                                         self::$dbo->error = $errorInfo[2];
535                                         self::$dbo->errorno = $errorInfo[1];
536                                         $retval = false;
537                                         break;
538                                 }
539
540                                 foreach ($args AS $param => $value) {
541                                         $stmt->bindParam($param, $args[$param]);
542                                 }
543
544                                 if (!$stmt->execute()) {
545                                         $errorInfo = self::$dbo->db->errorInfo();
546                                         self::$dbo->error = $errorInfo[2];
547                                         self::$dbo->errorno = $errorInfo[1];
548                                         $retval = false;
549                                 } else {
550                                         $retval = $stmt;
551                                 }
552                                 break;
553                         case 'mysqli':
554                                 $stmt = self::$dbo->db->stmt_init();
555
556                                 if (!$stmt->prepare($sql)) {
557                                         self::$dbo->error = self::$dbo->db->error;
558                                         self::$dbo->errorno = self::$dbo->db->errno;
559                                         $retval = false;
560                                         break;
561                                 }
562
563                                 $params = '';
564                                 $values = array();
565                                 foreach ($args AS $param => $value) {
566                                         if (is_int($args[$param])) {
567                                                 $params .= 'i';
568                                         } elseif (is_float($args[$param])) {
569                                                 $params .= 'd';
570                                         } elseif (is_string($args[$param])) {
571                                                 $params .= 's';
572                                         } else {
573                                                 $params .= 'b';
574                                         }
575                                         $values[] = &$args[$param];
576                                 }
577
578                                 array_unshift($values, $params);
579
580                                 call_user_func_array(array($stmt, 'bind_param'), $values);
581
582                                 if (!$stmt->execute()) {
583                                         self::$dbo->error = self::$dbo->db->error;
584                                         self::$dbo->errorno = self::$dbo->db->errno;
585                                         $retval = false;
586                                 } elseif (method_exists($stmt, 'get_result')) {
587                                         // Is mysqlnd installed?
588                                         $retval = $stmt->get_result();
589                                 } else {
590                                         $stmt->store_result();
591                                         $retval = $stmt;
592                                 }
593                                 break;
594                         case 'mysql':
595                                 // For the old "mysql" functions we cannot use prepared statements
596                                 foreach ($args AS $param => $value) {
597                                         if (is_int($args[$param]) OR is_float($args[$param])) {
598                                                 $replace = intval($args[$param]);
599                                         } else {
600                                                 $replace = "'".dbesc($args[$param])."'";
601                                         }
602
603                                         $pos = strpos($sql, '?');
604                                         if ($pos !== false) {
605                                                 $sql = substr_replace($sql, $replace, $pos, 1);
606                                         }
607                                 }
608
609                                 $retval = mysql_query($sql, self::$dbo->db);
610                                 if (mysql_errno(self::$dbo->db)) {
611                                         self::$dbo->error = mysql_error(self::$dbo->db);
612                                         self::$dbo->errorno = mysql_errno(self::$dbo->db);
613                                 }
614                                 break;
615                 }
616
617                 $a->save_timestamp($stamp1, 'database');
618
619                 if (x($a->config,'system') && x($a->config['system'], 'db_log')) {
620
621                         $stamp2 = microtime(true);
622                         $duration = (float)($stamp2 - $stamp1);
623
624                         if (($duration > $a->config["system"]["db_loglimit"])) {
625                                 $duration = round($duration, 3);
626                                 $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
627                                 @file_put_contents($a->config["system"]["db_log"], datetime_convert()."\t".$duration."\t".
628                                                 basename($backtrace[1]["file"])."\t".
629                                                 $backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t".
630                                                 substr($sql, 0, 2000)."\n", FILE_APPEND);
631                         }
632                 }
633                 return $retval;
634         }
635
636         /**
637          * @brief Executes a prepared statement
638          *
639          * @param string $sql SQL statement
640          * @return boolean Was the query successfull?
641          */
642         static public function e($sql) {
643                 $a = get_app();
644
645                 $stamp = microtime(true);
646
647                 $args = func_get_args();
648
649                 $stmt = call_user_func_array('self::p', $args);
650
651                 if (is_bool($stmt)) {
652                         $retval = $stmt;
653                 } elseif (is_object($stmt)) {
654                         $retval = true;
655                 } else {
656                         $retval = false;
657                 }
658
659                 self::close($stmt);
660
661                 $a->save_timestamp($stamp, "database_write");
662
663                 return $retval;
664         }
665
666         /**
667          * @brief Check if data exists
668          *
669          * @param string $sql SQL statement
670          * @return boolean Are there rows for that query?
671          */
672         static public function exists($sql) {
673                 $args = func_get_args();
674
675                 $stmt = call_user_func_array('self::p', $args);
676
677                 if (is_bool($stmt)) {
678                         $retval = $stmt;
679                 } else {
680                         $retval = (self::rows($stmt) > 0);
681                 }
682
683                 self::close($stmt);
684
685                 return $retval;
686         }
687
688         /**
689          * @brief Returnr the number of rows of a statement
690          *
691          * @param object Statement object
692          * @return int Number of rows
693          */
694         static public function rows($stmt) {
695                 switch (self::$dbo->driver) {
696                         case 'pdo':
697                                 return $stmt->rowCount();
698                         case 'mysqli':
699                                 return $stmt->num_rows;
700                         case 'mysql':
701                                 return mysql_num_rows($stmt);
702                 }
703                 return 0;
704         }
705
706         /**
707          * @brief Fetch a single row
708          *
709          * @param object $stmt statement object
710          * @return array current row
711          */
712         static public function fetch($stmt) {
713                 if (!is_object($stmt)) {
714                         return false;
715                 }
716
717                 switch (self::$dbo->driver) {
718                         case 'pdo':
719                                 return $stmt->fetch(PDO::FETCH_ASSOC);
720                         case 'mysqli':
721                                 // When mysqlnd is installed, we can use a shortcut
722                                 if (method_exists($stmt, 'fetch_array')) {
723                                         return $stmt->fetch_array(MYSQLI_ASSOC);
724                                 }
725
726                                 // This code works, but is slow
727
728                                 // Bind the result to a result array
729                                 $cols = array();
730
731                                 $cols_num = array();
732                                 for ($x = 0; $x < $stmt->field_count; $x++) {
733                                         $cols[] = &$cols_num[$x];
734                                 }
735
736                                 call_user_func_array(array($stmt, 'bind_result'), $cols);
737
738                                 $success = $stmt->fetch();
739
740                                 if (!$success) {
741                                         return false;
742                                 }
743
744                                 // The slow part:
745                                 // We need to get the field names for the array keys
746                                 // It seems that there is no better way to do this.
747                                 $result = $stmt->result_metadata();
748                                 $fields = $result->fetch_fields();
749
750                                 $columns = array();
751                                 foreach ($cols_num AS $param => $col) {
752                                         $columns[$fields[$param]->name] = $col;
753                                 }
754                                 return $columns;
755                         case 'mysql':
756                                 return mysql_fetch_array(self::$dbo->result, MYSQL_ASSOC);
757                 }
758         }
759
760         /**
761          * @brief Closes the current statement
762          *
763          * @param object $stmt statement object
764          * @return boolean was the close successfull?
765          */
766         static public function close($stmt) {
767                 if (!is_object($stmt)) {
768                         return false;
769                 }
770
771                 switch (self::$dbo->driver) {
772                         case 'pdo':
773                                 return $stmt->closeCursor();
774                         case 'mysqli':
775                                 return $stmt->close();
776                         case 'mysql':
777                                 return mysql_free_result($stmt);
778                 }
779         }
780 }
781
782 function printable($s) {
783         $s = preg_replace("~([\x01-\x08\x0E-\x0F\x10-\x1F\x7F-\xFF])~",".", $s);
784         $s = str_replace("\x00",'.',$s);
785         if (x($_SERVER,'SERVER_NAME')) {
786                 $s = escape_tags($s);
787         }
788         return $s;
789 }
790
791 // Procedural functions
792 function dbg($state) {
793         global $db;
794
795         if ($db) {
796                 $db->dbg($state);
797         }
798 }
799
800 function dbesc($str) {
801         global $db;
802
803         if ($db && $db->connected) {
804                 return($db->escape($str));
805         } else {
806                 return(str_replace("'","\\'",$str));
807         }
808 }
809
810 // Function: q($sql,$args);
811 // Description: execute SQL query with printf style args.
812 // Example: $r = q("SELECT * FROM `%s` WHERE `uid` = %d",
813 //                   'user', 1);
814 function q($sql) {
815         global $db;
816         $args = func_get_args();
817         unset($args[0]);
818
819         if ($db && $db->connected) {
820                 $sql = $db->any_value_fallback($sql);
821                 $stmt = @vsprintf($sql,$args); // Disabled warnings
822                 //logger("dba: q: $stmt", LOGGER_ALL);
823                 if ($stmt === false)
824                         logger('dba: vsprintf error: ' . print_r(debug_backtrace(),true), LOGGER_DEBUG);
825
826                 $db->log_index($stmt);
827
828                 return $db->q($stmt);
829         }
830
831         /**
832          *
833          * This will happen occasionally trying to store the
834          * session data after abnormal program termination
835          *
836          */
837         logger('dba: no database: ' . print_r($args,true));
838         return false;
839 }
840
841 /**
842  * @brief Performs a query with "dirty reads"
843  *
844  * By doing dirty reads (reading uncommitted data) no locks are performed
845  * This function can be used to fetch data that doesn't need to be reliable.
846  *
847  * @param $args Query parameters (1 to N parameters of different types)
848  * @return array Query array
849  */
850 function qu($sql) {
851         global $db;
852
853         $args = func_get_args();
854         unset($args[0]);
855
856         if ($db && $db->connected) {
857                 $sql = $db->any_value_fallback($sql);
858                 $stmt = @vsprintf($sql,$args); // Disabled warnings
859                 if ($stmt === false)
860                         logger('dba: vsprintf error: ' . print_r(debug_backtrace(),true), LOGGER_DEBUG);
861
862                 $db->log_index($stmt);
863
864                 $db->q("SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
865                 $retval = $db->q($stmt);
866                 $db->q("SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;");
867                 return $retval;
868         }
869
870         /**
871          *
872          * This will happen occasionally trying to store the
873          * session data after abnormal program termination
874          *
875          */
876         logger('dba: no database: ' . print_r($args,true));
877         return false;
878 }
879
880 /**
881  *
882  * Raw db query, no arguments
883  *
884  */
885 function dbq($sql) {
886         global $db;
887
888         if ($db && $db->connected) {
889                 $ret = $db->q($sql);
890         } else {
891                 $ret = false;
892         }
893         return $ret;
894 }
895
896 // Caller is responsible for ensuring that any integer arguments to
897 // dbesc_array are actually integers and not malformed strings containing
898 // SQL injection vectors. All integer array elements should be specifically
899 // cast to int to avoid trouble.
900 function dbesc_array_cb(&$item, $key) {
901         if (is_string($item))
902                 $item = dbesc($item);
903 }
904
905 function dbesc_array(&$arr) {
906         if (is_array($arr) && count($arr)) {
907                 array_walk($arr,'dbesc_array_cb');
908         }
909 }
910
911 function dba_timer() {
912         return microtime(true);
913 }