diff --git a/src/wp-includes/class-wp-hook.php b/src/wp-includes/class-wp-hook.php index d95691df6ad0b..9ef0c10edc484 100644 --- a/src/wp-includes/class-wp-hook.php +++ b/src/wp-includes/class-wp-hook.php @@ -18,6 +18,14 @@ #[AllowDynamicProperties] final class WP_Hook implements Iterator, ArrayAccess { + /** + * Hook callers. + * + * @since 4.7.0 + * @var array + */ + public $callers = array(); + /** * Hook callbacks. * @@ -99,6 +107,19 @@ public function add_filter( $hook_name, $callback, $priority, $accepted_args ) { if ( $this->nesting_level > 0 ) { $this->resort_active_iterations( $priority, $priority_existed ); } + + // Check if plugins are loaded + if ( defined( 'WP_PLUGIN_DIR' ) ) { + // Get the caller file path + $caller = debug_backtrace()[1]['file']; + + // Check if a plugin is adding this filter + if ( substr( $caller, 0, strlen( WP_PLUGIN_DIR ) ) === WP_PLUGIN_DIR ) { + // Strip the WP_PLUGIN_DIR from the caller path + $caller = substr( $caller, strlen( WP_PLUGIN_DIR ) ); + array_push( $this->callers, $caller ); + } + } } /** diff --git a/src/wp-includes/plugin.php b/src/wp-includes/plugin.php index 77c1eb4ef669b..104c4d2e35334 100644 --- a/src/wp-includes/plugin.php +++ b/src/wp-includes/plugin.php @@ -714,6 +714,11 @@ function apply_filters_deprecated( $hook_name, $args, $version, $replacement = ' return $args[0]; } + // Filters added by plugins have a caller to easily identify the source + global $wp_filter; + $callers = implode( ', ', $wp_filter[ $hook_name ]->callers ); + $message .= ' ' . __( 'used_by' ) . ' ' . $callers; + _deprecated_hook( $hook_name, $version, $replacement, $message ); return apply_filters_ref_array( $hook_name, $args );