wp/wp-includes/class-wp-object-cache.php
changeset 16 a86126ab1dd4
child 18 be944660c56a
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
       
     1 <?php
       
     2 /**
       
     3  * Object Cache API: WP_Object_Cache class
       
     4  *
       
     5  * @package WordPress
       
     6  * @subpackage Cache
       
     7  * @since 5.4.0
       
     8  */
       
     9 
       
    10 /**
       
    11  * Core class that implements an object cache.
       
    12  *
       
    13  * The WordPress Object Cache is used to save on trips to the database. The
       
    14  * Object Cache stores all of the cache data to memory and makes the cache
       
    15  * contents available by using a key, which is used to name and later retrieve
       
    16  * the cache contents.
       
    17  *
       
    18  * The Object Cache can be replaced by other caching mechanisms by placing files
       
    19  * in the wp-content folder which is looked at in wp-settings. If that file
       
    20  * exists, then this file will not be included.
       
    21  *
       
    22  * @since 2.0.0
       
    23  */
       
    24 class WP_Object_Cache {
       
    25 
       
    26 	/**
       
    27 	 * Holds the cached objects.
       
    28 	 *
       
    29 	 * @since 2.0.0
       
    30 	 * @var array
       
    31 	 */
       
    32 	private $cache = array();
       
    33 
       
    34 	/**
       
    35 	 * The amount of times the cache data was already stored in the cache.
       
    36 	 *
       
    37 	 * @since 2.5.0
       
    38 	 * @var int
       
    39 	 */
       
    40 	public $cache_hits = 0;
       
    41 
       
    42 	/**
       
    43 	 * Amount of times the cache did not have the request in cache.
       
    44 	 *
       
    45 	 * @since 2.0.0
       
    46 	 * @var int
       
    47 	 */
       
    48 	public $cache_misses = 0;
       
    49 
       
    50 	/**
       
    51 	 * List of global cache groups.
       
    52 	 *
       
    53 	 * @since 3.0.0
       
    54 	 * @var array
       
    55 	 */
       
    56 	protected $global_groups = array();
       
    57 
       
    58 	/**
       
    59 	 * The blog prefix to prepend to keys in non-global groups.
       
    60 	 *
       
    61 	 * @since 3.5.0
       
    62 	 * @var string
       
    63 	 */
       
    64 	private $blog_prefix;
       
    65 
       
    66 	/**
       
    67 	 * Holds the value of is_multisite().
       
    68 	 *
       
    69 	 * @since 3.5.0
       
    70 	 * @var bool
       
    71 	 */
       
    72 	private $multisite;
       
    73 
       
    74 	/**
       
    75 	 * Sets up object properties; PHP 5 style constructor.
       
    76 	 *
       
    77 	 * @since 2.0.8
       
    78 	 */
       
    79 	public function __construct() {
       
    80 		$this->multisite   = is_multisite();
       
    81 		$this->blog_prefix = $this->multisite ? get_current_blog_id() . ':' : '';
       
    82 	}
       
    83 
       
    84 	/**
       
    85 	 * Makes private properties readable for backward compatibility.
       
    86 	 *
       
    87 	 * @since 4.0.0
       
    88 	 *
       
    89 	 * @param string $name Property to get.
       
    90 	 * @return mixed Property.
       
    91 	 */
       
    92 	public function __get( $name ) {
       
    93 		return $this->$name;
       
    94 	}
       
    95 
       
    96 	/**
       
    97 	 * Makes private properties settable for backward compatibility.
       
    98 	 *
       
    99 	 * @since 4.0.0
       
   100 	 *
       
   101 	 * @param string $name  Property to set.
       
   102 	 * @param mixed  $value Property value.
       
   103 	 * @return mixed Newly-set property.
       
   104 	 */
       
   105 	public function __set( $name, $value ) {
       
   106 		return $this->$name = $value;
       
   107 	}
       
   108 
       
   109 	/**
       
   110 	 * Makes private properties checkable for backward compatibility.
       
   111 	 *
       
   112 	 * @since 4.0.0
       
   113 	 *
       
   114 	 * @param string $name Property to check if set.
       
   115 	 * @return bool Whether the property is set.
       
   116 	 */
       
   117 	public function __isset( $name ) {
       
   118 		return isset( $this->$name );
       
   119 	}
       
   120 
       
   121 	/**
       
   122 	 * Makes private properties un-settable for backward compatibility.
       
   123 	 *
       
   124 	 * @since 4.0.0
       
   125 	 *
       
   126 	 * @param string $name Property to unset.
       
   127 	 */
       
   128 	public function __unset( $name ) {
       
   129 		unset( $this->$name );
       
   130 	}
       
   131 
       
   132 	/**
       
   133 	 * Adds data to the cache if it doesn't already exist.
       
   134 	 *
       
   135 	 * @since 2.0.0
       
   136 	 *
       
   137 	 * @uses WP_Object_Cache::_exists() Checks to see if the cache already has data.
       
   138 	 * @uses WP_Object_Cache::set()     Sets the data after the checking the cache
       
   139 	 *                                  contents existence.
       
   140 	 *
       
   141 	 * @param int|string $key    What to call the contents in the cache.
       
   142 	 * @param mixed      $data   The contents to store in the cache.
       
   143 	 * @param string     $group  Optional. Where to group the cache contents. Default 'default'.
       
   144 	 * @param int        $expire Optional. When to expire the cache contents. Default 0 (no expiration).
       
   145 	 * @return bool True on success, false if cache key and group already exist.
       
   146 	 */
       
   147 	public function add( $key, $data, $group = 'default', $expire = 0 ) {
       
   148 		if ( wp_suspend_cache_addition() ) {
       
   149 			return false;
       
   150 		}
       
   151 
       
   152 		if ( empty( $group ) ) {
       
   153 			$group = 'default';
       
   154 		}
       
   155 
       
   156 		$id = $key;
       
   157 		if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) {
       
   158 			$id = $this->blog_prefix . $key;
       
   159 		}
       
   160 
       
   161 		if ( $this->_exists( $id, $group ) ) {
       
   162 			return false;
       
   163 		}
       
   164 
       
   165 		return $this->set( $key, $data, $group, (int) $expire );
       
   166 	}
       
   167 
       
   168 	/**
       
   169 	 * Sets the list of global cache groups.
       
   170 	 *
       
   171 	 * @since 3.0.0
       
   172 	 *
       
   173 	 * @param array $groups List of groups that are global.
       
   174 	 */
       
   175 	public function add_global_groups( $groups ) {
       
   176 		$groups = (array) $groups;
       
   177 
       
   178 		$groups              = array_fill_keys( $groups, true );
       
   179 		$this->global_groups = array_merge( $this->global_groups, $groups );
       
   180 	}
       
   181 
       
   182 	/**
       
   183 	 * Decrements numeric cache item's value.
       
   184 	 *
       
   185 	 * @since 3.3.0
       
   186 	 *
       
   187 	 * @param int|string $key    The cache key to decrement.
       
   188 	 * @param int        $offset Optional. The amount by which to decrement the item's value. Default 1.
       
   189 	 * @param string     $group  Optional. The group the key is in. Default 'default'.
       
   190 	 * @return int|false The item's new value on success, false on failure.
       
   191 	 */
       
   192 	public function decr( $key, $offset = 1, $group = 'default' ) {
       
   193 		if ( empty( $group ) ) {
       
   194 			$group = 'default';
       
   195 		}
       
   196 
       
   197 		if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) {
       
   198 			$key = $this->blog_prefix . $key;
       
   199 		}
       
   200 
       
   201 		if ( ! $this->_exists( $key, $group ) ) {
       
   202 			return false;
       
   203 		}
       
   204 
       
   205 		if ( ! is_numeric( $this->cache[ $group ][ $key ] ) ) {
       
   206 			$this->cache[ $group ][ $key ] = 0;
       
   207 		}
       
   208 
       
   209 		$offset = (int) $offset;
       
   210 
       
   211 		$this->cache[ $group ][ $key ] -= $offset;
       
   212 
       
   213 		if ( $this->cache[ $group ][ $key ] < 0 ) {
       
   214 			$this->cache[ $group ][ $key ] = 0;
       
   215 		}
       
   216 
       
   217 		return $this->cache[ $group ][ $key ];
       
   218 	}
       
   219 
       
   220 	/**
       
   221 	 * Removes the contents of the cache key in the group.
       
   222 	 *
       
   223 	 * If the cache key does not exist in the group, then nothing will happen.
       
   224 	 *
       
   225 	 * @since 2.0.0
       
   226 	 *
       
   227 	 * @param int|string $key        What the contents in the cache are called.
       
   228 	 * @param string     $group      Optional. Where the cache contents are grouped. Default 'default'.
       
   229 	 * @param bool       $deprecated Optional. Unused. Default false.
       
   230 	 * @return bool False if the contents weren't deleted and true on success.
       
   231 	 */
       
   232 	public function delete( $key, $group = 'default', $deprecated = false ) {
       
   233 		if ( empty( $group ) ) {
       
   234 			$group = 'default';
       
   235 		}
       
   236 
       
   237 		if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) {
       
   238 			$key = $this->blog_prefix . $key;
       
   239 		}
       
   240 
       
   241 		if ( ! $this->_exists( $key, $group ) ) {
       
   242 			return false;
       
   243 		}
       
   244 
       
   245 		unset( $this->cache[ $group ][ $key ] );
       
   246 		return true;
       
   247 	}
       
   248 
       
   249 	/**
       
   250 	 * Clears the object cache of all data.
       
   251 	 *
       
   252 	 * @since 2.0.0
       
   253 	 *
       
   254 	 * @return true Always returns true.
       
   255 	 */
       
   256 	public function flush() {
       
   257 		$this->cache = array();
       
   258 
       
   259 		return true;
       
   260 	}
       
   261 
       
   262 	/**
       
   263 	 * Retrieves the cache contents, if it exists.
       
   264 	 *
       
   265 	 * The contents will be first attempted to be retrieved by searching by the
       
   266 	 * key in the cache group. If the cache is hit (success) then the contents
       
   267 	 * are returned.
       
   268 	 *
       
   269 	 * On failure, the number of cache misses will be incremented.
       
   270 	 *
       
   271 	 * @since 2.0.0
       
   272 	 *
       
   273 	 * @param int|string $key   The key under which the cache contents are stored.
       
   274 	 * @param string     $group Optional. Where the cache contents are grouped. Default 'default'.
       
   275 	 * @param bool       $force Optional. Unused. Whether to force an update of the local cache
       
   276 	 *                          from the persistent cache. Default false.
       
   277 	 * @param bool       $found Optional. Whether the key was found in the cache (passed by reference).
       
   278 	 *                          Disambiguates a return of false, a storable value. Default null.
       
   279 	 * @return mixed|false The cache contents on success, false on failure to retrieve contents.
       
   280 	 */
       
   281 	public function get( $key, $group = 'default', $force = false, &$found = null ) {
       
   282 		if ( empty( $group ) ) {
       
   283 			$group = 'default';
       
   284 		}
       
   285 
       
   286 		if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) {
       
   287 			$key = $this->blog_prefix . $key;
       
   288 		}
       
   289 
       
   290 		if ( $this->_exists( $key, $group ) ) {
       
   291 			$found             = true;
       
   292 			$this->cache_hits += 1;
       
   293 			if ( is_object( $this->cache[ $group ][ $key ] ) ) {
       
   294 				return clone $this->cache[ $group ][ $key ];
       
   295 			} else {
       
   296 				return $this->cache[ $group ][ $key ];
       
   297 			}
       
   298 		}
       
   299 
       
   300 		$found               = false;
       
   301 		$this->cache_misses += 1;
       
   302 		return false;
       
   303 	}
       
   304 
       
   305 	/**
       
   306 	 * Retrieves multiple values from the cache in one call.
       
   307 	 *
       
   308 	 * @since 5.5.0
       
   309 	 *
       
   310 	 * @param array  $keys  Array of keys under which the cache contents are stored.
       
   311 	 * @param string $group Optional. Where the cache contents are grouped. Default 'default'.
       
   312 	 * @param bool   $force Optional. Whether to force an update of the local cache
       
   313 	 *                      from the persistent cache. Default false.
       
   314 	 * @return array Array of values organized into groups.
       
   315 	 */
       
   316 	public function get_multiple( $keys, $group = 'default', $force = false ) {
       
   317 		$values = array();
       
   318 
       
   319 		foreach ( $keys as $key ) {
       
   320 			$values[ $key ] = $this->get( $key, $group, $force );
       
   321 		}
       
   322 
       
   323 		return $values;
       
   324 	}
       
   325 
       
   326 	/**
       
   327 	 * Increments numeric cache item's value.
       
   328 	 *
       
   329 	 * @since 3.3.0
       
   330 	 *
       
   331 	 * @param int|string $key    The cache key to increment
       
   332 	 * @param int        $offset Optional. The amount by which to increment the item's value. Default 1.
       
   333 	 * @param string     $group  Optional. The group the key is in. Default 'default'.
       
   334 	 * @return int|false The item's new value on success, false on failure.
       
   335 	 */
       
   336 	public function incr( $key, $offset = 1, $group = 'default' ) {
       
   337 		if ( empty( $group ) ) {
       
   338 			$group = 'default';
       
   339 		}
       
   340 
       
   341 		if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) {
       
   342 			$key = $this->blog_prefix . $key;
       
   343 		}
       
   344 
       
   345 		if ( ! $this->_exists( $key, $group ) ) {
       
   346 			return false;
       
   347 		}
       
   348 
       
   349 		if ( ! is_numeric( $this->cache[ $group ][ $key ] ) ) {
       
   350 			$this->cache[ $group ][ $key ] = 0;
       
   351 		}
       
   352 
       
   353 		$offset = (int) $offset;
       
   354 
       
   355 		$this->cache[ $group ][ $key ] += $offset;
       
   356 
       
   357 		if ( $this->cache[ $group ][ $key ] < 0 ) {
       
   358 			$this->cache[ $group ][ $key ] = 0;
       
   359 		}
       
   360 
       
   361 		return $this->cache[ $group ][ $key ];
       
   362 	}
       
   363 
       
   364 	/**
       
   365 	 * Replaces the contents in the cache, if contents already exist.
       
   366 	 *
       
   367 	 * @since 2.0.0
       
   368 	 *
       
   369 	 * @see WP_Object_Cache::set()
       
   370 	 *
       
   371 	 * @param int|string $key    What to call the contents in the cache.
       
   372 	 * @param mixed      $data   The contents to store in the cache.
       
   373 	 * @param string     $group  Optional. Where to group the cache contents. Default 'default'.
       
   374 	 * @param int        $expire Optional. When to expire the cache contents. Default 0 (no expiration).
       
   375 	 * @return bool False if not exists, true if contents were replaced.
       
   376 	 */
       
   377 	public function replace( $key, $data, $group = 'default', $expire = 0 ) {
       
   378 		if ( empty( $group ) ) {
       
   379 			$group = 'default';
       
   380 		}
       
   381 
       
   382 		$id = $key;
       
   383 		if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) {
       
   384 			$id = $this->blog_prefix . $key;
       
   385 		}
       
   386 
       
   387 		if ( ! $this->_exists( $id, $group ) ) {
       
   388 			return false;
       
   389 		}
       
   390 
       
   391 		return $this->set( $key, $data, $group, (int) $expire );
       
   392 	}
       
   393 
       
   394 	/**
       
   395 	 * Resets cache keys.
       
   396 	 *
       
   397 	 * @since 3.0.0
       
   398 	 *
       
   399 	 * @deprecated 3.5.0 Use switch_to_blog()
       
   400 	 * @see switch_to_blog()
       
   401 	 */
       
   402 	public function reset() {
       
   403 		_deprecated_function( __FUNCTION__, '3.5.0', 'switch_to_blog()' );
       
   404 
       
   405 		// Clear out non-global caches since the blog ID has changed.
       
   406 		foreach ( array_keys( $this->cache ) as $group ) {
       
   407 			if ( ! isset( $this->global_groups[ $group ] ) ) {
       
   408 				unset( $this->cache[ $group ] );
       
   409 			}
       
   410 		}
       
   411 	}
       
   412 
       
   413 	/**
       
   414 	 * Sets the data contents into the cache.
       
   415 	 *
       
   416 	 * The cache contents are grouped by the $group parameter followed by the
       
   417 	 * $key. This allows for duplicate IDs in unique groups. Therefore, naming of
       
   418 	 * the group should be used with care and should follow normal function
       
   419 	 * naming guidelines outside of core WordPress usage.
       
   420 	 *
       
   421 	 * The $expire parameter is not used, because the cache will automatically
       
   422 	 * expire for each time a page is accessed and PHP finishes. The method is
       
   423 	 * more for cache plugins which use files.
       
   424 	 *
       
   425 	 * @since 2.0.0
       
   426 	 *
       
   427 	 * @param int|string $key    What to call the contents in the cache.
       
   428 	 * @param mixed      $data   The contents to store in the cache.
       
   429 	 * @param string     $group  Optional. Where to group the cache contents. Default 'default'.
       
   430 	 * @param int        $expire Not Used.
       
   431 	 * @return true Always returns true.
       
   432 	 */
       
   433 	public function set( $key, $data, $group = 'default', $expire = 0 ) {
       
   434 		if ( empty( $group ) ) {
       
   435 			$group = 'default';
       
   436 		}
       
   437 
       
   438 		if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) {
       
   439 			$key = $this->blog_prefix . $key;
       
   440 		}
       
   441 
       
   442 		if ( is_object( $data ) ) {
       
   443 			$data = clone $data;
       
   444 		}
       
   445 
       
   446 		$this->cache[ $group ][ $key ] = $data;
       
   447 		return true;
       
   448 	}
       
   449 
       
   450 	/**
       
   451 	 * Echoes the stats of the caching.
       
   452 	 *
       
   453 	 * Gives the cache hits, and cache misses. Also prints every cached group,
       
   454 	 * key and the data.
       
   455 	 *
       
   456 	 * @since 2.0.0
       
   457 	 */
       
   458 	public function stats() {
       
   459 		echo '<p>';
       
   460 		echo "<strong>Cache Hits:</strong> {$this->cache_hits}<br />";
       
   461 		echo "<strong>Cache Misses:</strong> {$this->cache_misses}<br />";
       
   462 		echo '</p>';
       
   463 		echo '<ul>';
       
   464 		foreach ( $this->cache as $group => $cache ) {
       
   465 			echo '<li><strong>Group:</strong> ' . esc_html( $group ) . ' - ( ' . number_format( strlen( serialize( $cache ) ) / KB_IN_BYTES, 2 ) . 'k )</li>';
       
   466 		}
       
   467 		echo '</ul>';
       
   468 	}
       
   469 
       
   470 	/**
       
   471 	 * Switches the internal blog ID.
       
   472 	 *
       
   473 	 * This changes the blog ID used to create keys in blog specific groups.
       
   474 	 *
       
   475 	 * @since 3.5.0
       
   476 	 *
       
   477 	 * @param int $blog_id Blog ID.
       
   478 	 */
       
   479 	public function switch_to_blog( $blog_id ) {
       
   480 		$blog_id           = (int) $blog_id;
       
   481 		$this->blog_prefix = $this->multisite ? $blog_id . ':' : '';
       
   482 	}
       
   483 
       
   484 	/**
       
   485 	 * Serves as a utility function to determine whether a key exists in the cache.
       
   486 	 *
       
   487 	 * @since 3.4.0
       
   488 	 *
       
   489 	 * @param int|string $key   Cache key to check for existence.
       
   490 	 * @param string     $group Cache group for the key existence check.
       
   491 	 * @return bool Whether the key exists in the cache for the given group.
       
   492 	 */
       
   493 	protected function _exists( $key, $group ) {
       
   494 		return isset( $this->cache[ $group ] ) && ( isset( $this->cache[ $group ][ $key ] ) || array_key_exists( $key, $this->cache[ $group ] ) );
       
   495 	}
       
   496 }