/bp-forums/bbpress/bb-includes/backpress/class.bpdb.php

  1. <?php 
  2. // backPress DB Class 
  3.  
  4. // ORIGINAL CODE FROM: 
  5. // Justin Vincent (justin@visunet.ie) 
  6. // http://php.justinvincent.com 
  7.  
  8. define( 'EZSQL_VERSION', 'BP1.25' ); 
  9. define( 'OBJECT', 'OBJECT', true ); 
  10. define( 'OBJECT_K', 'OBJECT_K', false ); 
  11. define( 'ARRAY_A', 'ARRAY_A', false ); 
  12. define( 'ARRAY_K', 'ARRAY_K', false ); 
  13. define( 'ARRAY_N', 'ARRAY_N', false ); 
  14.  
  15. if ( !defined( 'SAVEQUERIES' ) ) { 
  16. define( 'SAVEQUERIES', false ); 
  17.  
  18. if ( !defined( 'BPDB__ERROR_STRING' ) ) { 
  19. define( 'BPDB__ERROR_STRING', 'DB Error: %s, %s: %s' ); 
  20. if ( !defined( 'BPDB__ERROR_HTML' ) ) { 
  21. define( 'BPDB__ERROR_HTML', '<div class="error"><p><strong>DB Error in %3$s:</strong> %1$s</p><pre>%2$s</pre></div>' ); 
  22. if ( !defined( 'BPDB__CONNECT_ERROR_MESSAGE' ) ) { 
  23. define( 'BPDB__CONNECT_ERROR_MESSAGE', 'DB Error: cannot connect' ); 
  24. if ( !defined( 'BPDB__SELECT_ERROR_MESSAGE' ) ) { 
  25. define( 'BPDB__SELECT_ERROR_MESSAGE', 'DB Error: cannot select' ); 
  26. if ( !defined( 'BPDB__DB_VERSION_ERROR' ) ) { 
  27. define( 'BPDB__DB_VERSION_ERROR', 'DB Requires MySQL version 4.0 or higher' ); 
  28. if ( !defined( 'BPDB__PHP_EXTENSION_MISSING' ) ) { 
  29. define( 'BPDB__PHP_EXTENSION_MISSING', 'DB Requires The MySQL PHP extension' ); 
  30.  
  31. class BPDB 
  32. /** 
  33. * Whether to show SQL/DB errors 
  34. * 
  35. * @since 1.0 
  36. * @access private 
  37. * @var bool 
  38. */ 
  39. var $show_errors = false; 
  40.  
  41. /** 
  42. * Whether to suppress errors during the DB bootstrapping. 
  43. * 
  44. * @access private 
  45. * @since 1.0 
  46. * @var bool 
  47. */ 
  48. var $suppress_errors = false; 
  49.  
  50. /** 
  51. * The last error during query. 
  52. * 
  53. * @since 1.0 
  54. * @var string 
  55. */ 
  56. var $last_error = ''; 
  57.  
  58. /** 
  59. * Amount of queries made 
  60. * 
  61. * @since 1.0 
  62. * @access private 
  63. * @var int 
  64. */ 
  65. var $num_queries = 0; 
  66.  
  67. /** 
  68. * The last query made 
  69. * 
  70. * @since 1.0 
  71. * @access private 
  72. * @var string 
  73. */ 
  74. var $last_query = null; 
  75.  
  76. /** 
  77. * Saved info on the table column 
  78. * 
  79. * @since 1.0 
  80. * @access private 
  81. * @var array 
  82. */ 
  83. var $col_info = array(); 
  84.  
  85. /** 
  86. * Saved queries that were executed 
  87. * 
  88. * @since 1.0 
  89. * @access private 
  90. * @var array 
  91. */ 
  92. var $queries = array(); 
  93.  
  94. /** 
  95. * Whether to use the query log 
  96. * 
  97. * @since 1.0 
  98. * @access private 
  99. * @var bool 
  100. */ 
  101. var $save_queries = false; 
  102.  
  103. /** 
  104. * Table prefix 
  105. * 
  106. * You can set this to have multiple installations 
  107. * in a single database. The second reason is for possible 
  108. * security precautions. 
  109. * 
  110. * @since 1.0 
  111. * @access private 
  112. * @var string 
  113. */ 
  114. var $prefix = ''; 
  115.  
  116. /** 
  117. * Whether the database queries are ready to start executing. 
  118. * 
  119. * @since 1.0 
  120. * @access private 
  121. * @var bool 
  122. */ 
  123. var $ready = false; 
  124.  
  125. /** 
  126. * The currently connected MySQL connection resource. 
  127. * 
  128. * @since 1.0 
  129. * @access private 
  130. * @var bool|resource 
  131. */ 
  132. var $dbh = false; 
  133.  
  134. /** 
  135. * List of tables 
  136. * 
  137. * @since 1.0 
  138. * @access private 
  139. * @var array 
  140. */ 
  141. var $tables = array(); 
  142.  
  143. /** 
  144. * Whether to use mysql_real_escape_string 
  145. * 
  146. * @since 1.0 
  147. * @access public 
  148. * @var bool 
  149. */ 
  150. var $real_escape = false; 
  151.  
  152. /** 
  153. * PHP4 style constructor 
  154. * 
  155. * @since 1.0 
  156. * 
  157. * @return unknown Returns the result of bpdb::__construct() 
  158. */ 
  159. function BPDB() 
  160. $args = func_get_args(); 
  161. register_shutdown_function( array( &$this, '__destruct' ) ); 
  162. return call_user_func_array( array( &$this, '__construct' ), $args ); 
  163.  
  164. /** 
  165. * PHP5 style constructor 
  166. * 
  167. * Grabs the arguments, calls bpdb::_init() and then connects to the database 
  168. * 
  169. * @since 1.0 
  170. * 
  171. * @return void 
  172. */ 
  173. function __construct() 
  174. $args = func_get_args(); 
  175. $args = call_user_func_array( array( &$this, '_init' ), $args ); 
  176.  
  177. $this->db_connect_host( $args ); 
  178.  
  179. /** 
  180. * Initialises the class variables based on provided arguments 
  181. * 
  182. * @since 1.0 
  183. * 
  184. * @param array $args The provided connection settings 
  185. * @return array The current connection settings after processing by init 
  186. */ 
  187. function _init( $args ) 
  188. if ( !extension_loaded( 'mysql' ) ) { 
  189. $this->show_errors(); 
  190. $this->bail( BPDB__PHP_EXTENSION_MISSING ); 
  191. return; 
  192.  
  193. if ( 4 == func_num_args() ) { 
  194. $args = array( 
  195. 'user' => $args,  
  196. 'password' => func_get_arg( 1 ),  
  197. 'name' => func_get_arg( 2 ),  
  198. 'host' => func_get_arg( 3 ) 
  199. ); 
  200.  
  201. $defaults = array( 
  202. 'user' => false,  
  203. 'password' => false,  
  204. 'name' => false,  
  205. 'host' => 'localhost',  
  206. 'charset' => false,  
  207. 'collate' => false,  
  208. 'errors' => false 
  209. ); 
  210.  
  211. $args = wp_parse_args( $args, $defaults ); 
  212.  
  213. switch ( $args['errors'] ) { 
  214. case 'show' : 
  215. $this->show_errors( true ); 
  216. break; 
  217. case 'suppress' : 
  218. $this->suppress_errors( true ); 
  219. break; 
  220.  
  221. return $args; 
  222.  
  223. /** 
  224. * PHP5 style destructor, registered as shutdown function in PHP4 
  225. * 
  226. * @since 1.0 
  227. * 
  228. * @return bool Always returns true 
  229. */ 
  230. function __destruct() 
  231. return true; 
  232.  
  233. /** 
  234. * Figure out which database server should handle the query, and connect to it. 
  235. * 
  236. * @since 1.0 
  237. * 
  238. * @param string query 
  239. * @return resource mysql database connection 
  240. */ 
  241. function &db_connect( $query = '' ) 
  242. $false = false; 
  243. if ( empty( $query ) ) { 
  244. return $false; 
  245. return $this->dbh; 
  246.  
  247. /** 
  248. * Connects to the database server and selects a database 
  249. * 
  250. * @since 1.0 
  251. * 
  252. * @param array args 
  253. * name => string DB name (required) 
  254. * user => string DB user (optional: false) 
  255. * password => string DB user password (optional: false) 
  256. * host => string DB hostname (optional: 'localhost') 
  257. * charset => string DB default charset. Used in a SET NAMES query. (optional) 
  258. * collate => string DB default collation. If charset supplied, optionally added to the SET NAMES query (optional) 
  259. * @return void|bool void if cannot connect, false if cannot select, true if success 
  260. */ 
  261. function db_connect_host( $args ) 
  262. extract( $args, EXTR_SKIP ); 
  263.  
  264. unset( $this->dbh ); // De-reference before re-assigning 
  265. $this->dbh = @mysql_connect( $host, $user, $password, true ); 
  266.  
  267. if ( !$this->dbh ) { 
  268. if ( !$this->suppress_errors ) { 
  269. $this->show_errors(); 
  270. $this->bail( BPDB__CONNECT_ERROR_MESSAGE ); 
  271. return; 
  272.  
  273. $this->ready = true; 
  274.  
  275. if ( $this->has_cap( 'collation' ) ) { 
  276. if ( !empty( $charset ) ) { 
  277. if ( function_exists( 'mysql_set_charset' ) ) { 
  278. mysql_set_charset( $charset, $this->dbh ); 
  279. $this->real_escape = true; 
  280. } else { 
  281. $collation_query = "SET NAMES '{$charset}'"; 
  282. if ( !empty( $collate ) ) { 
  283. $collation_query .= " COLLATE '{$collate}'"; 
  284. $this->query( $collation_query, true ); 
  285.  
  286. return $this->select( $name, $this->dbh ); 
  287.  
  288. /** 
  289. * Sets the table prefix for the WordPress tables. 
  290. * 
  291. * @since 1.0 
  292. * 
  293. * @param string prefix 
  294. * @param false|array tables (optional: false) 
  295. * table identifiers are array keys 
  296. * array values 
  297. * empty: set prefix: array( 'posts' => false, 'users' => false, ... ) 
  298. * string: set to that array value: array( 'posts' => 'my_posts', 'users' => 'my_users' ) 
  299. * OR array values (with numeric keys): array( 'posts', 'users', ... ) 
  300. * 
  301. * @return string the previous prefix (mostly only meaningful if all $table parameter was false) 
  302. */ 
  303. function set_prefix( $prefix, $tables = false ) 
  304. if ( !$prefix ) { 
  305. return false; 
  306. if ( preg_match( '|[^a-z0-9_]|i', $prefix ) ) { 
  307. return new WP_Error( 'invalid_db_prefix', 'Invalid database prefix' ); // No gettext here 
  308.  
  309. $old_prefix = $this->prefix; 
  310.  
  311. if ( $tables && is_array( $tables ) ) { 
  312. $_tables =& $tables; 
  313. } else { 
  314. $_tables =& $this->tables; 
  315. $this->prefix = $prefix; 
  316.  
  317. foreach ( $_tables as $key => $value ) { 
  318. if ( is_numeric( $key ) ) { // array( 'posts', 'users', ... ) 
  319. $this->$value = $prefix . $value; 
  320. } elseif ( !$value ) { 
  321. $this->$key = $prefix . $key; // array( 'posts' => false, 'users' => false, ... ) 
  322. } elseif ( is_string( $value ) ) { // array( 'posts' => 'my_posts', 'users' => 'my_users' ) 
  323. $this->$key = $value; 
  324.  
  325. return $old_prefix; 
  326.  
  327. /** 
  328. * Selects a database using the current database connection. 
  329. * 
  330. * The database name will be changed based on the current database 
  331. * connection. On failure, the execution will bail and display an DB error. 
  332. * 
  333. * @since 1.0 
  334. * 
  335. * @param string $db MySQL database name 
  336. * @return bool True on success, false on failure. 
  337. */ 
  338. function select( $db, &$dbh ) 
  339. if ( !@mysql_select_db( $db, $dbh ) ) { 
  340. $this->ready = false; 
  341. $this->show_errors(); 
  342. $this->bail( BPDB__SELECT_ERROR_MESSAGE ); 
  343. return false; 
  344. return true; 
  345.  
  346. function _weak_escape( $string ) 
  347. return addslashes( $string ); 
  348.  
  349. function _real_escape( $string ) 
  350. if ( $this->dbh && $this->real_escape ) { 
  351. return mysql_real_escape_string( $string, $this->dbh ); 
  352. } else { 
  353. return addslashes( $string ); 
  354.  
  355. function _escape( $data ) 
  356. if ( is_array( $data ) ) { 
  357. foreach ( (array) $data as $k => $v ) { 
  358. if ( is_array( $v ) ) { 
  359. $data[$k] = $this->_escape( $v ); 
  360. } else { 
  361. $data[$k] = $this->_real_escape( $v ); 
  362. } else { 
  363. $data = $this->_real_escape( $data ); 
  364.  
  365. return $data; 
  366.  
  367. /** 
  368. * Escapes content for insertion into the database using addslashes(), for security 
  369. * 
  370. * @since 1.0 
  371. * 
  372. * @param string|array $data 
  373. * @return string query safe string 
  374. */ 
  375. function escape( $data ) 
  376. if ( is_array( $data ) ) { 
  377. foreach ( (array) $data as $k => $v ) { 
  378. if ( is_array( $v ) ) { 
  379. $data[$k] = $this->escape( $v ); 
  380. } else { 
  381. $data[$k] = $this->_weak_escape( $v ); 
  382. } else { 
  383. $data = $this->_weak_escape( $data ); 
  384.  
  385. return $data; 
  386.  
  387. /** 
  388. * Escapes content by reference for insertion into the database, for security 
  389. * 
  390. * @since 1.0 
  391. * 
  392. * @param string $s 
  393. */ 
  394. function escape_by_ref( &$string ) 
  395. $string = $this->_real_escape( $string ); 
  396.  
  397. /** 
  398. * Escapes array recursively for insertion into the database, for security 
  399. * @param array $array 
  400. */ 
  401. function escape_deep( $array ) 
  402. return $this->_escape( $array ); 
  403.  
  404. /** 
  405. * Prepares a SQL query for safe execution. Uses sprintf()-like syntax. 
  406. * 
  407. * This function only supports a small subset of the sprintf syntax; it only supports %d (decimal number), %s (string). 
  408. * Does not support sign, padding, alignment, width or precision specifiers. 
  409. * Does not support argument numbering/swapping. 
  410. * 
  411. * May be called like {@link http://php.net/sprintf sprintf()} or like {@link http://php.net/vsprintf vsprintf()}. 
  412. * 
  413. * Both %d and %s should be left unquoted in the query string. 
  414. * 
  415. * <code> 
  416. * wpdb::prepare( "SELECT * FROM `table` WHERE `column` = %s AND `field` = %d", "foo", 1337 ) 
  417. * </code> 
  418. * 
  419. * @link http://php.net/sprintf Description of syntax. 
  420. * @since 1.0 
  421. * 
  422. * @param string $query Query statement with sprintf()-like placeholders 
  423. * @param array|mixed $args The array of variables to substitute into the query's placeholders if being called like {@link http://php.net/vsprintf vsprintf()}, or the first variable to substitute into the query's placeholders if being called like {@link http://php.net/sprintf sprintf()}. 
  424. * @param mixed $args, ... further variables to substitute into the query's placeholders if being called like {@link http://php.net/sprintf sprintf()}. 
  425. * @return null|string Sanitized query string 
  426. */ 
  427. function prepare( $query = null ) // ( $query, *$args ) 
  428. if ( is_null( $query ) ) { 
  429. return; 
  430. $args = func_get_args(); 
  431. array_shift( $args ); 
  432. // If args were passed as an array (as in vsprintf), move them up 
  433. if ( isset( $args[0] ) && is_array( $args[0] ) ) { 
  434. $args = $args[0]; 
  435. $query = str_replace( "'%s'", '%s', $query ); // in case someone mistakenly already singlequoted it 
  436. $query = str_replace( '"%s"', '%s', $query ); // doublequote unquoting 
  437. $query = str_replace( '%s', "'%s'", $query ); // quote the strings 
  438. array_walk( $args, array( &$this, 'escape_by_ref' ) ); 
  439. return @vsprintf( $query, $args ); 
  440.  
  441. /** 
  442. * Get SQL/DB error 
  443. * 
  444. * @since 1.0 
  445. * 
  446. * @param string $str Error string 
  447. */ 
  448. function get_error( $str = '' ) 
  449. if ( empty( $str ) ) { 
  450. if ( $this->last_error ) { 
  451. $str = $this->last_error; 
  452. } else { 
  453. return false; 
  454.  
  455. $caller = $this->get_caller(); 
  456. $error_str = sprintf( BPDB__ERROR_STRING, $str, $this->last_query, $caller ); 
  457.  
  458. if ( class_exists( 'WP_Error' ) ) { 
  459. return new WP_Error( 'db_query', $error_str, array( 'query' => $this->last_query, 'error' => $str, 'caller' => $caller ) ); 
  460. } else { 
  461. return array( 'query' => $this->last_query, 'error' => $str, 'caller' => $caller, 'error_str' => $error_str ); 
  462.  
  463. /** 
  464. * Print SQL/DB error. 
  465. * 
  466. * @since 1.0 
  467. * 
  468. * @param string $str The error to display 
  469. * @return bool False if the showing of errors is disabled. 
  470. */ 
  471. function print_error( $str = '' ) 
  472. if ( $this->suppress_errors ) { 
  473. return false; 
  474.  
  475. $error = $this->get_error( $str ); 
  476. if ( is_object( $error ) && is_a( $error, 'WP_Error' ) ) { 
  477. $err = $error->get_error_data(); 
  478. $err['error_str'] = $error->get_error_message(); 
  479. } else { 
  480. $err =& $error; 
  481.  
  482. $log_file = ini_get( 'error_log' ); 
  483. if ( !empty( $log_file ) && ( 'syslog' != $log_file ) && is_writable( $log_file ) && function_exists( 'error_log' ) ) { 
  484. error_log($err['error_str'], 0); 
  485.  
  486. // Is error output turned on or not 
  487. if ( !$this->show_errors ) { 
  488. return false; 
  489.  
  490. $str = htmlspecialchars( $err['error'], ENT_QUOTES ); 
  491. $query = htmlspecialchars( $err['query'], ENT_QUOTES ); 
  492. $caller = htmlspecialchars( $err['caller'], ENT_QUOTES ); 
  493.  
  494. // If there is an error then take note of it 
  495.  
  496. printf( BPDB__ERROR_HTML, $str, $query, $caller ); 
  497.  
  498. /** 
  499. * Enables showing of database errors. 
  500. * 
  501. * This function should be used only to enable showing of errors. 
  502. * bpdb::hide_errors() should be used instead for hiding of errors. However,  
  503. * this function can be used to enable and disable showing of database 
  504. * errors. 
  505. * 
  506. * @since 1.0 
  507. * 
  508. * @param bool $show Whether to show or hide errors 
  509. * @return bool Old value for showing errors. 
  510. */ 
  511. function show_errors( $show = true ) 
  512. $errors = $this->show_errors; 
  513. $this->show_errors = $show; 
  514. return $errors; 
  515.  
  516. /** 
  517. * Disables showing of database errors. 
  518. * 
  519. * @since 1.0 
  520. * 
  521. * @return bool Whether showing of errors was active or not 
  522. */ 
  523. function hide_errors() 
  524. return $this->show_errors( false ); 
  525.  
  526. /** 
  527. * Whether to suppress database errors. 
  528. * 
  529. * @since 1.0 
  530. * 
  531. * @param bool $suppress 
  532. * @return bool previous setting 
  533. */ 
  534. function suppress_errors( $suppress = true ) 
  535. $errors = $this->suppress_errors; 
  536. $this->suppress_errors = $suppress; 
  537. return $errors; 
  538.  
  539. /** 
  540. * Kill cached query results. 
  541. * 
  542. * @since 1.0 
  543. */ 
  544. function flush() 
  545. $this->last_result = array(); 
  546. $this->col_info = array(); 
  547. $this->last_query = null; 
  548. $this->last_error = ''; 
  549. $this->num_rows = 0; 
  550.  
  551. /** 
  552. * Perform a MySQL database query, using current database connection. 
  553. * 
  554. * More information can be found on the codex page. 
  555. * 
  556. * @since 1.0 
  557. * 
  558. * @param string $query 
  559. * @return int|false Number of rows affected/selected or false on error 
  560. */ 
  561. function query( $query, $use_current = false ) 
  562. if ( !$this->ready ) { 
  563. return false; 
  564.  
  565. // filter the query, if filters are available 
  566. // NOTE: some queries are made before the plugins have been loaded, and thus cannot be filtered with this method 
  567. if ( function_exists( 'apply_filters' ) ) { 
  568. $query = apply_filters( 'query', $query ); 
  569.  
  570. // initialise return 
  571. $return_val = 0; 
  572. $this->flush(); 
  573.  
  574. // Log how the function was called 
  575. $this->func_call = "\$db->query(\"$query\")"; 
  576.  
  577. // Keep track of the last query for debug.. 
  578. $this->last_query = $query; 
  579.  
  580. // Perform the query via std mysql_query function.. 
  581. if ( SAVEQUERIES ) { 
  582. $this->timer_start(); 
  583.  
  584. if ( $use_current ) { 
  585. $dbh =& $this->dbh; 
  586. } else { 
  587. $dbh = $this->db_connect( $query ); 
  588.  
  589. $this->result = @mysql_query( $query, $dbh ); 
  590. ++$this->num_queries; 
  591.  
  592. if ( SAVEQUERIES ) { 
  593. $this->queries[] = array( $query, $this->timer_stop(), $this->get_caller() ); 
  594.  
  595. // If there is an error then take note of it.. 
  596. if ( $this->last_error = mysql_error( $dbh ) ) { 
  597. return $this->print_error( $this->last_error ); 
  598.  
  599. if ( preg_match( "/^\\s*(insert|delete|update|replace|alter) /i", $query ) ) { 
  600. $this->rows_affected = mysql_affected_rows( $dbh ); 
  601. // Take note of the insert_id 
  602. if ( preg_match( "/^\\s*(insert|replace) /i", $query ) ) { 
  603. $this->insert_id = mysql_insert_id( $dbh ); 
  604. // Return number of rows affected 
  605. $return_val = $this->rows_affected; 
  606. } else { 
  607. $i = 0; 
  608. while ( $i < @mysql_num_fields( $this->result ) ) { 
  609. $this->col_info[$i] = @mysql_fetch_field( $this->result ); 
  610. $i++; 
  611. $num_rows = 0; 
  612. while ( $row = @mysql_fetch_object( $this->result ) ) { 
  613. $this->last_result[$num_rows] = $row; 
  614. $num_rows++; 
  615.  
  616. @mysql_free_result( $this->result ); 
  617.  
  618. // Log number of rows the query returned 
  619. $this->num_rows = $num_rows; 
  620.  
  621. // Return number of rows selected 
  622. $return_val = $this->num_rows; 
  623.  
  624. return $return_val; 
  625.  
  626. /** 
  627. * Insert a row into a table. 
  628. * 
  629. * <code> 
  630. * wpdb::insert( 'table', array( 'column' => 'foo', 'field' => 1337 ), array( '%s', '%d' ) ) 
  631. * </code> 
  632. * 
  633. * @since 1.0 
  634. * @see bpdb::prepare() 
  635. * 
  636. * @param string $table table name 
  637. * @param array $data Data to insert (in column => value pairs). Both $data columns and $data values should be "raw" (neither should be SQL escaped). 
  638. * @param array|string $format (optional) An array of formats to be mapped to each of the value in $data. If string, that format will be used for all of the values in $data. A format is one of '%d', '%s' (decimal number, string). If omitted, all values in $data will be treated as strings. 
  639. * @return int|false The number of rows inserted, or false on error. 
  640. */ 
  641. function insert( $table, $data, $format = null ) 
  642. $formats = $format = (array) $format; 
  643. $fields = array_keys( $data ); 
  644. $formatted_fields = array(); 
  645. foreach ( $fields as $field ) { 
  646. if ( !empty( $format ) ) { 
  647. $form = ( $form = array_shift( $formats ) ) ? $form : $format[0]; 
  648. } elseif ( isset( $this->field_types[$field] ) ) { 
  649. $form = $this->field_types[$field]; 
  650. } elseif ( is_null( $data[$field] ) ) { 
  651. $form = 'NULL'; 
  652. unset( $data[$field] ); 
  653. } else { 
  654. $form = '%s'; 
  655. $formatted_fields[] = $form; 
  656. $sql = "INSERT INTO `$table` (`" . implode( '`, `', $fields ) . "`) VALUES (" . implode( ", ", $formatted_fields ) . ")"; 
  657. return $this->query( $this->prepare( $sql, $data ) ); 
  658.  
  659. /** 
  660. * Update a row in the table 
  661. * 
  662. * <code> 
  663. * wpdb::update( 'table', array( 'column' => 'foo', 'field' => 1337 ), array( 'ID' => 1 ), array( '%s', '%d' ), array( '%d' ) ) 
  664. * </code> 
  665. * 
  666. * @since 1.0 
  667. * @see bpdb::prepare() 
  668. * 
  669. * @param string $table table name 
  670. * @param array $data Data to update (in column => value pairs). Both $data columns and $data values should be "raw" (neither should be SQL escaped). 
  671. * @param array $where A named array of WHERE clauses (in column => value pairs). Multiple clauses will be joined with ANDs. Both $where columns and $where values should be "raw". 
  672. * @param array|string $format (optional) An array of formats to be mapped to each of the values in $data. If string, that format will be used for all of the values in $data. A format is one of '%d', '%s' (decimal number, string). If omitted, all values in $data will be treated as strings. 
  673. * @param array|string $format_where (optional) An array of formats to be mapped to each of the values in $where. If string, that format will be used for all of the items in $where. A format is one of '%d', '%s' (decimal number, string). If omitted, all values in $where will be treated as strings. 
  674. * @return int|false The number of rows updated, or false on error. 
  675. */ 
  676. function update( $table, $data, $where, $format = null, $where_format = null ) 
  677. if ( !is_array( $where ) ) { 
  678. return false; 
  679.  
  680. $formats = $format = (array) $format; 
  681. $bits = $wheres = array(); 
  682. foreach ( (array) array_keys( $data ) as $field ) { 
  683. if ( !empty( $format ) ) { 
  684. $form = ( $form = array_shift( $formats ) ) ? $form : $format[0]; 
  685. } elseif ( isset( $this->field_types[$field] ) ) { 
  686. $form = $this->field_types[$field]; 
  687. } elseif ( is_null( $data[$field] ) ) { 
  688. $form = 'NULL'; 
  689. unset( $data[$field] ); 
  690. } else { 
  691. $form = '%s'; 
  692. $bits[] = "`$field` = {$form}"; 
  693.  
  694. $where_formats = $where_format = (array) $where_format; 
  695. foreach ( (array) array_keys( $where ) as $field ) { 
  696. if ( !empty( $where_format ) ) { 
  697. $form = ( $form = array_shift( $where_formats ) ) ? $form : $where_format[0]; 
  698. } elseif ( isset( $this->field_types[$field] ) ) { 
  699. $form = $this->field_types[$field]; 
  700. } elseif ( is_null( $where[$field] ) ) { 
  701. unset( $where[$field] ); 
  702. $wheres[] = "`$field` IS NULL"; 
  703. continue; 
  704. } else { 
  705. $form = '%s'; 
  706. $wheres[] = "`$field` = {$form}"; 
  707.  
  708. $sql = "UPDATE `$table` SET " . implode( ', ', $bits ) . ' WHERE ' . implode( ' AND ', $wheres ); 
  709. return $this->query( $this->prepare( $sql, array_merge( array_values( $data ), array_values( $where ) ) ) ); 
  710.  
  711. /** 
  712. * Retrieve one variable from the database. 
  713. * 
  714. * Executes a SQL query and returns the value from the SQL result. 
  715. * If the SQL result contains more than one column and/or more than one row, this function returns the value in the column and row specified. 
  716. * If $query is null, this function returns the value in the specified column and row from the previous SQL result. 
  717. * 
  718. * @since 1.0 
  719. * 
  720. * @param string|null $query SQL query. If null, use the result from the previous query. 
  721. * @param int $x (optional) Column of value to return. Indexed from 0. 
  722. * @param int $y (optional) Row of value to return. Indexed from 0. 
  723. * @return string Database query result 
  724. */ 
  725. function get_var( $query=null, $x = 0, $y = 0 ) 
  726. $this->func_call = "\$db->get_var(\"$query\", $x, $y)"; 
  727. if ( $query ) { 
  728. $this->query( $query ); 
  729.  
  730. // Extract var out of cached results based x, y vals 
  731. if ( !empty( $this->last_result[$y] ) ) { 
  732. $values = array_values( get_object_vars( $this->last_result[$y] ) ); 
  733.  
  734. // If there is a value return it else return null 
  735. return ( isset($values[$x]) && $values[$x]!=='' ) ? $values[$x] : null; 
  736.  
  737. /** 
  738. * Retrieve one row from the database. 
  739. * 
  740. * Executes a SQL query and returns the row from the SQL result. 
  741. * 
  742. * @since 1.0 
  743. * 
  744. * @param string|null $query SQL query. 
  745. * @param string $output (optional) one of ARRAY_A | ARRAY_N | OBJECT constants. Return an associative array (column => value, ...), a numerically indexed array (0 => value, ...) or an object ( ->column = value ), respectively. 
  746. * @param int $y (optional) Row to return. Indexed from 0. 
  747. * @return mixed Database query result in format specifed by $output 
  748. */ 
  749. function get_row( $query = null, $output = OBJECT, $y = 0 ) 
  750. $this->func_call = "\$db->get_row(\"$query\", $output, $y)"; 
  751. if ( $query ) { 
  752. $this->query( $query ); 
  753. } else { 
  754. return null; 
  755.  
  756. if ( !isset( $this->last_result[$y] ) ) { 
  757. return null; 
  758.  
  759. if ( $output == OBJECT ) { 
  760. return $this->last_result[$y] ? $this->last_result[$y] : null; 
  761. } elseif ( $output == ARRAY_A ) { 
  762. return $this->last_result[$y] ? get_object_vars( $this->last_result[$y] ) : null; 
  763. } elseif ( $output == ARRAY_N ) { 
  764. return $this->last_result[$y] ? array_values( get_object_vars( $this->last_result[$y] ) ) : null; 
  765. } else { 
  766. $this->print_error( " \$db->get_row(string query, output type, int offset) -- Output type must be one of: OBJECT, ARRAY_A, ARRAY_N" ); 
  767.  
  768. /** 
  769. * Retrieve one column from the database. 
  770. * 
  771. * Executes a SQL query and returns the column from the SQL result. 
  772. * If the SQL result contains more than one column, this function returns the column specified. 
  773. * If $query is null, this function returns the specified column from the previous SQL result. 
  774. * 
  775. * @since 1.0 
  776. * 
  777. * @param string|null $query SQL query. If null, use the result from the previous query. 
  778. * @param int $x Column to return. Indexed from 0. 
  779. * @return array Database query result. Array indexed from 0 by SQL result row number. 
  780. */ 
  781. function get_col( $query = null , $x = 0 ) 
  782. if ( $query ) { 
  783. $this->query( $query ); 
  784.  
  785. $new_array = array(); 
  786. // Extract the column values 
  787. for ( $i=0; $i < count( $this->last_result ); $i++ ) { 
  788. $new_array[$i] = $this->get_var( null, $x, $i ); 
  789. return $new_array; 
  790.  
  791. /** 
  792. * Retrieve an entire SQL result set from the database (i.e., many rows) 
  793. * 
  794. * Executes a SQL query and returns the entire SQL result. 
  795. * 
  796. * @since 1.0 
  797. * 
  798. * @param string $query SQL query. 
  799. * @param string $output (optional) ane of ARRAY_A | ARRAY_N | OBJECT | OBJECT_K | ARRAY_K constants. With one of the first three, return an array of rows indexed from 0 by SQL result row number. Each row is an associative array (column => value, ...), a numerically indexed array (0 => value, ...), or an object. ( ->column = value ), respectively. With OBJECT_K and ARRAY_K, return an associative array of row objects keyed by the value of each row's first column's value. Duplicate keys are discarded. 
  800. * @return mixed Database query results 
  801. */ 
  802. function get_results( $query = null, $output = OBJECT )  
  803. $this->func_call = "\$db->get_results(\"$query\", $output)"; 
  804.  
  805. if ( $query ) { 
  806. $this->query($query); 
  807. } else { 
  808. return null; 
  809.  
  810. if ( $output == OBJECT ) { 
  811. // Return an integer-keyed array of row objects 
  812. return $this->last_result; 
  813. } elseif ( $output == OBJECT_K || $output == ARRAY_K ) { 
  814. // Return an array of row objects with keys from column 1 
  815. // (Duplicates are discarded) 
  816. $key = $this->col_info[0]->name; 
  817. foreach ( $this->last_result as $row ) { 
  818. if ( !isset( $new_array[ $row->$key ] ) ) { 
  819. $new_array[ $row->$key ] = $row; 
  820. if ( $output == ARRAY_K ) { 
  821. return array_map( 'get_object_vars', $new_array ); 
  822. return $new_array; 
  823. } elseif ( $output == ARRAY_A || $output == ARRAY_N ) { 
  824. // Return an integer-keyed array of... 
  825. if ( $this->last_result ) { 
  826. $i = 0; 
  827. foreach( $this->last_result as $row ) { 
  828. if ( $output == ARRAY_N ) { 
  829. // ...integer-keyed row arrays 
  830. $new_array[$i] = array_values( get_object_vars( $row ) ); 
  831. } else { 
  832. // ...column name-keyed row arrays 
  833. $new_array[$i] = get_object_vars( $row ); 
  834. ++$i; 
  835. return $new_array; 
  836.  
  837. /** 
  838. * Retrieve column metadata from the last query. 
  839. * 
  840. * @since 1.0 
  841. * 
  842. * @param string $info_type one of name, table, def, max_length, not_null, primary_key, multiple_key, unique_key, numeric, blob, type, unsigned, zerofill 
  843. * @param int $col_offset 0: col name. 1: which table the col's in. 2: col's max length. 3: if the col is numeric. 4: col's type 
  844. * @return mixed Column Results 
  845. */ 
  846. function get_col_info( $info_type = 'name', $col_offset = -1 ) 
  847. if ( $this->col_info ) { 
  848. if ( $col_offset == -1 ) { 
  849. $i = 0; 
  850. foreach( (array) $this->col_info as $col ) { 
  851. $new_array[$i] = $col->{$info_type}; 
  852. $i++; 
  853. return $new_array; 
  854. } else { 
  855. return $this->col_info[$col_offset]->{$info_type}; 
  856.  
  857. /** 
  858. * Starts the timer, for debugging purposes. 
  859. * 
  860. * @since 1.0 
  861. * 
  862. * @return true 
  863. */ 
  864. function timer_start() 
  865. $mtime = microtime(); 
  866. $mtime = explode( ' ', $mtime ); 
  867. $this->time_start = $mtime[1] + $mtime[0]; 
  868. return true; 
  869.  
  870. /** 
  871. * Stops the debugging timer. 
  872. * 
  873. * @since 1.0 
  874. * 
  875. * @return int Total time spent on the query, in milliseconds 
  876. */ 
  877. function timer_stop() 
  878. $mtime = microtime(); 
  879. $mtime = explode( ' ', $mtime ); 
  880. $time_end = $mtime[1] + $mtime[0]; 
  881. $time_total = $time_end - $this->time_start; 
  882. return $time_total; 
  883.  
  884. /** 
  885. * Wraps errors in a nice header and footer and dies. 
  886. * 
  887. * Will not die if bpdb::$show_errors is true 
  888. * 
  889. * @since 1.0 
  890. * 
  891. * @param string $message 
  892. * @return false|void 
  893. */ 
  894. function bail( $message ) 
  895. if ( !$this->show_errors ) { 
  896. if ( class_exists( 'WP_Error' ) ) 
  897. $this->error = new WP_Error( '500', $message ); 
  898. else 
  899. $this->error = $message; 
  900. return false; 
  901. backpress_die( $message ); 
  902.  
  903. /** 
  904. * Whether or not MySQL database is at least the required minimum version. 
  905. * 
  906. * @since 1.0 
  907. * 
  908. * @return WP_Error 
  909. */ 
  910. function check_database_version( $dbh_or_table = false ) 
  911. // Make sure the server has MySQL 4.0 
  912. if ( version_compare( $this->db_version( $dbh_or_table ), '4.0.0', '<' ) ) { 
  913. return new WP_Error( 'database_version', BPDB__DB_VERSION_ERROR ); 
  914.  
  915. /** 
  916. * Whether of not the database supports collation. 
  917. * 
  918. * Called when BackPress is generating the table scheme. 
  919. * 
  920. * @since 1.0 
  921. * 
  922. * @return bool True if collation is supported, false if version does not 
  923. */ 
  924. function supports_collation() 
  925. return $this->has_cap( 'collation' ); 
  926.  
  927. /** 
  928. * Generic function to determine if a database supports a particular feature 
  929. * 
  930. * @since 1.0 
  931. * 
  932. * @param string $db_cap the feature 
  933. * @param false|string|resource $dbh_or_table Which database to test. False = the currently selected database, string = the database containing the specified table, resource = the database corresponding to the specified mysql resource. 
  934. * @return bool 
  935. */ 
  936. function has_cap( $db_cap, $dbh_or_table = false ) 
  937. $version = $this->db_version( $dbh_or_table ); 
  938.  
  939. switch ( strtolower( $db_cap ) ) { 
  940. case 'collation' : 
  941. case 'group_concat' : 
  942. case 'subqueries' : 
  943. return version_compare( $version, '4.1', '>=' ); 
  944. break; 
  945.  
  946. case 'index_hint_for_join' : 
  947. return version_compare( $version, '5.0', '>=' ); 
  948. break; 
  949.  
  950. case 'index_hint_lists' : 
  951. case 'index_hint_for_any' : 
  952. return version_compare( $version, '5.1', '>=' ); 
  953. break; 
  954.  
  955. return false; 
  956.  
  957. /** 
  958. * The database version number 
  959. * 
  960. * @since 1.0 
  961. * 
  962. * @param false|string|resource $dbh_or_table Which database to test. False = the currently selected database, string = the database containing the specified table, resource = the database corresponding to the specified mysql resource. 
  963. * @return false|string false on failure, version number on success 
  964. */ 
  965. function db_version( $dbh_or_table = false ) 
  966. if ( !$dbh_or_table ) { 
  967. $dbh =& $this->dbh; 
  968. } elseif ( is_resource( $dbh_or_table ) ) { 
  969. $dbh =& $dbh_or_table; 
  970. } else { 
  971. $dbh = $this->db_connect( "DESCRIBE $dbh_or_table" ); 
  972.  
  973. if ( $dbh ) { 
  974. return preg_replace( '|[^0-9\.]|', '', mysql_get_server_info( $dbh ) ); 
  975. return false; 
  976.  
  977. /** 
  978. * Retrieve the name of the function that called bpdb. 
  979. * 
  980. * Requires PHP 4.3 and searches up the list of functions until it reaches 
  981. * the one that would most logically had called this method. 
  982. * 
  983. * @since 1.0 
  984. * 
  985. * @return string The name of the calling function 
  986. */ 
  987. function get_caller() 
  988. // requires PHP 4.3+ 
  989. if ( !is_callable( 'debug_backtrace' ) ) { 
  990. return ''; 
  991.  
  992. $bt = debug_backtrace(); 
  993. $caller = array(); 
  994.  
  995. $bt = array_reverse( $bt ); 
  996. foreach ( (array) $bt as $call ) { 
  997. if ( @$call['class'] == __CLASS__ ) { 
  998. continue; 
  999. $function = $call['function']; 
  1000. if ( isset( $call['class'] ) ) { 
  1001. $function = $call['class'] . "->$function"; 
  1002. $caller[] = $function; 
  1003. $caller = join( ', ', $caller ); 
  1004.  
  1005. return $caller; 
.