Make WordPress Core


Ignore:
Timestamp:
08/26/2026 10:20:49 PM (3 days 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-site.php

    r62640 r63354  
    160160         *
    161161         * @since 4.5.0
     162         * @since 7.2.0 Cache values that are neither a site object nor the -1 miss sentinel are now treated as a cache miss and replaced.
    162163         *
    163164         * @global wpdb $wpdb WordPress database abstraction object.
     
    176177                $_site = wp_cache_get( $site_id, 'sites' );
    177178
    178                 if ( false === $_site ) {
     179                // A cached -1 records a previous lookup that found nothing. Any other non-numeric value that is not a site object is treated as a cache miss.
     180                if (
     181                        ( ! is_object( $_site ) || ! isset( $_site->blog_id ) )
     182                        &&
     183                        ! is_numeric( $_site )
     184                ) {
    179185                        $_site = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->blogs} WHERE blog_id = %d LIMIT 1", $site_id ) );
    180186
     
    183189                        }
    184190
    185                         wp_cache_add( $site_id, $_site, 'sites' );
     191                        // Not wp_cache_add(), since an unusable cached value may still be present and must be replaced.
     192                        wp_cache_set( $site_id, $_site, 'sites' );
    186193                }
    187194
Note: See TracChangeset for help on using the changeset viewer.