Hooks & Filters

This is a complete reference of all hooks and filters available in MilliCache.

Action Hooks#Copied!

Cache Storage Events#Copied!

millicache_entry_storing

Fires before a cache entry is stored.

 1add_action( 'millicache_entry_storing', function( $hash, $key, $flags, $data ) {
 2    // $hash  - Cache hash
 3    // $key   - Cache key
 4    // $flags - Array of flags
 5    // $data  - Cache data array
 6
 7    error_log( "Storing cache: $key with " . count( $flags ) . " flags" );
 8}, 10, 4 );

millicache_entry_stored

Fires after a cache entry is stored.

 1add_action( 'millicache_entry_stored', function( $hash, $key, $flags, $data ) {
 2    // Notify external CDN
 3    cdn_notify_cached( $key );
 4}, 10, 4 );

millicache_entry_deleting

Fires before a cache entry is deleted.

 1add_action( 'millicache_entry_deleting', function( $hash, $key, $flags, $url ) {
 2    error_log( "Deleting cache: $url" );
 3}, 10, 4 );

millicache_entry_deleted

Fires after a cache entry is deleted. The $url argument lets a listener mirror the eviction elsewhere (for example, purging a CDN/edge cache by URL) without having to resolve the hash back to a request.

 1add_action( 'millicache_entry_deleted', function( $hash, $key, $flags, $url ) {
 2    // $hash  - Cache hash
 3    // $key   - Cache key
 4    // $flags - Array of canonical flags (e.g. "2:post:123"), as emitted by
 5    //          millicache_cache_cleared_by_flags
 6    // $url   - The original request URL of the deleted entry
 7
 8    cdn_purge_url( $url );
 9}, 10, 4 );

millicache_entry_expired

Fires after a cache entry is expired (aged out) by flag, e.g. via millicache()->clear()->flags( ..., $expire = true ). Unlike deletion, the entry and its flag membership are preserved; only its freshness is reset, so the origin regenerates the response on the next request. Edge/CDN mirrors should treat this as a purge signal too, since the cached body is now stale.

The payload matches millicache_entry_deleted exactly: $hash, $key, canonical $flags, and $url.

 1add_action( 'millicache_entry_expired', function( $hash, $key, $flags, $url ) {
 2    cdn_purge_url( $url );
 3}, 10, 4 );

Cache Clearing Events#Copied!

millicache_cache_cleared_by_urls

Fires after cache is cleared by URLs.

 1add_action( 'millicache_cache_cleared_by_urls', function( $urls, $expire ) {
 2    // $urls   - Array of cleared URLs
 3    // $expire - Whether expire mode was used
 4
 5    foreach ( $urls as $url ) {
 6        error_log( "Cleared cache for URL: $url" );
 7    }
 8}, 10, 2 );

millicache_cache_cleared_by_posts

Fires after cache is cleared by post IDs.

 1add_action( 'millicache_cache_cleared_by_posts', function( $post_ids, $expire ) {
 2    // $post_ids - Array of post IDs
 3    // $expire   - Whether expire mode was used
 4
 5    foreach ( $post_ids as $post_id ) {
 6        error_log( "Cleared cache for post: $post_id" );
 7    }
 8}, 10, 2 );

millicache_cache_cleared_by_flags

Fires after cache is cleared by flags.

 1add_action( 'millicache_cache_cleared_by_flags', function( $flags, $expire ) {
 2    // $flags  - Array of cleared flags
 3    // $expire - Whether expire mode was used
 4
 5    // Notify external cache
 6    external_cache_purge_by_tags( $flags );
 7}, 10, 2 );

millicache_cache_cleared_by_sites

Fires after cache is cleared by site IDs (multisite).

 1add_action( 'millicache_cache_cleared_by_sites', function( $site_ids, $network_id, $expire ) {
 2    // $site_ids   - Array of site IDs
 3    // $network_id - Network ID (if specified)
 4    // $expire     - Whether expire mode was used
 5}, 10, 3 );

millicache_cache_cleared_by_networks

Fires after cache is cleared by network IDs.

 1add_action( 'millicache_cache_cleared_by_networks', function( $network_ids, $expire ) {
 2    // $network_ids - Array of network IDs
 3    // $expire      - Whether expire mode was used
 4}, 10, 2 );

millicache_cache_cleared

Fires after any cache clearing operation.

 1add_action( 'millicache_cache_cleared', function( $expire ) {
 2    // $expire - Whether expire mode was used
 3
 4    // Log clearing event
 5    error_log( 'MilliCache cleared at ' . current_time( 'mysql' ) );
 6
 7    // Clear external caches
 8    if ( function_exists( 'wp_cache_flush' ) ) {
 9        wp_cache_flush();
10    }
11} );

REST API Events#Copied!

millicache_rest_cache_action_performed

Fires after a REST API cache action is performed.

 1add_action( 'millicache_rest_cache_action_performed', function( $action, $params, $request ) {
 2    // $action  - Action performed (clear, clear_current, clear_targets)
 3    // $params  - Action parameters
 4    // $request - WP_REST_Request object
 5
 6    // Audit log
 7    audit_log( "Cache action: $action by user " . get_current_user_id() );
 8}, 10, 3 );

millicache_rest_settings_action_performed

Fires after a REST API settings action is performed.

 1add_action( 'millicache_rest_settings_action_performed', function( $action, $params, $request ) {
 2    // $action - Action performed (reset, restore)
 3
 4    audit_log( "Settings action: $action" );
 5}, 10, 3 );

Filter Hooks#Copied!

Settings Filters#Copied!

millicache_settings_defaults

Filter default settings.

 1add_filter( 'millicache_settings_defaults', function( $defaults ) {
 2    // Modify defaults
 3    $defaults['cache']['ttl'] = 3600;  // 1 hour default
 4
 5    return $defaults;
 6} );

Cache Entry Filters#Copied!

millicache_entry_headers

Filter the response headers stored with a cache entry. Runs on every store: a fresh cache miss and a stale-while-revalidate background regeneration alike. Because it runs on regeneration too, headers derived from the entry's flags (such as edge cache tags or Cache-Control: s-maxage) always match the flags being persisted and cannot drift.

 1add_filter( 'millicache_entry_headers', function( $headers, $flags, $context ) {
 2    // Leave private (per-cookie/variant) entries off shared caches.
 3    if ( null !== $context['variant'] ) {
 4        return $headers;
 5    }
 6
 7    // Replace it, don't append: strip the header we own before re-adding it.
 8    $headers = array_values( array_filter(
 9        $headers,
10        fn( $h ) => stripos( $h, 'Cache-Tag:' ) !== 0
11    ) );
12
13    $headers[] = 'Cache-Tag: ' . implode( ',', $flags );
14
15    return $headers;
16}, 10, 3 );

Parameters:

  • $headers (string[]) — Response headers as "Key: Value" strings, already scrubbed of MilliCache's own and per-hop headers.
  • $flags (string[]) — Canonical flags persisted with the entry, in the same form Redis stores minus the <storage_prefix>:f: key prefix. On multisite these carry the site/network prefix (e.g. 2:post:9); on single-site they are unprefixed (e.g. post:9).
  • $context (array) — Store-time context: url (string), variant (array|null; non-null marks a private entry), status (int), ttl (int seconds), grace (int seconds).
Important: Listeners must be replace-not-append: strip any header they own before re-adding it, so the filter stays idempotent across the miss and regeneration passes.

Cache Clearing Filters#Copied!

millicache_flags_related_to_post

Filter flags that should be cleared when a post is updated.

 1add_filter( 'millicache_flags_related_to_post', function( $flags, $post ) {
 2    // Add custom flags based on post content
 3    if ( has_block( 'myblock/featured', $post ) ) {
 4        $flags[] = 'featured-content';
 5    }
 6
 7    // Add taxonomy-based flags
 8    $categories = get_the_category( $post->ID );
 9    foreach ( $categories as $cat ) {
10        $flags[] = 'category:' . $cat->term_id;
11    }
12
13    return $flags;
14}, 10, 2 );

millicache_settings_clear_site_hooks

Filter hooks that trigger full site cache clearing.

 1add_filter( 'millicache_settings_clear_site_hooks', function( $hooks ) {
 2    // Add a custom hook that should clear the cache
 3    $hooks[] = 'my_custom_global_update';
 4
 5    // Remove a default hook
 6    $key = array_search( 'switch_theme', $hooks );
 7    if ( $key !== false ) {
 8        unset( $hooks[ $key ] );
 9    }
10
11    return $hooks;
12} );

Default hooks:

  • save_post_wp_template_part
  • customize_save_after
  • wp_update_nav_menu
  • switch_theme
  • update_option_permalink_structure
  • update_option_active_plugins

millicache_settings_clear_site_options

Filter options that trigger full site cache clearing when updated.

 1add_filter( 'millicache_settings_clear_site_options', function( $options ) {
 2    // Add a custom option
 3    $options[] = 'my_global_setting';
 4
 5    return $options;
 6} );

Bucket Extension#Copied!

Buckets are short canonical tokens folded into the cache key to differentiate per-request signals. MilliCache does not expose a runtime hook filter for buckets — advanced-cache.php runs before any plugin or mu-plugin loads, so a add_filter() callback registered on plugins_loaded would never run during cache lookup. The lookup hash and the write hash would diverge, producing a permanent cache miss.

Buckets are extended through two paths whose timing matches MilliCache's boot order:

Static bucket configuration

Lookup tables for resolvers go in the MC_CACHE_BUCKETS constant. Read during Config construction, available when the cache key is generated.

 1define( 'MC_CACHE_BUCKETS', [
 2    'tenant' => [ 'acme' => 'acme', 'globex' => 'glx' ],
 3    'ab'     => [ 'control' => 'a', 'variant' => 'b' ],
 4] );

Defining a lookup table doesn't bucket anything by itself; a resolver still has to read the table and call add_bucket().

Runtime bucket extension via the rules engine

For per-request bucket resolution that depends on conditions (URL match, header presence, cookie value, etc.), use the rules engine. Rules are evaluated during the early PHP phase, in time to influence the request hash.

The set_bucket PHP-phase action calls into:

 1MilliCacheEngineRequestBucketResolver::add_bucket( string $name, string $token ): void

add_bucket() is the programmatic extension point. Programmatic additions take precedence over built-in resolutions when names collide.

Example: bucket A/B test arms from a cookie:

Condition: cookie ab_arm matches /^[ab]$/
Action:    set_bucket name="ab" token="{cookie:ab_arm}"

The rule fires per request; the action calls $resolver->add_bucket('ab', $cookie_value). Cache entries for arm A and arm B stay distinct, but if the rendered HTML happens to be byte-identical they automatically share storage via the content-addressable output keyspace.

Note: Regular WordPress plugins cannot extend buckets at the cache-lookup phase because they load after advanced-cache.php. To influence cache differentiation, ship a settings update (extending MC_CACHE_BUCKETS) or register rules.

Request Flags Filters#Copied!

millicache_flags_for_request

Filter flags assigned to the current request.

 1add_filter( 'millicache_flags_for_request', function( $flags ) {
 2    // Add flags based on content
 3    if ( has_block( 'myblock/weather' ) ) {
 4        $flags[] = 'block:weather';
 5    }
 6
 7    // Add flags for WooCommerce
 8    if ( function_exists( 'is_shop' ) && is_shop() ) {
 9        $flags[] = 'woo:shop';
10    }
11
12    // Add a template-based flag
13    if ( is_page_template( 'templates/landing.php' ) ) {
14        $flags[] = 'template:landing';
15    }
16
17    return $flags;
18} );

Capability Filters#Copied!

millicache_clear_cache_capability

Filter the capability required to clear cache.

 1add_filter( 'millicache_clear_cache_capability', function( $capability ) {
 2    // Require higher capability
 3    return 'manage_options';  // Only administrators
 4
 5    // Or lower for specific sites
 6    // return 'edit_posts';  // Editors can clear
 7} );

Default: publish_pages


REST API Filters#Copied!

millicache_rest_cache_allowed_actions

Filter allowed cache actions via REST API.

 1add_filter( 'millicache_rest_cache_allowed_actions', function( $actions ) {
 2    // Add custom action
 3    $actions[] = 'my_custom_action';
 4
 5    // Remove an action
 6    $key = array_search( 'clear', $actions );
 7    if ( $key !== false ) {
 8        unset( $actions[ $key ] );
 9    }
10
11    return $actions;
12} );

Default: ['clear', 'clear_current', 'clear_targets']

millicache_rest_settings_allowed_actions

Filter allowed settings actions via REST API.

 1add_filter( 'millicache_rest_settings_allowed_actions', function( $actions ) {
 2    // Remove restore action
 3    $key = array_search( 'restore', $actions );
 4    if ( $key !== false ) {
 5        unset( $actions[ $key ] );
 6    }
 7
 8    return $actions;
 9} );

Default: ['reset', 'restore']

millicache_rest_status_response

Filter the REST API status response.

 1add_filter( 'millicache_rest_status_response', function( $status ) {
 2    // Add custom data
 3    $status['custom_metric'] = get_custom_cache_metric();
 4
 5    // Remove sensitive data
 6    unset( $status['storage']['password'] );
 7
 8    return $status;
 9} );

Update Filters#Copied!

millicache_updates

Filter whether MilliCache checks MilliPress.com for plugin updates. Return false to disable update checks entirely, which stops the remote request and hides the update notice on the Plugins screen.

The filter is evaluated at update-check time (when WordPress refreshes the update_plugins transient), so it can be added from a theme's functions.php or another plugin and still be honored.

 1add_filter( 'millicache_updates', '__return_false' );

Default: true


Hook Usage Examples#Copied!

Notify CDN on Cache Clear#Copied!

 1add_action( 'millicache_cache_cleared_by_flags', function( $flags, $expire ) {
 2    // Convert MilliCache flags to CDN tags
 3    $cdn_tags = array_map( function( $flag ) {
 4        return 'millicache-' . sanitize_title( $flag );
 5    }, $flags );
 6
 7    // Purge CDN
 8    cdn_purge_by_tags( $cdn_tags );
 9}, 10, 2 );

Audit Logging#Copied!

 1add_action( 'millicache_cache_cleared', function( $expire ) {
 2    $user = wp_get_current_user();
 3    $method = $expire ? 'expired' : 'deleted';
 4
 5    log_audit_event( 'cache_cleared', [
 6        'user_id'   => $user->ID,
 7        'user_name' => $user->user_login,
 8        'method'    => $method,
 9        'timestamp' => current_time( 'mysql' ),
10    ] );
11} );

Custom Flags for ACF#Copied!

 1add_filter( 'millicache_flags_for_request', function( $flags ) {
 2    if ( ! function_exists( 'get_field' ) ) {
 3        return $flags;
 4    }
 5
 6    // Add a flag for pages with a specific ACF field
 7    if ( is_singular() && get_field( 'enable_dynamic_content' ) ) {
 8        $flags[] = 'acf:dynamic';
 9    }
10
11    return $flags;
12} );
13
14// Clear when the ACF field changes
15add_action( 'acf/save_post', function( $post_id ) {
16    if ( get_field( 'enable_dynamic_content', $post_id ) ) {
17        millicache_clear_cache_by_flags( [ 'acf:dynamic' ] );
18    }
19} );

Restrict Cache Clearing by Role#Copied!

 1add_filter( 'millicache_clear_cache_capability', function( $capability ) {
 2    // Only allow on staging/development
 3    if ( defined( 'WP_ENV' ) && WP_ENV === 'production' ) {
 4        return 'manage_options';  // Admins only in production
 5    }
 6
 7    return 'edit_posts';  // Editors can clear on staging
 8} );

Next Steps#Copied!