Skip to content

Instantly share code, notes, and snippets.

@josegonzalez
Created January 14, 2014 09:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save josegonzalez/8415777 to your computer and use it in GitHub Desktop.
Save josegonzalez/8415777 to your computer and use it in GitHub Desktop.
<?php
/**
* APC-based WordPress Database Access Abstraction Object
*
* Extends the wpdb class in order to cache queries in APC
*
* Falls back to non-APC database when requesting an admin resource or
* the query modifies the database.
*
* Using something like Memcached might be better as we can namespace
* the keys and clear them whenever we need to without affecting other apps
* on the server
*
* @link http://codex.wordpress.org/Function_Reference/wpdb_Class
*/
class APC_WPDB extends wpdb {
/**
* Perform a MySQL database query, using current database connection.
*
* More information can be found on the codex page.
*
* @since 0.71
*
* @param string $query Database query
* @return int|false Number of rows affected/selected or false on error
*/
function query( $query ) {
if ( ! $this->ready )
return false;
// some queries are made before the plugins have been
// loaded, and thus cannot be filtered with this method
if ( function_exists( 'apply_filters' ) )
$query = apply_filters( 'query', $query );
$this->flush();
// Log how the function was called
$this->func_call = "\$db->query(\"$query\")";
// Keep track of the last query for debug..
$this->last_query = $query;
if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES )
$this->timer_start();
// Don't try caching any database altering queries
// Or WordPress's lovely SQL_CALC_FOUND_ROWS related-queries
// This is a performance hit, but wordpress appears to enjoy SQL_CALC_FOUND_ROWS
// And until they move away from it (never), these queries cannot be safely cached
// TODO: Cache SQL_CALC_FOUND_ROWS queries via request
if (preg_match( "/^\\s*(insert|delete|update|replace|alter|FOUND_ROWS) /i", $query) || preg_match("/FOUND_ROWS/i", $query)) {
return $this->no_cache_query($query);
}
$response = $this->getCache($query);
if ($response === false) {
return $this->no_cache_query($query);
}
// Cache the shit out of this bitch
$this->num_queries++;
if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES )
$this->queries[] = array( $query, $this->timer_stop(), $this->get_caller() );
$this->col_info = $response['col_info'];
$this->last_result = $response['last_result'];
$this->num_rows = $response['num_rows'];
return $response['return'];
}
/**
* Performs a regular query against the
* database without caching the response
*
* @param string $query SQL Query being performed
* @return mixed result of query, or false on error
*/
function no_cache_query($query) {
$this->result = @mysql_query( $query, $this->dbh );
$this->num_queries++;
if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES )
$this->queries[] = array( $query, $this->timer_stop(), $this->get_caller() );
// If there is an error then take note of it..
if ( $this->last_error = mysql_error( $this->dbh ) ) {
$this->print_error();
return false;
}
$return_val = 0;
if ( preg_match( '/^\s*(create|alter|truncate|drop) /i', $query ) ) {
$return_val = $this->result;
} elseif ( preg_match( '/^\s*(insert|delete|update|replace) /i', $query ) ) {
$this->rows_affected = mysql_affected_rows( $this->dbh );
// Take note of the insert_id
if ( preg_match( "/^\s*(insert|replace) /i", $query ) ) {
$this->insert_id = mysql_insert_id($this->dbh);
}
// Return number of rows affected
$return_val = $this->rows_affected;
} else {
$i = 0;
while ( $i < @mysql_num_fields( $this->result ) ) {
$this->col_info[$i] = @mysql_fetch_field( $this->result );
$i++;
}
$num_rows = 0;
while ( $row = @mysql_fetch_object( $this->result ) ) {
$this->last_result[$num_rows] = $row;
$num_rows++;
}
@mysql_free_result( $this->result );
// Log number of rows the query returned
// and return number of rows selected
$this->num_rows = $num_rows;
$return_val = $num_rows;
$this->setCache($query, array(
'col_info' => $this->col_info,
'last_result' => $this->last_result,
'num_rows' => $this->num_rows,
'return' => $return_val,
));
}
return $return_val;
}
/**
* Sets a cache value
*
* @param string $key name of unsha'd cache key
* @param mixed $value value to cache
* @param int $time time to cache in seconds
* @return boolean returns the success of the apc value caching
*/
public function setCache($key, $value, $time = 300) {
if ($value === false) return false;
return apc_add(sha1($key), $value, $time);
}
/**
* Retrieves a key from cache
*
* @param string $key name of unsha'd cache key
* @return mixed cached resultset pertaining to the key
*/
public function getCache($key) {
return apc_fetch(sha1($key));
}
}
/**
* Convenience method that can be used to check if caching is possible
*
* @return boolean true is the current request is cacheable, false otherwise
*/
function apc_canCacheQueries() {
if ((defined('WP_ADMIN') && WP_ADMIN) || (defined('DOING_CRON') && DOING_CRON) || (defined('DOING_AJAX') && DOING_AJAX)) {
return false;
}
if (preg_match('/(wp-admin|wp-login|wp-register|wp-signup)/', $_SERVER['REQUEST_URI']) || isset($_GET['preview'])) {
return false;
}
return true;
}
/**
* Creates a WPDB Object which is APC-based when cacheable and WP-based when not
*/
if (!isset($wpdb)) {
if (function_exists('apc_fetch') && apc_canCacheQueries()) {
$wpdb = new APC_WPDB( DB_USER, DB_PASSWORD, DB_NAME, DB_HOST );
} else {
$wpdb = new wpdb( 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