Key from key() => the callback WordPress holds. */ private static array $wrapped = array(); public static function action( string $hook, callable $callback, int $priority = 10, int $accepted_args = 1 ): void { self::add( $hook, $callback, $priority, $accepted_args, false ); } public static function filter( string $hook, callable $callback, int $priority = 10, int $accepted_args = 1 ): void { self::add( $hook, $callback, $priority, $accepted_args, true ); } /** Is this plugin callback attached to the hook? Priority, or false. */ public static function has( string $hook, callable $callback ): int|false { foreach ( self::$wrapped as $key => $closure ) { if ( str_starts_with( $key, $hook . '|' ) && str_ends_with( $key, '|' . self::id( $callback ) ) ) { return has_filter( $hook, $closure ); } } return false; } /** Detach a plugin callback, like remove_filter(). */ public static function remove( string $hook, callable $callback, int $priority = 10 ): bool { $key = self::key( $hook, $priority, $callback ); if ( ! isset( self::$wrapped[ $key ] ) ) { return false; } $removed = remove_filter( $hook, self::$wrapped[ $key ], $priority ); unset( self::$wrapped[ $key ] ); return $removed; } private static function add( string $hook, callable $callback, int $priority, int $accepted_args, bool $is_filter ): void { if ( str_starts_with( $hook, 'admin_post_' ) ) { add_filter( $hook, $callback, $priority, $accepted_args ); Failsafe::track( $hook, $callback, $priority ); return; } $wrapped = Failsafe::guard( $callback, $hook, $is_filter ); self::$wrapped[ self::key( $hook, $priority, $callback ) ] = $wrapped; add_filter( $hook, $wrapped, $priority, $accepted_args ); Failsafe::track( $hook, $wrapped, $priority ); } private static function key( string $hook, int $priority, callable $callback ): string { return $hook . '|' . $priority . '|' . self::id( $callback ); } /** Same idea as _wp_filter_build_unique_id(): object id + method, or the function name. */ private static function id( callable $callback ): string { if ( is_string( $callback ) ) { return $callback; } if ( is_object( $callback ) ) { return (string) spl_object_id( $callback ); } if ( is_array( $callback ) ) { $target = is_object( $callback[0] ) ? (string) spl_object_id( $callback[0] ) : (string) $callback[0]; return $target . '::' . (string) $callback[1]; } return ''; } }