Hubzilla core code
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.
 
 
 
 
 
 

428 lines
10 KiB

  1. <?php
  2. class DBA {
  3. /**
  4. * @file dba_driver.php
  5. * @brief some database related functions and abstract driver class.
  6. *
  7. * This file contains the abstract database driver class dba_driver and some
  8. * functions for working with databases.
  9. */
  10. static public $dba = null;
  11. static public $dbtype = null;
  12. /**
  13. * @brief Returns the database driver object.
  14. *
  15. * If available it will use PHP's mysqli otherwise mysql driver.
  16. *
  17. * @param string $server DB server name
  18. * @param string $port DB port
  19. * @param string $user DB username
  20. * @param string $pass DB password
  21. * @param string $db database name
  22. * @param string $dbtype 0 for mysql, 1 for postgres
  23. * @param bool $install Defaults to false
  24. * @return null|dba_driver A database driver object (dba_mysql|dba_mysqli) or null if no driver found.
  25. */
  26. static public function dba_factory($server, $port,$user,$pass,$db,$dbtype,$install = false) {
  27. self::$dba = null;
  28. self::$dbtype = intval($dbtype);
  29. $set_port = $port;
  30. if(self::$dbtype == DBTYPE_POSTGRES) {
  31. require_once('include/dba/dba_postgres.php');
  32. if(is_null($port)) $set_port = 5432;
  33. self::$dba = new dba_postgres($server, $set_port, $user, $pass, $db, $install);
  34. }
  35. else {
  36. // Highly experimental at the present time.
  37. // require_once('include/dba/dba_pdo.php');
  38. // self::$dba = new dba_pdo($server, $set_port,$user,$pass,$db,$install);
  39. // }
  40. if(class_exists('mysqli')) {
  41. if (is_null($port)) $set_port = ini_get("mysqli.default_port");
  42. require_once('include/dba/dba_mysqli.php');
  43. self::$dba = new dba_mysqli($server, $set_port,$user,$pass,$db,$install);
  44. }
  45. }
  46. // Until we have a proper PDO driver, store the DB connection parameters for
  47. // plugins/addons which use PDO natively (such as cdav). This is wasteful as
  48. // it opens a separate connection to the DB, but saves a lot of effort re-writing
  49. // third-party interfaces that are working and well tested.
  50. if(is_object(self::$dba) && self::$dba->connected) {
  51. $dns = ((self::$dbtype == DBTYPE_POSTGRES) ? 'postgres' : 'mysql')
  52. . ':host=' . $server . (is_null($port) ? '' : ';port=' . $port)
  53. . ';dbname=' . $db;
  54. self::$dba->pdo_set(array($dns,$user,$pass));
  55. }
  56. define('NULL_DATE', self::$dba->get_null_date());
  57. define('ACTIVE_DBTYPE', self::$dbtype);
  58. return self::$dba;
  59. }
  60. }
  61. /**
  62. * @brief abstract database driver class.
  63. *
  64. * This class gets extended by the real database driver classes, e.g. dba_mysql,
  65. * dba_mysqli.
  66. */
  67. abstract class dba_driver {
  68. // legacy behavior
  69. const INSTALL_SCRIPT='install/schema_mysql.sql';
  70. const NULL_DATE = '0000-00-00 00:00:00';
  71. const UTC_NOW = 'UTC_TIMESTAMP()';
  72. protected $db;
  73. protected $pdo = array();
  74. public $debug = 0;
  75. public $connected = false;
  76. public $error = false;
  77. /**
  78. * @brief Connect to the database.
  79. *
  80. * This abstract function needs to be implemented in the real driver.
  81. *
  82. * @param string $server DB server name
  83. * @param string $port DB port
  84. * @param string $user DB username
  85. * @param string $pass DB password
  86. * @param string $db database name
  87. * @return bool
  88. */
  89. abstract function connect($server, $port, $user, $pass, $db);
  90. /**
  91. * @brief Perform a DB query with the SQL statement $sql.
  92. *
  93. * This abstract function needs to be implemented in the real driver.
  94. *
  95. * @param string $sql The SQL query to execute
  96. */
  97. abstract function q($sql);
  98. /**
  99. * @brief Escape a string before being passed to a DB query.
  100. *
  101. * This abstract function needs to be implemented in the real driver.
  102. *
  103. * @param string $str The string to escape.
  104. */
  105. abstract function escape($str);
  106. /**
  107. * @brief Close the database connection.
  108. *
  109. * This abstract function needs to be implemented in the real driver.
  110. */
  111. abstract function close();
  112. /**
  113. * @brief Return text name for db driver
  114. *
  115. * This abstract function needs to be implemented in the real driver.
  116. */
  117. abstract function getdriver();
  118. function __construct($server, $port, $user,$pass,$db,$install = false) {
  119. if(($install) && (! $this->install($server, $port, $user, $pass, $db))) {
  120. return;
  121. }
  122. $this->connect($server, $port, $user, $pass, $db);
  123. }
  124. function get_null_date() {
  125. return static::NULL_DATE;
  126. }
  127. function get_install_script() {
  128. return static::INSTALL_SCRIPT;
  129. }
  130. function utcnow() {
  131. return static::UTC_NOW;
  132. }
  133. function install($server,$user,$pass,$db) {
  134. if (!(strlen($server) && strlen($user))){
  135. $this->connected = false;
  136. $this->db = null;
  137. return false;
  138. }
  139. if(strlen($server) && ($server !== 'localhost') && ($server !== '127.0.0.1')) {
  140. if((! dns_get_record($server, DNS_A + DNS_CNAME + DNS_PTR)) && (! filter_var($server, FILTER_VALIDATE_IP))) {
  141. $this->error = sprintf( t('Cannot locate DNS info for database server \'%s\''), $server);
  142. $this->connected = false;
  143. $this->db = null;
  144. return false;
  145. }
  146. }
  147. return true;
  148. }
  149. /**
  150. * @brief Sets the database driver's debugging state.
  151. *
  152. * @param int $dbg 0 to disable debugging
  153. */
  154. function dbg($dbg) {
  155. $this->debug = $dbg;
  156. }
  157. function __destruct() {
  158. if($this->db && $this->connected) {
  159. $this->close();
  160. }
  161. }
  162. function quote_interval($txt) {
  163. return $txt;
  164. }
  165. function optimize_table($table) {
  166. q('OPTIMIZE TABLE '.$table);
  167. }
  168. function concat($fld, $sep) {
  169. return 'GROUP_CONCAT(DISTINCT '.$fld.' SEPARATOR \''.$sep.'\')';
  170. }
  171. function escapebin($str) {
  172. return $this->escape($str);
  173. }
  174. function unescapebin($str) {
  175. return $str;
  176. }
  177. function pdo_set($x) {
  178. $this->pdo = $x;
  179. }
  180. function pdo_get() {
  181. return $this->pdo;
  182. }
  183. } // end abstract dba_driver class
  184. // Procedural functions
  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. /**
  193. * @brief set database driver debugging state.
  194. *
  195. * @param int $state 0 to disable debugging
  196. */
  197. function dbg($state) {
  198. global $db;
  199. if(\DBA::$dba)
  200. \DBA::$dba->dbg($state);
  201. }
  202. /**
  203. * @brief Escape strings being passed to DB queries.
  204. *
  205. * Always escape strings being used in DB queries. This function returns the
  206. * escaped string. Integer DB parameters should all be proven integers by
  207. * wrapping with intval().
  208. *
  209. * @param string $str A string to pass to a DB query
  210. * @return Return an escaped string of the value to pass to a DB query.
  211. */
  212. function dbesc($str) {
  213. if(\DBA::$dba && \DBA::$dba->connected)
  214. return(\DBA::$dba->escape($str));
  215. else
  216. return(str_replace("'", "\\'", $str));
  217. }
  218. function dbescbin($str) {
  219. return \DBA::$dba->escapebin($str);
  220. }
  221. function dbunescbin($str) {
  222. return \DBA::$dba->unescapebin($str);
  223. }
  224. function dbescdate($date) {
  225. if(ACTIVE_DBTYPE == DBTYPE_POSTGRES && $date == '0000-00-00 00:00:00') {
  226. $date = NULL_DATE;
  227. } else if(ACTIVE_DBTYPE != DBTYPE_POSTGRES && $date == '0001-01-01 00:00:00') {
  228. $date = NULL_DATE;
  229. }
  230. return $date;
  231. }
  232. function db_quoteinterval($txt) {
  233. return \DBA::$dba->quote_interval($txt);
  234. }
  235. function dbesc_identifier($str) {
  236. return \DBA::$dba->escape_identifier($str);
  237. }
  238. function db_utcnow() {
  239. return \DBA::$dba->utcnow();
  240. }
  241. function db_optimizetable($table) {
  242. \DBA::$dba->optimize_table($table);
  243. }
  244. function db_concat($fld, $sep) {
  245. return \DBA::$dba->concat($fld, $sep);
  246. }
  247. /**
  248. * @brief Execute a SQL query with printf style args.
  249. *
  250. * printf style arguments %s and %d are replaced with variable arguments, which
  251. * should each be appropriately dbesc() or intval().
  252. * SELECT queries return an array of results or false if SQL or DB error. Other
  253. * queries return true if the command was successful or false if it wasn't.
  254. *
  255. * Example:
  256. * $r = q("SELECT * FROM `%s` WHERE `uid` = %d",
  257. * 'user', 1);
  258. *
  259. * @param string $sql The SQL query to execute
  260. * @return bool|array
  261. */
  262. function q($sql) {
  263. $args = func_get_args();
  264. unset($args[0]);
  265. if(\DBA::$dba && \DBA::$dba->connected) {
  266. $stmt = vsprintf($sql, $args);
  267. if($stmt === false) {
  268. if(version_compare(PHP_VERSION, '5.4.0') >= 0)
  269. db_logger('dba: vsprintf error: ' .
  270. print_r(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1), true),LOGGER_NORMAL,LOG_CRIT);
  271. else
  272. db_logger('dba: vsprintf error: ' . print_r(debug_backtrace(), true),LOGGER_NORMAL,LOG_CRIT);
  273. }
  274. return \DBA::$dba->q($stmt);
  275. }
  276. /*
  277. * This will happen occasionally trying to store the
  278. * session data after abnormal program termination
  279. */
  280. db_logger('dba: no database: ' . print_r($args,true),LOGGER_NORMAL,LOG_CRIT);
  281. return false;
  282. }
  283. /**
  284. * @brief Raw DB query, no arguments.
  285. *
  286. * This function executes a raw DB query without any arguments.
  287. *
  288. * @param string $sql The SQL query to execute
  289. */
  290. function dbq($sql) {
  291. if(\DBA::$dba && \DBA::$dba->connected)
  292. $ret = \DBA::$dba->q($sql);
  293. else
  294. $ret = false;
  295. return $ret;
  296. }
  297. // Caller is responsible for ensuring that any integer arguments to
  298. // dbesc_array are actually integers and not malformed strings containing
  299. // SQL injection vectors. All integer array elements should be specifically
  300. // cast to int to avoid trouble.
  301. function dbesc_array_cb(&$item, $key) {
  302. if(is_string($item)) {
  303. if($item == '0000-00-00 00:00:00' && ACTIVE_DBTYPE == DBTYPE_POSTGRES)
  304. $item = '0001-01-01 00:00:00';
  305. else if($item == '0001-01-01 00:00:00' && ACTIVE_DBTYPE == DBTYPE_MYSQL)
  306. $item = '0000-00-00 00:00:00';
  307. $item = dbesc($item);
  308. }
  309. }
  310. function dbesc_array(&$arr) {
  311. if(is_array($arr) && count($arr)) {
  312. array_walk($arr,'dbesc_array_cb');
  313. }
  314. }
  315. function db_getfunc($f) {
  316. $lookup = array(
  317. 'rand'=>array(
  318. DBTYPE_MYSQL=>'RAND()',
  319. DBTYPE_POSTGRES=>'RANDOM()'
  320. ),
  321. 'utc_timestamp'=>array(
  322. DBTYPE_MYSQL=>'UTC_TIMESTAMP()',
  323. DBTYPE_POSTGRES=>"now() at time zone 'UTC'"
  324. ),
  325. 'regexp'=>array(
  326. DBTYPE_MYSQL=>'REGEXP',
  327. DBTYPE_POSTGRES=>'~'
  328. ),
  329. '^'=>array(
  330. DBTYPE_MYSQL=>'^',
  331. DBTYPE_POSTGRES=>'#'
  332. )
  333. );
  334. $f = strtolower($f);
  335. if(isset($lookup[$f]) && isset($lookup[$f][ACTIVE_DBTYPE]))
  336. return $lookup[$f][ACTIVE_DBTYPE];
  337. db_logger('Unable to abstract DB function "'. $f . '" for dbtype ' . ACTIVE_DBTYPE, LOGGER_DEBUG, LOG_ERR);
  338. return $f;
  339. }
  340. // The logger function may make DB calls internally to query the system logging parameters.
  341. // This can cause a recursion if database debugging is enabled.
  342. // So this function preserves the current database debugging state and then turns it off
  343. // temporarily while doing the logger() call
  344. function db_logger($s,$level = LOGGER_NORMAL,$syslog = LOG_INFO) {
  345. $saved = \DBA::$dba->debug;
  346. \DBA::$dba->debug = false;
  347. logger($s,$level,$syslog);
  348. \DBA::$dba->debug = $saved;
  349. }