Skip to content

Instantly share code, notes, and snippets.

@a-ludi
Last active August 29, 2015 14:27
Show Gist options
  • Save a-ludi/354de07403192e64c456 to your computer and use it in GitHub Desktop.
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.
<?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