<?php
/**
* This file contains a bunch of helper functions that handle add caching to core WordPress functions.
*
*/
defined( 'ABSPATH' ) || exit; // Exit if accessed directly
if( ! class_exists( 'TIELABS_WP_HELPER' ) ) {
class TIELABS_WP_HELPER {
/**
* Runs on class initialization. Adds filters and actions.
*/
function __construct() {
// Properly clear get_term_by() cache when a term is updated
add_action( 'edit_terms', array( $this, 'wp_flush_get_term_by_cache' ), 10, 2 );
// Properly clear get_term_by() cache when a term is updated
add_action( 'edit_terms', array( $this, 'wp_flush_get_term_by_cache' ), 10, 2 );
// Properly clear term_exists() cache when a term is updated
add_action( 'delete_term', array( $this, 'wp_flush_term_exists' ), 10, 4 );
// Flush the cache for published pages so we don't end up with stale data
add_action( 'transition_post_status', array( $this, 'flush_get_page_by_path_cache' ), 10, 3 );
add_action( 'transition_post_status', array( $this, 'flush_get_page_by_title_cache' ), 10, 3 );
}
/**
* Cached version of get_term_by.
*
* Many calls to get_term_by (with name or slug lookup) across on a single pageload can easily add up the query count.
* This function helps prevent that by adding a layer of caching.
*
* @param string $field Either 'slug', 'name', or 'id'
* @param string|int $value Search for this term value
* @param string $taxonomy Taxonomy Name
* @param string $output Optional. Constant OBJECT, ARRAY_A, or ARRAY_N
* @param string $filter Optional. Default is 'raw' or no WordPress defined filter will applied.
* @return mixed|null|bool Term Row from database in the type specified by $filter. Will return false if $taxonomy does not exist or $term was not found.
* @link http://vip.wordpress.com/documentation/uncached-functions/ Uncached Functions
*/
public static function get_term_by( $field, $value, $taxonomy, $output = OBJECT, $filter = 'raw' ){
// ID lookups are cached
if ( 'id' == $field ){
return get_term_by( $field, $value, $taxonomy, $output, $filter );
}
$cache_key = $field . '|' . $taxonomy . '|' . md5( $value );
$term_id = wp_cache_get( $cache_key, 'get_term_by' );
if ( false === $term_id ){
$term = get_term_by( $field, $value, $taxonomy );
if ( $term && ! is_wp_error( $term ) ){
wp_cache_set( $cache_key, $term->term_id, 'get_term_by' );
} else{
wp_cache_set( $cache_key, 0, 'get_term_by' ); // if we get an invalid value, let's cache it anyway
}
} else {
$term = get_term( $term_id, $taxonomy, $output, $filter );
}
if ( is_wp_error( $term ) ){
$term = false;
}
return $term;
}
/**
* Cached version of term_exists()
*
* Term exists calls can pile up on a single pageload.
* This function adds a layer of caching to prevent lots of queries.
*
* @param int|string $term The term to check can be id, slug or name.
* @param string $taxonomy The taxonomy name to use
* @param int $parent Optional. ID of parent term under which to confine the exists search.
* @return mixed Returns null if the term does not exist. Returns the term ID
* if no taxonomy is specified and the term ID exists. Returns
* an array of the term ID and the term taxonomy ID the taxonomy
* is specified and the pairing exists.
*/
public static function term_exists( $term, $taxonomy = '', $parent = null ){
//If $parent is not null, let's skip the cache.
if ( null !== $parent ){
return term_exists( $term, $taxonomy, $parent );
}
if ( ! empty( $taxonomy ) ){
$cache_key = $term . '|' . $taxonomy;
}else{
$cache_key = $term;
}
$cache_value = wp_cache_get( $cache_key, 'term_exists' );
//term_exists frequently returns null, but (happily) never false
if ( false === $cache_value ){
$term_exists = term_exists( $term, $taxonomy );
wp_cache_set( $cache_key, $term_exists, 'term_exists' );
}else{
$term_exists = $cache_value;
}
if ( is_wp_error( $term_exists ) )
$term_exists = null;
return $term_exists;
}
/**
* Optimized version of get_term_link that adds caching for slug-based lookups.
*
* Returns permalink for a taxonomy term archive, or a WP_Error object if the term does not exist.
*
* @param int|string|object $term The term object / term ID / term slug whose link will be retrieved.
* @param string $taxonomy The taxonomy slug. NOT required if you pass the term object in the first parameter
*
* @return string|WP_Error HTML link to taxonomy term archive on success, WP_Error if term does not exist.
*/
public static function get_term_link( $term, $taxonomy = null ){
// ID- or object-based lookups already result in cached lookups, so we can ignore those.
if ( is_numeric( $term ) || is_object( $term ) ){
return get_term_link( $term, $taxonomy );
}
$term_object = self::get_term_by( 'slug', $term, $taxonomy );
return get_term_link( $term_object );
}
/**
* Cached version of count_user_posts, which is uncached but doesn't always need to hit the db
*
* count_user_posts is generally fast, but it can be easy to end up with many redundant queries
* if it's called several times per request. This allows bypassing the db queries in favor of
* the cache
*/
public static function count_user_posts( $user_id ){
if ( ! is_numeric( $user_id ) ){
return 0;
}
$cache_key = 'vip_' . (int) $user_id;
$cache_group = 'user_posts_count';
if ( false === ( $count = wp_cache_get( $cache_key, $cache_group ) ) ){
$count = count_user_posts( $user_id );
wp_cache_set( $cache_key, $count, $cache_group, 5 * MINUTE_IN_SECONDS );
}
return $count;
}
/**
* Properly clear get_term_by() cache when a term is updated
*/
function wp_flush_get_term_by_cache( $term_id, $taxonomy ){
$term = get_term_by( 'id', $term_id, $taxonomy );
if ( ! $term ){
return;
}
foreach( array( 'name', 'slug' ) as $field ){
$cache_key = $field . '|' . $taxonomy . '|' . md5( $term->$field );
$cache_group = 'get_term_by';
wp_cache_delete( $cache_key, $cache_group );
}
}
/**
* Flush the cache for published pages so we don't end up with stale data
*
* @param string $new_status The post's new status
* @param string $old_status The post's previous status
* @param WP_Post $post The post
* @link http://vip.wordpress.com/documentation/uncached-functions/ Uncached Functions
*/
function flush_get_page_by_title_cache( $new_status, $old_status, $post ){
if ( 'publish' == $new_status || 'publish' == $old_status )
wp_cache_delete( $post->post_type . '_' . sanitize_key( $post->post_title ), 'get_page_by_title' );
}
/**
* Flush the cache for published pages so we don't end up with stale data
*
* @param string $new_status The post's new status
* @param string $old_status The post's previous status
* @param WP_Post $post The post
*
* @link http://vip.wordpress.com/documentation/uncached-functions/ Uncached Functions
*/
function flush_get_page_by_path_cache( $new_status, $old_status, $post ){
if ( 'publish' === $new_status || 'publish' === $old_status ){
$page_path = get_page_uri( $post->ID );
wp_cache_delete( $post->post_type . '_' . sanitize_key( $page_path ), 'get_page_by_path' );
}
}
/**
* Properly clear term_exists() cache when a term is updated
*/
function wp_flush_term_exists( $term, $tt_id, $taxonomy, $deleted_term ){
foreach( array( 'term_id', 'name', 'slug' ) as $field ){
$cache_key = $deleted_term->$field . '|' . $taxonomy ;
$cache_group = 'term_exists';
wp_cache_delete( $cache_key, $cache_group );
}
}
}
// Single instance.
$TIELABS_WP_HELPER = new TIELABS_WP_HELPER();
}