Last active
August 29, 2015 14:27
-
-
Save a-ludi/354de07403192e64c456 to your computer and use it in GitHub Desktop.
This is an extension to the default `wpdb` class providing some debugging facilities as well as `ez_*` family of functions for easy handling of MySQL queries.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* Copyright © 2015 Arne Ludwig <arne.ludwig@posteo.de> | |
* | |
* This program is free software: you can redistribute it and/or modify it | |
* under the terms of the GNU General Public License as published by the Free | |
* Software Foundation, either version 3 of the License, or (at your option) | |
* any later version. | |
* | |
* This program is distributed in the hope that it will be useful, but WITHOUT | |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
* more details. | |
* | |
* You should have received a copy of the GNU General Public License along | |
* with this program. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
/** | |
* This is an extension to the default wpdb class providing some debugging | |
* facilities as well as `ez_*` family of functions for easy handling of MySQL | |
* queries. | |
* | |
* | |
* Installation/Usage | |
* ================== | |
* | |
* Just drop this file in your `ABSPATH/wp-content` folder. It will replace | |
* the global variable `$wpdb` with an instance of this class. | |
*/ | |
class wpdb_debug extends wpdb { | |
/** | |
* If this evaluates to `true` then every query will be printed before | |
* execution. | |
* | |
* @see wpdb_debug::$format | |
*/ | |
public $echo_queries; | |
/** | |
* Select the format of the echoed queries. This may be `html` or `text`. | |
* Defaults to `html`. | |
*/ | |
public $format; | |
/** | |
* If this evaluates to `true` then queries will not be executed, but every | |
* other action will hapen as expected. | |
* | |
* @see wpdb_debug::$dry_run_result | |
*/ | |
public $dry_run; | |
/** | |
* This value will be returned by `wpdb_debug::query()` if | |
* `wpdb_debug::$dry_run` is true. | |
*/ | |
public $dry_run_result; | |
public function __construct($dbuser, $dbpassword, $dbname, $dbhost) { | |
parent::__construct($dbuser, $dbpassword, $dbname, $dbhost); | |
$this->echo_queries = false; | |
$this->dry_run = false; | |
$this->dry_run_result = 0; | |
$this->format = 'html'; | |
} | |
/** | |
* Execute `$callback` with debugging aids. The aids are given as additional | |
* arguments (strings) after the callback. Available aids are: | |
* | |
* - __dry_run:__ Do not execute queries, but do everything else. Use the | |
* `wpdb_debug::$dry_run_result` member variable to set a default return | |
* value for queries. | |
* - __echo:__ Print every query that is executed. The member variable | |
* `wpdb_debug::$format` controls the output format. | |
*/ | |
public function run_with($callback) { | |
$args = func_get_args(); | |
if(count($args) == 2 && is_array($args[1])) | |
$args = $args[1]; | |
elseif(count($args) > 1) | |
array_shift($args); | |
$orig_echo_queries = $this->echo_queries; | |
$orig_dry_run = $this->dry_run; | |
$this->dry_run = in_array('dry_run', $args, true); | |
$this->echo_queries = in_array('echo', $args, true); | |
$return = call_user_func($callback); | |
$this->echo_queries = $orig_echo_queries; | |
$this->dry_run = $orig_dry_run; | |
return $return; | |
} | |
/** | |
* The original `wpdb::query()` method but extended with debugging aids. | |
* The aids will be available only if `WP_DEBUG != false`. | |
*/ | |
public function query($query) { | |
if(! defined('WP_DEBUG') || WP_DEBUG == false) | |
return parent::query($query); | |
if($this->echo_queries) | |
$this->echo_query($query); | |
return $this->dry_run ? | |
$this->dry_run_result : | |
parent::query($query); | |
} | |
private function echo_query($query) { | |
switch((string) $this->_output) { | |
case 'text': | |
echo "[SQL: $query]".PHP_EOL; | |
break; | |
case 'html': | |
default: | |
echo '<pre class="postbox" style="padding: 20px;">'; | |
echo esc_html($query); | |
echo '</pre>'; | |
break; | |
} | |
} | |
/** | |
* Get the last MySQL error if any. | |
* | |
* @see mysqli_error() | |
* @see mysql_error() | |
*/ | |
public function last_error() { | |
return $this->use_mysqli ? | |
mysqli_error($this->dbh) : | |
mysql_error($this->dbh); | |
} | |
const E_NOT_ENOUGH_PLACEHOLDERS = 1; | |
const E_TOO_MANY_PLACEHOLDERS = 2; | |
const E_UNEXPECTED_TYPE = 3; | |
/** | |
* Prepares a SQL query for safe execution. Uses `?` as a placeholder. Use | |
* `\?` to insert a literal `?`. | |
*/ | |
public function ez_prepare($query, $args) { | |
if(empty($query)) | |
return ''; | |
$args = func_get_args(); | |
array_shift( $args ); | |
// If args were passed as an array (as in vsprintf), move them up | |
if(isset($args[0]) && is_array($args[0])) | |
$args = $args[0]; | |
reset($args); | |
$error = false; | |
$query = preg_replace_callback( | |
'/(?<!\\\\)\\?/', | |
function($_) use(&$args, $error) { | |
list($key, $arg) = each($args); | |
switch(gettype($arg)) { | |
case 'integer': | |
case 'boolean': | |
return '%d'; | |
case 'double': | |
return '%f'; | |
case 'string': | |
return '%s'; | |
case 'NULL': | |
unset($args[$key]); | |
return 'NULL'; | |
default: | |
$error = self::E_UNEXPECTED_TYPE; | |
return ''; | |
} | |
}, | |
$query | |
); | |
if(false !== each($args)) | |
$error = self::E_TOO_MANY_PLACEHOLDERS; | |
if($error) { | |
switch($error) { | |
case self::E_NOT_ENOUGH_PLACEHOLDERS: | |
trigger_error( | |
'not enough placeholders in query string', | |
E_USER_WARNING | |
); | |
break; | |
case self::E_TOO_MANY_PLACEHOLDERS: | |
trigger_error( | |
'too many placeholders in query string', | |
E_USER_WARNING | |
); | |
break; | |
case self::E_UNEXPECTED_TYPE: | |
trigger_error( | |
'arguments must be integer, float or string.', | |
E_USER_WARNING | |
); | |
break; | |
default: | |
break; | |
} | |
return false; | |
} | |
if(count($args) > 0) | |
return $this->prepare($query, $args); | |
else | |
return $query; | |
} | |
/** | |
* Prepare and execute the given query. This is roughly equivalent to: | |
* | |
* $prepared_query = $wpdb->ez_prepare($query, $args); | |
* $result = $wpdb->query($prepared_query); | |
*/ | |
public function ez_query($query, $args=array()) { | |
if(empty($query)) | |
return ''; | |
$args = func_get_args(); | |
array_shift( $args ); | |
// If args were passed as an array (as in vsprintf), move them up | |
if(isset($args[0]) && is_array($args[0])) | |
$args = $args[0]; | |
$prepared_query = $this->ez_prepare($query, $args); | |
if(false === $prepared_query) | |
return false; | |
return $this->query($prepared_query); | |
} | |
} | |
global $wpdb; | |
$wpdb = new wpdb_debug(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST); | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment