Last active
March 14, 2022 19:47
-
-
Save jeanox/9a748830a01a6bbc1228a980e174ba96 to your computer and use it in GitHub Desktop.
Remove Class Filters w/ Wordpress ID/Hash Conditional
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
/** | |
* Make sure the function does not exist before defining it | |
*/ | |
if( ! function_exists( 'remove_class_filter' ) ){ | |
/** | |
* Remove Class Filter Without Access to Class Object | |
* | |
* In order to use the core WordPress remove_filter() on a filter added with the callback | |
* to a class, you either have to have access to that class object, or it has to be a call | |
* to a static method. This method allows you to remove filters with a callback to a class | |
* you don't have access to. | |
* | |
* Works with WordPress 1.2+ (4.7+ support added 9-19-2016) | |
* Updated 2-27-2017 to use internal WordPress removal for 4.7+ (to prevent PHP warnings output) | |
* Updated 03-04-2022 to account for wordpress id/hashes that are often prepended to filters. | |
* | |
* @param string $tag Filter to remove | |
* @param string $class_name Class name for the filter's callback | |
* @param string $method_name Method name for the filter's callback | |
* @param int $priority Priority of the filter (default 10) | |
* | |
* @return bool Whether the function is removed. | |
*/ | |
function remove_class_filter( $tag, $class_name = '', $method_name = '', $priority = 10 ) { | |
global $wp_filter; | |
// Setting up variables in the event that Wordpress prepends a hash to the method | |
$method_key = $GLOBALS['wp_filter'][$tag]->callbacks[$priority]; | |
$method_keyname = implode( preg_grep( '/' . preg_quote($method_name) . '/', array_keys($method_key) ) ); | |
$method_hash = substr( $method_keyname, 0, -strlen($method_name) ); | |
// Check that filter actually exists first | |
if ( ! isset( $wp_filter[ $tag ] ) ) { | |
return FALSE; | |
} | |
/** | |
* If filter config is an object, means we're using WordPress 4.7+ and the config is no longer | |
* a simple array, rather it is an object that implements the ArrayAccess interface. | |
* | |
* To be backwards compatible, we set $callbacks equal to the correct array as a reference (so $wp_filter is updated) | |
* | |
* @see https://make.wordpress.org/core/2016/09/08/wp_hook-next-generation-actions-and-filters/ | |
*/ | |
if ( is_object( $wp_filter[ $tag ] ) && isset( $wp_filter[ $tag ]->callbacks ) ) { | |
// Create $fob object from filter tag, to use below | |
$fob = $wp_filter[ $tag ]; | |
$callbacks = &$wp_filter[ $tag ]->callbacks; | |
} else { | |
$callbacks = &$wp_filter[ $tag ]; | |
} | |
// Exit if there aren't any callbacks for specified priority | |
if ( ! isset( $callbacks[ $priority ] ) || empty( $callbacks[ $priority ] ) ) { | |
return FALSE; | |
} | |
// Loop through each filter for the specified priority, looking for our class & method | |
foreach ( (array) $callbacks[ $priority ] as $filter_id => $filter ) { | |
// Filter should always be an array - array( $this, 'method' ), if not goto next | |
if ( ! isset( $filter['function'] ) || ! is_array( $filter['function'] ) ) { | |
continue; | |
} | |
// If first value in array is not an object, it can't be a class | |
if ( ! is_object( $filter['function'][0] ) ) { | |
continue; | |
} | |
// Method doesn't match the one we're looking for, goto next | |
if ( $filter['function'][1] !== $method_name ) { | |
continue; | |
} | |
// Method doesn't match with WP unique ID / Hash appended | |
if ( $method_hash . $filter['function'][1] !== $method_keyname ) { | |
continue; | |
} | |
// Method matched, now let's check the Class | |
if ( get_class( $filter['function'][0] ) === $class_name ) { | |
// WordPress 4.7+ use core remove_filter() since we found the class object | |
if ( isset( $fob ) ) { | |
// Handles removing filter, reseting callback priority keys mid-iteration, etc. | |
$fob->remove_filter( $tag, $filter['function'], $priority ); | |
} else { | |
// Use legacy removal process (pre 4.7) | |
unset( $callbacks[ $priority ][ $filter_id ] ); | |
// and if it was the only filter in that priority, unset that priority | |
if ( empty( $callbacks[ $priority ] ) ) { | |
unset( $callbacks[ $priority ] ); | |
} | |
// and if the only filter for that tag, set the tag to an empty array | |
if ( empty( $callbacks ) ) { | |
$callbacks = array(); | |
} | |
// Remove this filter from merged_filters, which specifies if filters have been sorted | |
unset( $GLOBALS['merged_filters'][ $tag ] ); | |
} | |
return TRUE; | |
} | |
} | |
return FALSE; | |
} | |
} | |
/** | |
* Make sure the function does not exist before defining it | |
*/ | |
if( ! function_exists( 'remove_class_action') ){ | |
/** | |
* Remove Class Action Without Access to Class Object | |
* | |
* In order to use the core WordPress remove_action() on an action added with the callback | |
* to a class, you either have to have access to that class object, or it has to be a call | |
* to a static method. This method allows you to remove actions with a callback to a class | |
* you don't have access to. | |
* | |
* Works with WordPress 1.2+ (4.7+ support added 9-19-2016) | |
* | |
* @param string $tag Action to remove | |
* @param string $class_name Class name for the action's callback | |
* @param string $method_name Method name for the action's callback | |
* @param int $priority Priority of the action (default 10) | |
* | |
* @return bool Whether the function is removed. | |
*/ | |
function remove_class_action( $tag, $class_name = '', $method_name = '', $priority = 10 ) { | |
remove_class_filter( $tag, $class_name, $method_name, $priority ); | |
} | |
} | |
/* | |
// APPLICATION EXAMPLE (place this in a functions.php file or have it referenced | |
// This specific example is to remove these polylang filters which were really slowing down page speed (redis) | |
// NOTE: the priority stated in remove_class_filter must match the original filter defined in the plugin | |
// NOTE2: The supplementary remove_filter was necessary to completely shed caching for polylang terms and it needed to happen in this order to work (hence numbered comments) | |
// Polylang Terms Caching (disabled) | |
// 1. Removes filters registered in the Polylang Plugin (priority must be exact match) | |
add_action('init', function(){ | |
remove_class_filter( 'get_terms', 'PLL_Translated_Term', '_prime_terms_cache', 10); | |
remove_class_filter( 'get_object_terms', 'PLL_Translated_Term', 'wp_get_object_terms', 10); | |
remove_class_action( 'clean_term_cache', 'PLL_Translated_Term', 'clean_term_cache', 10 ); | |
}, 99); | |
// 2. Supplementary to remove_class_action by targeting parent classes too. | |
add_action('init', function(){ | |
remove_filter('get_terms', ['PLL_Translated_Term', '_prime_terms_cache'], 10); | |
remove_filter('get_object_terms', ['PLL_Translated_Term', 'wp_get_object_terms'], 10); | |
remove_action('clean_term_cache', ['PLL_Translated_Term', 'clean_term_cache'], 10); | |
}, 99); | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment