Make WordPress Core


Ignore:
Timestamp:
08/26/2026 10:20:49 PM (34 hours ago)
Author:
westonruter
Message:

Cache API: Treat an unusable cached value as a miss.

WP_Site::get_instance() treated only false as a cache miss, but wp_cache_get() can return whatever a persistent object cache holds under that key. A cached string or array therefore skipped the refetch and reached the constructor, where get_object_vars() raised a TypeError. The same lookup is repeated in WP_Network::get_instance(), WP_Post::get_instance(), WP_Comment::get_instance() and WP_Term::get_instance(); networks and terms carried the identical fatal.

All five now refetch unless the cached value is an object carrying the property that identifies it. That also closes a quieter bug in the same code: an object missing that property was accepted as a cache hit, so posts came back with an ID of 0, comments with a null comment_ID, and terms as an entirely different term. Sites and networks keep their -1 sentinel recording a lookup that previously found nothing.

The refetched row is now stored with wp_cache_set() rather than wp_cache_add(). Widening the guard makes the branch reachable while an entry is still present, and add() does not overwrite, so the unusable value survived and every later lookup for that object queried the database again.

Developed in https://github.com/WordPress/wordpress-develop/pull/13270.
Follow-up to r33891, r35537, r45910, r62648.

Props josephscott, westonruter.
Fixes #65962.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-term.php

    r61789 r63354  
    104104         *
    105105         * @since 4.4.0
     106         * @since 7.2.0 Cache values that are not usable as a term object are now treated as a cache miss and replaced.
    106107         *
    107108         * @global wpdb $wpdb WordPress database abstraction object.
     
    124125                $_term = wp_cache_get( $term_id, 'terms' );
    125126
    126                 // If there isn't a cached version, hit the database.
    127                 if ( ! $_term || ( $taxonomy && $taxonomy !== $_term->taxonomy ) ) {
     127                /*
     128                 * If there isn't a usable cached version, hit the database. A cached value that is
     129                 * not a term object, or that belongs to another taxonomy, is treated as a cache miss.
     130                 */
     131                if (
     132                        ! is_object( $_term )
     133                        || ! isset( $_term->term_id, $_term->taxonomy )
     134                        || ( $taxonomy && $taxonomy !== $_term->taxonomy )
     135                ) {
    128136                        // Any term found in the cache is not a match, so don't use it.
    129137                        $_term = false;
     
    178186                        // Don't cache terms that are shared between taxonomies.
    179187                        if ( 1 === count( $terms ) ) {
    180                                 wp_cache_add( $term_id, $_term, 'terms' );
     188                                // Not wp_cache_add(), since an unusable cached value may still be present and must be replaced.
     189                                wp_cache_set( $term_id, $_term, 'terms' );
    181190                        }
    182191                }
Note: See TracChangeset for help on using the changeset viewer.