[COT] Reintroduce the orders cache (#35014)
This commit is contained in:
commit
ca49caabcb
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: add
|
||||||
|
|
||||||
|
Add a cache for orders, to use when custom order tables are enabled
|
|
@ -10,9 +10,11 @@
|
||||||
* @package WooCommerce\Classes
|
* @package WooCommerce\Classes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Automattic\WooCommerce\Caches\OrderCache;
|
||||||
use Automattic\WooCommerce\Proxies\LegacyProxy;
|
use Automattic\WooCommerce\Proxies\LegacyProxy;
|
||||||
use Automattic\WooCommerce\Utilities\ArrayUtil;
|
use Automattic\WooCommerce\Utilities\ArrayUtil;
|
||||||
use Automattic\WooCommerce\Utilities\NumberUtil;
|
use Automattic\WooCommerce\Utilities\NumberUtil;
|
||||||
|
use Automattic\WooCommerce\Utilities\OrderUtil;
|
||||||
|
|
||||||
defined( 'ABSPATH' ) || exit;
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
@ -203,6 +205,11 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
||||||
|
|
||||||
$this->save_items();
|
$this->save_items();
|
||||||
|
|
||||||
|
if ( OrderUtil::orders_cache_usage_is_enabled() ) {
|
||||||
|
$order_cache = wc_get_container()->get( OrderCache::class );
|
||||||
|
$order_cache->update_if_cached( $this );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trigger action after saving to the DB.
|
* Trigger action after saving to the DB.
|
||||||
*
|
*
|
||||||
|
|
|
@ -488,6 +488,8 @@ if ( ! class_exists( 'WC_Admin_Settings', false ) ) :
|
||||||
$visibility_class[] = 'show_options_if_checked';
|
$visibility_class[] = 'show_options_if_checked';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$must_disable = ArrayUtil::get_value_or_default( $value, 'disabled', false );
|
||||||
|
|
||||||
if ( ! isset( $value['checkboxgroup'] ) || 'start' === $value['checkboxgroup'] ) {
|
if ( ! isset( $value['checkboxgroup'] ) || 'start' === $value['checkboxgroup'] ) {
|
||||||
$has_tooltip = isset( $value['tooltip'] ) && '' !== $value['tooltip'];
|
$has_tooltip = isset( $value['tooltip'] ) && '' !== $value['tooltip'];
|
||||||
$tooltip_container_class = $has_tooltip ? 'with-tooltip' : '';
|
$tooltip_container_class = $has_tooltip ? 'with-tooltip' : '';
|
||||||
|
@ -515,6 +517,7 @@ if ( ! class_exists( 'WC_Admin_Settings', false ) ) :
|
||||||
?>
|
?>
|
||||||
<label for="<?php echo esc_attr( $value['id'] ); ?>">
|
<label for="<?php echo esc_attr( $value['id'] ); ?>">
|
||||||
<input
|
<input
|
||||||
|
<?php echo $must_disable ? 'disabled' : ''; ?>
|
||||||
name="<?php echo esc_attr( $value['field_name'] ); ?>"
|
name="<?php echo esc_attr( $value['field_name'] ); ?>"
|
||||||
id="<?php echo esc_attr( $value['id'] ); ?>"
|
id="<?php echo esc_attr( $value['id'] ); ?>"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
|
|
|
@ -5,12 +5,15 @@
|
||||||
* @package WooCommerce\Classes
|
* @package WooCommerce\Classes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Automattic\WooCommerce\Caching\CacheNameSpaceTrait;
|
||||||
|
|
||||||
defined( 'ABSPATH' ) || exit;
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WC_Cache_Helper.
|
* WC_Cache_Helper.
|
||||||
*/
|
*/
|
||||||
class WC_Cache_Helper {
|
class WC_Cache_Helper {
|
||||||
|
use CacheNameSpaceTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transients to delete on shutdown.
|
* Transients to delete on shutdown.
|
||||||
|
@ -114,44 +117,6 @@ class WC_Cache_Helper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get prefix for use with wp_cache_set. Allows all cache in a group to be invalidated at once.
|
|
||||||
*
|
|
||||||
* @param string $group Group of cache to get.
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function get_cache_prefix( $group ) {
|
|
||||||
// Get cache key - uses cache key wc_orders_cache_prefix to invalidate when needed.
|
|
||||||
$prefix = wp_cache_get( 'wc_' . $group . '_cache_prefix', $group );
|
|
||||||
|
|
||||||
if ( false === $prefix ) {
|
|
||||||
$prefix = microtime();
|
|
||||||
wp_cache_set( 'wc_' . $group . '_cache_prefix', $prefix, $group );
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'wc_cache_' . $prefix . '_';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increment group cache prefix (invalidates cache).
|
|
||||||
*
|
|
||||||
* @param string $group Group of cache to clear.
|
|
||||||
*/
|
|
||||||
public static function incr_cache_prefix( $group ) {
|
|
||||||
wc_deprecated_function( 'WC_Cache_Helper::incr_cache_prefix', '3.9.0', 'WC_Cache_Helper::invalidate_cache_group' );
|
|
||||||
self::invalidate_cache_group( $group );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invalidate cache group.
|
|
||||||
*
|
|
||||||
* @param string $group Group of cache to clear.
|
|
||||||
* @since 3.9.0
|
|
||||||
*/
|
|
||||||
public static function invalidate_cache_group( $group ) {
|
|
||||||
wp_cache_set( 'wc_' . $group . '_cache_prefix', microtime(), $group );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a hash of the customer location.
|
* Get a hash of the customer location.
|
||||||
*
|
*
|
||||||
|
|
|
@ -8,10 +8,11 @@
|
||||||
* @package WooCommerce\Classes
|
* @package WooCommerce\Classes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defined( 'ABSPATH' ) || exit;
|
use Automattic\WooCommerce\Caches\OrderCache;
|
||||||
|
|
||||||
use Automattic\WooCommerce\Utilities\OrderUtil;
|
use Automattic\WooCommerce\Utilities\OrderUtil;
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Order factory class
|
* Order factory class
|
||||||
*/
|
*/
|
||||||
|
@ -30,13 +31,26 @@ class WC_Order_Factory {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$use_orders_cache = OrderUtil::orders_cache_usage_is_enabled();
|
||||||
|
if ( $use_orders_cache ) {
|
||||||
|
$order_cache = wc_get_container()->get( OrderCache::class );
|
||||||
|
$order = $order_cache->get( $order_id );
|
||||||
|
if ( ! is_null( $order ) ) {
|
||||||
|
return $order;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$classname = self::get_class_name_for_order_id( $order_id );
|
$classname = self::get_class_name_for_order_id( $order_id );
|
||||||
if ( ! $classname ) {
|
if ( ! $classname ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return new $classname( $order_id );
|
$order = new $classname( $order_id );
|
||||||
|
if ( $use_orders_cache && $order instanceof \WC_Abstract_Legacy_Order ) {
|
||||||
|
$order_cache->set( $order, $order_id );
|
||||||
|
}
|
||||||
|
return $order;
|
||||||
} catch ( Exception $e ) {
|
} catch ( Exception $e ) {
|
||||||
wc_caught_exception( $e, __FUNCTION__, array( $order_id ) );
|
wc_caught_exception( $e, __FUNCTION__, array( $order_id ) );
|
||||||
return false;
|
return false;
|
||||||
|
@ -56,6 +70,22 @@ class WC_Order_Factory {
|
||||||
$result = array();
|
$result = array();
|
||||||
$order_ids = array_filter( array_map( array( __CLASS__, 'get_order_id' ), $order_ids ) );
|
$order_ids = array_filter( array_map( array( __CLASS__, 'get_order_id' ), $order_ids ) );
|
||||||
|
|
||||||
|
$already_cached_orders = array();
|
||||||
|
$use_orders_cache = OrderUtil::orders_cache_usage_is_enabled();
|
||||||
|
if ( $use_orders_cache ) {
|
||||||
|
$uncached_order_ids = array();
|
||||||
|
$order_cache = wc_get_container()->get( OrderCache::class );
|
||||||
|
foreach ( $order_ids as $order_id ) {
|
||||||
|
$cached_order = $order_cache->get( absint( $order_id ) );
|
||||||
|
if ( is_null( $cached_order ) ) {
|
||||||
|
$uncached_order_ids[] = $order_id;
|
||||||
|
} else {
|
||||||
|
$already_cached_orders[] = $cached_order;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$order_ids = $uncached_order_ids;
|
||||||
|
}
|
||||||
|
|
||||||
// We separate order list by class, since their datastore might be different.
|
// We separate order list by class, since their datastore might be different.
|
||||||
$order_list_by_class = array();
|
$order_list_by_class = array();
|
||||||
$order_id_classnames = self::get_class_names_for_order_ids( $order_ids );
|
$order_id_classnames = self::get_class_names_for_order_ids( $order_ids );
|
||||||
|
@ -99,9 +129,16 @@ class WC_Order_Factory {
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore the sort order.
|
// restore the sort order.
|
||||||
$result = array_replace( array_flip( $order_ids ), $result );
|
$result = array_values( array_replace( array_flip( $order_ids ), $result ) );
|
||||||
|
|
||||||
return array_values( $result );
|
if ( $use_orders_cache ) {
|
||||||
|
foreach ( $result as $order ) {
|
||||||
|
$order_cache->set( $order );
|
||||||
|
}
|
||||||
|
return array_merge( $already_cached_orders, $result );
|
||||||
|
} else {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Automattic\WooCommerce\Caches;
|
||||||
|
|
||||||
|
use Automattic\WooCommerce\Caching\ObjectCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class to cache order objects.
|
||||||
|
*/
|
||||||
|
class OrderCache extends ObjectCache {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the identifier for the type of the cached objects.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get_object_type(): string {
|
||||||
|
return 'orders';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the id of an object to be cached.
|
||||||
|
*
|
||||||
|
* @param array|object $object The object to be cached.
|
||||||
|
* @return int|string|null The id of the object, or null if it can't be determined.
|
||||||
|
*/
|
||||||
|
protected function get_object_id( $object ) {
|
||||||
|
return $object->get_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate an object before caching it.
|
||||||
|
*
|
||||||
|
* @param array|object $object The object to validate.
|
||||||
|
* @return string[]|null An array of error messages, or null if the object is valid.
|
||||||
|
*/
|
||||||
|
protected function validate( $object ): ?array {
|
||||||
|
if ( ! $object instanceof \WC_Abstract_Order ) {
|
||||||
|
return array( 'The supplied order is not an instance of WC_Abstract_Order, ' . gettype( $object ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Automattic\WooCommerce\Caches;
|
||||||
|
|
||||||
|
use Automattic\WooCommerce\Internal\Features\FeaturesController;
|
||||||
|
use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods;
|
||||||
|
use Automattic\WooCommerce\Utilities\OrderUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class to control the usage of the orders cache.
|
||||||
|
*/
|
||||||
|
class OrderCacheController {
|
||||||
|
|
||||||
|
use AccessiblePrivateMethods;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The orders cache to use.
|
||||||
|
*
|
||||||
|
* @var OrderCache
|
||||||
|
*/
|
||||||
|
private $order_cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The orders cache to use.
|
||||||
|
*
|
||||||
|
* @var FeaturesController
|
||||||
|
*/
|
||||||
|
private $features_controller;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The backup value of the cache usage enable status, stored while the cache is temporarily disabled.
|
||||||
|
*
|
||||||
|
* @var null|bool
|
||||||
|
*/
|
||||||
|
private $orders_cache_usage_backup = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class initialization, invoked by the DI container.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @param OrderCache $order_cache The order cache engine to use.
|
||||||
|
*/
|
||||||
|
final public function init( OrderCache $order_cache ) {
|
||||||
|
$this->order_cache = $order_cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether order cache usage is enabled. Currently, linked to custom orders' table usage.
|
||||||
|
*
|
||||||
|
* @return bool True if the order cache is enabled.
|
||||||
|
*/
|
||||||
|
public function orders_cache_usage_is_enabled(): bool {
|
||||||
|
return OrderUtil::custom_orders_table_usage_is_enabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Temporarily disable the order cache if it's enabled.
|
||||||
|
*
|
||||||
|
* This is a purely in-memory operation: a variable is created with the value
|
||||||
|
* of the current enable status for the feature, and this variable
|
||||||
|
* is checked by orders_cache_usage_is_enabled. In the next request the
|
||||||
|
* feature will be again enabled or not depending on how the feature is set.
|
||||||
|
*/
|
||||||
|
public function temporarily_disable_orders_cache_usage(): void {
|
||||||
|
if ( $this->orders_cache_usage_is_temporarly_disabled() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->orders_cache_usage_backup = $this->orders_cache_usage_is_enabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the order cache has been temporarily disabled.
|
||||||
|
*
|
||||||
|
* @return bool True if the order cache is currently temporarily disabled.
|
||||||
|
*/
|
||||||
|
public function orders_cache_usage_is_temporarly_disabled(): bool {
|
||||||
|
return null !== $this->orders_cache_usage_backup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the order cache usage that had been temporarily disabled.
|
||||||
|
*/
|
||||||
|
public function maybe_restore_orders_cache_usage(): void {
|
||||||
|
$this->orders_cache_usage_backup = null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,9 +12,11 @@ interface CacheEngine {
|
||||||
* Retrieves an object cached under a given key.
|
* Retrieves an object cached under a given key.
|
||||||
*
|
*
|
||||||
* @param string $key They key under which the object to retrieve is cached.
|
* @param string $key They key under which the object to retrieve is cached.
|
||||||
|
* @param string $group The group under which the object is cached.
|
||||||
|
*
|
||||||
* @return array|object|null The cached object, or null if there's no object cached under the passed key.
|
* @return array|object|null The cached object, or null if there's no object cached under the passed key.
|
||||||
*/
|
*/
|
||||||
public function get_cached_object( string $key);
|
public function get_cached_object( string $key, string $group = '' );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Caches an object under a given key, and with a given expiration.
|
* Caches an object under a given key, and with a given expiration.
|
||||||
|
@ -22,23 +24,38 @@ interface CacheEngine {
|
||||||
* @param string $key The key under which the object will be cached.
|
* @param string $key The key under which the object will be cached.
|
||||||
* @param array|object $object The object to cache.
|
* @param array|object $object The object to cache.
|
||||||
* @param int $expiration Expiration for the cached object, in seconds.
|
* @param int $expiration Expiration for the cached object, in seconds.
|
||||||
|
* @param string $group The group under which the object will be cached.
|
||||||
|
*
|
||||||
* @return bool True if the object is cached successfully, false otherwise.
|
* @return bool True if the object is cached successfully, false otherwise.
|
||||||
*/
|
*/
|
||||||
public function cache_object( string $key, $object, int $expiration): bool;
|
public function cache_object( string $key, $object, int $expiration, string $group = '' ): bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a cached object from the cache.
|
* Removes a cached object from the cache.
|
||||||
*
|
*
|
||||||
* @param string $key They key under which the object is cached.
|
* @param string $key They key under which the object is cached.
|
||||||
|
* @param string $group The group under which the object is cached.
|
||||||
|
*
|
||||||
* @return bool True if the object is removed from the cache successfully, false otherwise (because the object wasn't cached or for other reason).
|
* @return bool True if the object is removed from the cache successfully, false otherwise (because the object wasn't cached or for other reason).
|
||||||
*/
|
*/
|
||||||
public function delete_cached_object( string $key): bool;
|
public function delete_cached_object( string $key, string $group = '' ): bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if an object is cached under a given key.
|
* Checks if an object is cached under a given key.
|
||||||
*
|
*
|
||||||
* @param string $key The key to verify.
|
* @param string $key The key to verify.
|
||||||
|
* @param string $group The group under which the object is cached.
|
||||||
|
*
|
||||||
* @return bool True if there's an object cached under the given key, false otherwise.
|
* @return bool True if there's an object cached under the given key, false otherwise.
|
||||||
*/
|
*/
|
||||||
public function is_cached( string $key): bool;
|
public function is_cached( string $key, string $group = '' ): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all cached objects under a given group.
|
||||||
|
*
|
||||||
|
* @param string $group The group to delete.
|
||||||
|
*
|
||||||
|
* @return bool True if the group is deleted successfully, false otherwise.
|
||||||
|
*/
|
||||||
|
public function delete_cache_group( string $group = '' ): bool;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Automattic\WooCommerce\Caching;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements namespacing algorithm to simulate grouping and namespacing for wp_cache, memcache and other caching engines that don't support grouping natively.
|
||||||
|
*
|
||||||
|
* See the algorithm details here: https://github.com/memcached/memcached/wiki/ProgrammingTricks#namespacing.
|
||||||
|
*
|
||||||
|
* To use the namespacing algorithm in the CacheEngine class:
|
||||||
|
* 1. Use a group string to identify all objects of a type.
|
||||||
|
* 2. Before setting cache, prefix the cache key by using the `get_cache_prefix`.
|
||||||
|
* 3. Use `invalidate_cache_group` function to invalidate all caches in entire group at once.
|
||||||
|
*/
|
||||||
|
trait CacheNameSpaceTrait {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get prefix for use with wp_cache_set. Allows all cache in a group to be invalidated at once.
|
||||||
|
*
|
||||||
|
* @param string $group Group of cache to get.
|
||||||
|
* @return string Prefix.
|
||||||
|
*/
|
||||||
|
public static function get_cache_prefix( $group ) {
|
||||||
|
// Get cache key - uses cache key wc_orders_cache_prefix to invalidate when needed.
|
||||||
|
$prefix = wp_cache_get( 'wc_' . $group . '_cache_prefix', $group );
|
||||||
|
|
||||||
|
if ( false === $prefix ) {
|
||||||
|
$prefix = microtime();
|
||||||
|
wp_cache_set( 'wc_' . $group . '_cache_prefix', $prefix, $group );
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'wc_cache_' . $prefix . '_';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment group cache prefix (invalidates cache).
|
||||||
|
*
|
||||||
|
* @param string $group Group of cache to clear.
|
||||||
|
*/
|
||||||
|
public static function incr_cache_prefix( $group ) {
|
||||||
|
wc_deprecated_function( 'WC_Cache_Helper::incr_cache_prefix', '3.9.0', 'WC_Cache_Helper::invalidate_cache_group' );
|
||||||
|
self::invalidate_cache_group( $group );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate cache group.
|
||||||
|
*
|
||||||
|
* @param string $group Group of cache to clear.
|
||||||
|
* @since 3.9.0
|
||||||
|
*/
|
||||||
|
public static function invalidate_cache_group( $group ) {
|
||||||
|
return wp_cache_set( 'wc_' . $group . '_cache_prefix', microtime(), $group );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to get prefixed key.
|
||||||
|
*
|
||||||
|
* @param string $key Key to prefix.
|
||||||
|
* @param string $group Group of cache to get.
|
||||||
|
*
|
||||||
|
* @return string Prefixed key.
|
||||||
|
*/
|
||||||
|
public static function get_prefixed_key( $key, $group ) {
|
||||||
|
return self::get_cache_prefix( $group ) . $key;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,8 +17,6 @@ namespace Automattic\WooCommerce\Caching;
|
||||||
*/
|
*/
|
||||||
abstract class ObjectCache {
|
abstract class ObjectCache {
|
||||||
|
|
||||||
private const CACHE_PREFIX_OPTION_NAME = 'wp_object_cache_key_prefix_';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expiration value to be passed to 'set' to use the value of $default_expiration.
|
* Expiration value to be passed to 'set' to use the value of $default_expiration.
|
||||||
*/
|
*/
|
||||||
|
@ -54,24 +52,10 @@ abstract class ObjectCache {
|
||||||
/**
|
/**
|
||||||
* The cache engine to use.
|
* The cache engine to use.
|
||||||
*
|
*
|
||||||
* @var CacheEngine
|
* @var ?CacheEngine
|
||||||
*/
|
*/
|
||||||
private $cache_engine = null;
|
private $cache_engine = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* The prefix to use for cache keys to pass to the cache engine.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $cache_key_prefix = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the option used to store the cache prefix.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $cache_key_prefix_option_name;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets an identifier for the types of objects cached by this class.
|
* Gets an identifier for the types of objects cached by this class.
|
||||||
* This identifier will be used to compose the keys passed to the cache engine,
|
* This identifier will be used to compose the keys passed to the cache engine,
|
||||||
|
@ -92,8 +76,6 @@ abstract class ObjectCache {
|
||||||
if ( empty( $this->object_type ) ) {
|
if ( empty( $this->object_type ) ) {
|
||||||
throw new CacheException( 'Class ' . get_class( $this ) . ' returns an empty value for get_object_type', $this );
|
throw new CacheException( 'Class ' . get_class( $this ) . ' returns an empty value for get_object_type', $this );
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->cache_key_prefix_option_name = self::CACHE_PREFIX_OPTION_NAME . $this->object_type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -117,7 +99,7 @@ abstract class ObjectCache {
|
||||||
/**
|
/**
|
||||||
* Filters the underlying cache engine to be used by an instance of ObjectCache.
|
* Filters the underlying cache engine to be used by an instance of ObjectCache.
|
||||||
*
|
*
|
||||||
* @since 6.8.0
|
* @since 7.4.0
|
||||||
*
|
*
|
||||||
* @param CacheEngine $engine The cache engine to be used by default.
|
* @param CacheEngine $engine The cache engine to be used by default.
|
||||||
* @param ObjectCache $cache_instance The instance of ObjectCache that will use the cache engine.
|
* @param ObjectCache $cache_instance The instance of ObjectCache that will use the cache engine.
|
||||||
|
@ -128,49 +110,16 @@ abstract class ObjectCache {
|
||||||
return $this->cache_engine;
|
return $this->cache_engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current cache prefix to use, generating one if none is in use yet.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function get_cache_key_prefix(): string {
|
|
||||||
$value = $this->cache_key_prefix;
|
|
||||||
if ( ! $value ) {
|
|
||||||
$value = get_option( $this->cache_key_prefix_option_name );
|
|
||||||
if ( ! $value ) {
|
|
||||||
$value = $this->create_cache_key_prefix();
|
|
||||||
}
|
|
||||||
$this->cache_key_prefix = $value;
|
|
||||||
}
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a prefix for the cache keys to use, containing the object type and a random string,
|
|
||||||
* and store it persistently using an option.
|
|
||||||
*
|
|
||||||
* @return string The generated prefix.
|
|
||||||
* @throws CacheException Can't store the generated prefix.
|
|
||||||
*/
|
|
||||||
private function create_cache_key_prefix(): string {
|
|
||||||
$prefix_variable_part = $this->get_random_string();
|
|
||||||
$prefix = "woocommerce_object_cache|{$this->object_type}|{$prefix_variable_part}|";
|
|
||||||
if ( ! update_option( $this->cache_key_prefix_option_name, $prefix ) ) {
|
|
||||||
throw new CacheException( "Can't store the key prefix option", $this );
|
|
||||||
}
|
|
||||||
return $prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an object to the cache, or update an already cached object.
|
* Add an object to the cache, or update an already cached object.
|
||||||
*
|
*
|
||||||
* @param int|string|null $id Id of the object to be cached, if null, get_object_id will be used to get it.
|
|
||||||
* @param object|array $object The object to be cached.
|
* @param object|array $object The object to be cached.
|
||||||
|
* @param int|string|null $id Id of the object to be cached, if null, get_object_id will be used to get it.
|
||||||
* @param int $expiration Expiration of the cached data in seconds from the current time, or DEFAULT_EXPIRATION to use the default value.
|
* @param int $expiration Expiration of the cached data in seconds from the current time, or DEFAULT_EXPIRATION to use the default value.
|
||||||
* @return bool True on success, false on error.
|
* @return bool True on success, false on error.
|
||||||
* @throws CacheException Invalid parameter, or null id was passed and get_object_id returns null too.
|
* @throws CacheException Invalid parameter, or null id was passed and get_object_id returns null too.
|
||||||
*/
|
*/
|
||||||
public function set( $id = null, $object, int $expiration = self::DEFAULT_EXPIRATION ): bool {
|
public function set( $object, $id = null, int $expiration = self::DEFAULT_EXPIRATION ): bool {
|
||||||
if ( null === $object ) {
|
if ( null === $object ) {
|
||||||
throw new CacheException( "Can't cache a null value", $this, $id );
|
throw new CacheException( "Can't cache a null value", $this, $id );
|
||||||
}
|
}
|
||||||
|
@ -185,6 +134,62 @@ abstract class ObjectCache {
|
||||||
|
|
||||||
$this->verify_expiration_value( $expiration );
|
$this->verify_expiration_value( $expiration );
|
||||||
|
|
||||||
|
$errors = $this->validate( $object );
|
||||||
|
if ( ! is_null( $errors ) ) {
|
||||||
|
try {
|
||||||
|
$id = $this->get_id_from_object_if_null( $object, $id );
|
||||||
|
} catch ( \Throwable $ex ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
|
||||||
|
// Nothing else to do, we won't be able to add any significant object id to the CacheException and that's it.
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( count( $errors ) === 1 ) {
|
||||||
|
throw new CacheException( 'Object validation/serialization failed: ' . $errors[0], $this, $id, $errors );
|
||||||
|
} elseif ( ! empty( $errors ) ) {
|
||||||
|
throw new CacheException( 'Object validation/serialization failed', $this, $id, $errors );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $this->get_id_from_object_if_null( $object, $id );
|
||||||
|
|
||||||
|
$this->last_cached_data = $object;
|
||||||
|
return $this->get_cache_engine()->cache_object(
|
||||||
|
$id,
|
||||||
|
$object,
|
||||||
|
self::DEFAULT_EXPIRATION === $expiration ? $this->default_expiration : $expiration,
|
||||||
|
$this->get_object_type()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an object in the cache, but only if an object is already cached with the same id.
|
||||||
|
*
|
||||||
|
* @param object|array $object The new object that will replace the already cached one.
|
||||||
|
* @param int|string|null $id Id of the object to be cached, if null, get_object_id will be used to get it.
|
||||||
|
* @param int $expiration Expiration of the cached data in seconds from the current time, or DEFAULT_EXPIRATION to use the default value.
|
||||||
|
* @return bool True on success, false on error or if no object wiith the supplied id was cached.
|
||||||
|
* @throws CacheException Invalid parameter, or null id was passed and get_object_id returns null too.
|
||||||
|
*/
|
||||||
|
public function update_if_cached( $object, $id = null, int $expiration = self::DEFAULT_EXPIRATION ): bool {
|
||||||
|
$id = $this->get_id_from_object_if_null( $object, $id );
|
||||||
|
|
||||||
|
if ( ! $this->is_cached( $id ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->set( $object, $id, $expiration );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the id from an object if the id itself is null.
|
||||||
|
*
|
||||||
|
* @param object|array $object The object to get the id from.
|
||||||
|
* @param int|string|null $id An object id or null.
|
||||||
|
*
|
||||||
|
* @return int|string|null Passed $id if it wasn't null, otherwise id obtained from $object using get_object_id.
|
||||||
|
*
|
||||||
|
* @throws CacheException Passed $id is null and get_object_id returned null too.
|
||||||
|
*/
|
||||||
|
private function get_id_from_object_if_null( $object, $id ) {
|
||||||
if ( null === $id ) {
|
if ( null === $id ) {
|
||||||
$id = $this->get_object_id( $object );
|
$id = $this->get_object_id( $object );
|
||||||
if ( null === $id ) {
|
if ( null === $id ) {
|
||||||
|
@ -192,28 +197,7 @@ abstract class ObjectCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$errors = $this->validate( $object );
|
return $id;
|
||||||
if ( null !== $errors && 1 === count( $errors ) ) {
|
|
||||||
throw new CacheException( 'Object validation/serialization failed: ' . $errors[0], $this, $id, $errors );
|
|
||||||
} elseif ( ! empty( $errors ) ) {
|
|
||||||
throw new CacheException( 'Object validation/serialization failed', $this, $id, $errors );
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = $this->serialize( $object );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filters the serialized object that will be passed by an instance of ObjectCache to its cache engine to be cached.
|
|
||||||
*
|
|
||||||
* @since 6.8.0
|
|
||||||
*
|
|
||||||
* @param array $data The already serialized object data.
|
|
||||||
* @param array|object $object The object before serialization.
|
|
||||||
* @returns array The actual serialized object data that will be passed to the cache engine.
|
|
||||||
*/
|
|
||||||
$data = apply_filters( "woocommerce_after_serializing_{$this->object_type}_for_caching", $data, $object, $id );
|
|
||||||
|
|
||||||
$this->last_cached_data = $data;
|
|
||||||
return $this->get_cache_engine()->cache_object( $this->get_cache_key_prefix() . $id, $data, self::DEFAULT_EXPIRATION === $expiration ? $this->default_expiration : $expiration );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -249,34 +233,21 @@ abstract class ObjectCache {
|
||||||
|
|
||||||
$this->verify_expiration_value( $expiration );
|
$this->verify_expiration_value( $expiration );
|
||||||
|
|
||||||
$data = $this->get_cache_engine()->get_cached_object( $this->get_cache_key_prefix() . $id );
|
$data = $this->get_cache_engine()->get_cached_object( $id, $this->get_object_type() );
|
||||||
if ( null === $data ) {
|
if ( null === $data ) {
|
||||||
|
$object = null;
|
||||||
if ( $get_from_datastore_callback ) {
|
if ( $get_from_datastore_callback ) {
|
||||||
$object = $get_from_datastore_callback( $id );
|
$object = $get_from_datastore_callback( $id );
|
||||||
} else {
|
|
||||||
$object = $this->get_from_datastore( $id );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( null === $object ) {
|
if ( null === $object ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
$this->set( $object, $id, $expiration );
|
||||||
$this->set( $id, $object, $expiration );
|
|
||||||
$data = $this->last_cached_data;
|
$data = $this->last_cached_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
$object = $this->deserialize( $data );
|
return $data;
|
||||||
|
|
||||||
/**
|
|
||||||
* Filters the deserialized object that is retrieved from the cache engine of an instance of ObjectCache and will be returned by 'get'.
|
|
||||||
*
|
|
||||||
* @since 6.8.0
|
|
||||||
*
|
|
||||||
* @param array|object $object The object after being deserialized.
|
|
||||||
* @param array $data The serialized object data that was retrieved from the cache engine.
|
|
||||||
* @returns array|object The actual deserialized object data that will be returned by 'get'.
|
|
||||||
*/
|
|
||||||
return apply_filters( "woocommerce_after_deserializing_{$this->object_type}_from_cache", $object, $data, $id );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -286,41 +257,16 @@ abstract class ObjectCache {
|
||||||
* @return bool True if the object is removed from the cache successfully, false otherwise (because the object wasn't cached or for other reason).
|
* @return bool True if the object is removed from the cache successfully, false otherwise (because the object wasn't cached or for other reason).
|
||||||
*/
|
*/
|
||||||
public function remove( $id ): bool {
|
public function remove( $id ): bool {
|
||||||
$result = $this->get_cache_engine()->delete_cached_object( $this->get_cache_key_prefix() . $id );
|
return $this->get_cache_engine()->delete_cached_object( $id, $this->get_object_type() );
|
||||||
|
|
||||||
/**
|
|
||||||
* Action triggered by an instance of ObjectCache after an object is (attempted to be) removed from the cache.
|
|
||||||
*
|
|
||||||
* @since 6.8.0
|
|
||||||
*
|
|
||||||
* @param int|string $id The id of the object being removed.
|
|
||||||
* @param bool $result True if the object removal succeeded, false otherwise.
|
|
||||||
*/
|
|
||||||
do_action( "woocommerce_after_removing_{$this->object_type}_from_cache", $id, $result );
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all the objects from the cache.
|
* Remove all the objects from the cache.
|
||||||
* This is done by forcing the generation of a new cache key prefix
|
|
||||||
* and leaving the old cached objects to expire.
|
|
||||||
*
|
*
|
||||||
* @return void
|
* @return bool True on success, false on error.
|
||||||
*/
|
*/
|
||||||
public function flush(): void {
|
public function flush(): bool {
|
||||||
delete_option( $this->cache_key_prefix_option_name );
|
return $this->get_cache_engine()->delete_cache_group( $this->get_object_type() );
|
||||||
$this->cache_key_prefix = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Action triggered by an instance of ObjectCache after it flushes all the cached objects.
|
|
||||||
*
|
|
||||||
* @since 6.8.0
|
|
||||||
*
|
|
||||||
* @param ObjectCache $cache_instance The instance of ObjectCache whose 'flush` method has been called.
|
|
||||||
* @param CacheEngine $engine The cache engine in use.
|
|
||||||
*/
|
|
||||||
do_action( "woocommerce_after_flushing_{$this->object_type}_cache", $this, $this->get_cache_engine() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -330,7 +276,7 @@ abstract class ObjectCache {
|
||||||
* @return bool True if there's a cached object with the specified id.
|
* @return bool True if there's a cached object with the specified id.
|
||||||
*/
|
*/
|
||||||
public function is_cached( $id ): bool {
|
public function is_cached( $id ): bool {
|
||||||
return $this->get_cache_engine()->is_cached( $this->get_cache_key_prefix() . $id );
|
return $this->get_cache_engine()->is_cached( $id, $this->get_object_type() );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -340,9 +286,7 @@ abstract class ObjectCache {
|
||||||
* @param array|object $object The object to get the id for.
|
* @param array|object $object The object to get the id for.
|
||||||
* @return int|string|null
|
* @return int|string|null
|
||||||
*/
|
*/
|
||||||
protected function get_object_id( $object ) {
|
abstract protected function get_object_id( $object );
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate an object before it's cached.
|
* Validate an object before it's cached.
|
||||||
|
@ -350,42 +294,7 @@ abstract class ObjectCache {
|
||||||
* @param array|object $object Object to validate.
|
* @param array|object $object Object to validate.
|
||||||
* @return array|null An array with validation error messages, null or an empty array if there are no errors.
|
* @return array|null An array with validation error messages, null or an empty array if there are no errors.
|
||||||
*/
|
*/
|
||||||
protected function validate( $object ): ?array {
|
abstract protected function validate( $object ): ?array;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert an object to a serialized form suitable for caching.
|
|
||||||
* If a class overrides this method it should override 'deserialize' as well.
|
|
||||||
*
|
|
||||||
* @param array|object $object The object to serialize.
|
|
||||||
* @return array The serialized object.
|
|
||||||
*/
|
|
||||||
protected function serialize( $object ): array {
|
|
||||||
return array( 'data' => $object );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deserializes a set of object data after having been retrieved from the cache.
|
|
||||||
* If a class overrides this method it should override 'serialize' as well.
|
|
||||||
*
|
|
||||||
* @param array $serialized Serialized object data as it was returned by 'validate_and_serialize'.
|
|
||||||
* @return object|array Deserialized object, ready to be returned by 'get'.
|
|
||||||
*/
|
|
||||||
protected function deserialize( array $serialized ) {
|
|
||||||
return $serialized['data'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an object from an authoritative data store.
|
|
||||||
* This is used by 'get' if the object isn't cached and no custom object retrieval callback is suupplied.
|
|
||||||
*
|
|
||||||
* @param int|string $id The id of the object to get.
|
|
||||||
* @return array|object|null The retrieved object, or null if it's not possible to retrieve an object by the given id.
|
|
||||||
*/
|
|
||||||
protected function get_from_datastore( $id ) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the instance of the cache engine to use.
|
* Get the instance of the cache engine to use.
|
||||||
|
@ -393,7 +302,7 @@ abstract class ObjectCache {
|
||||||
* @return CacheEngine
|
* @return CacheEngine
|
||||||
*/
|
*/
|
||||||
protected function get_cache_engine_instance(): CacheEngine {
|
protected function get_cache_engine_instance(): CacheEngine {
|
||||||
return wc_get_container()->get( WpCacheEngine::class );
|
return wc_get_container()->get( WPCacheEngine::class );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Automattic\WooCommerce\Caching;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of CacheEngine that uses the built-in WordPress cache.
|
||||||
|
*/
|
||||||
|
class WPCacheEngine implements CacheEngine {
|
||||||
|
use CacheNameSpaceTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves an object cached under a given key.
|
||||||
|
*
|
||||||
|
* @param string $key They key under which the object to retrieve is cached.
|
||||||
|
* @param string $group The group under which the object is cached.
|
||||||
|
*
|
||||||
|
* @return array|object|null The cached object, or null if there's no object cached under the passed key.
|
||||||
|
*/
|
||||||
|
public function get_cached_object( string $key, string $group = '' ) {
|
||||||
|
$prefixed_key = self::get_prefixed_key( $key, $group );
|
||||||
|
$value = wp_cache_get( $prefixed_key, $group );
|
||||||
|
return false === $value ? null : $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caches an object under a given key, and with a given expiration.
|
||||||
|
*
|
||||||
|
* @param string $key The key under which the object will be cached.
|
||||||
|
* @param array|object $object The object to cache.
|
||||||
|
* @param int $expiration Expiration for the cached object, in seconds.
|
||||||
|
* @param string $group The group under which the object will be cached.
|
||||||
|
*
|
||||||
|
* @return bool True if the object is cached successfully, false otherwise.
|
||||||
|
*/
|
||||||
|
public function cache_object( string $key, $object, int $expiration, string $group = '' ): bool {
|
||||||
|
$prefixed_key = self::get_prefixed_key( $key, $group );
|
||||||
|
return wp_cache_set( $prefixed_key, $object, $group, $expiration );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a cached object from the cache.
|
||||||
|
*
|
||||||
|
* @param string $key They key under which the object is cached.
|
||||||
|
* @param string $group The group under which the object is cached.
|
||||||
|
*
|
||||||
|
* @return bool True if the object is removed from the cache successfully, false otherwise (because the object wasn't cached or for other reason).
|
||||||
|
*/
|
||||||
|
public function delete_cached_object( string $key, string $group = '' ): bool {
|
||||||
|
$prefixed_key = self::get_prefixed_key( $key, $group );
|
||||||
|
return wp_cache_delete( $prefixed_key, $group );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if an object is cached under a given key.
|
||||||
|
*
|
||||||
|
* @param string $key The key to verify.
|
||||||
|
* @param string $group The group under which the object is cached.
|
||||||
|
*
|
||||||
|
* @return bool True if there's an object cached under the given key, false otherwise.
|
||||||
|
*/
|
||||||
|
public function is_cached( string $key, string $group = '' ): bool {
|
||||||
|
$prefixed_key = self::get_prefixed_key( $key, $group );
|
||||||
|
return false !== wp_cache_get( $prefixed_key, $group );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all cached objects under a given group.
|
||||||
|
*
|
||||||
|
* @param string $group The group to delete.
|
||||||
|
*
|
||||||
|
* @return bool True if the group is deleted successfully, false otherwise.
|
||||||
|
*/
|
||||||
|
public function delete_cache_group( string $group = '' ): bool {
|
||||||
|
return self::invalidate_cache_group( $group );
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,32 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Automattic\WooCommerce\Caching;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation of CacheEngine that uses the built-in WordPress cache.
|
|
||||||
*/
|
|
||||||
class WpCacheEngine implements CacheEngine {
|
|
||||||
|
|
||||||
public const CACHE_GROUP_NAME = 'wc-object-cache';
|
|
||||||
|
|
||||||
// phpcs:disable Squiz.Commenting.FunctionComment.Missing
|
|
||||||
|
|
||||||
public function get_cached_object( string $key ) {
|
|
||||||
$value = wp_cache_get( $key, self::CACHE_GROUP_NAME );
|
|
||||||
return false === $value ? null : $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function cache_object( string $key, $object, int $expiration ): bool {
|
|
||||||
return wp_cache_set( $key, $object, self::CACHE_GROUP_NAME, $expiration );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete_cached_object( string $key ): bool {
|
|
||||||
return wp_cache_delete( $key, self::CACHE_GROUP_NAME );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function is_cached( string $key ): bool {
|
|
||||||
return false !== wp_cache_get( $key, self::CACHE_GROUP_NAME );
|
|
||||||
}
|
|
||||||
|
|
||||||
// phpcs:enable Squiz.Commenting.FunctionComment.Missing
|
|
||||||
}
|
|
|
@ -5,9 +5,12 @@
|
||||||
|
|
||||||
namespace Automattic\WooCommerce\Internal\DataStores\Orders;
|
namespace Automattic\WooCommerce\Internal\DataStores\Orders;
|
||||||
|
|
||||||
|
use Automattic\WooCommerce\Caches\OrderCache;
|
||||||
|
use Automattic\WooCommerce\Caches\OrderCacheController;
|
||||||
use Automattic\WooCommerce\Internal\BatchProcessing\BatchProcessingController;
|
use Automattic\WooCommerce\Internal\BatchProcessing\BatchProcessingController;
|
||||||
use Automattic\WooCommerce\Internal\Features\FeaturesController;
|
use Automattic\WooCommerce\Internal\Features\FeaturesController;
|
||||||
use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods;
|
use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods;
|
||||||
|
use Automattic\WooCommerce\Utilities\OrderUtil;
|
||||||
|
|
||||||
defined( 'ABSPATH' ) || exit;
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
@ -81,6 +84,20 @@ class CustomOrdersTableController {
|
||||||
*/
|
*/
|
||||||
private $features_controller;
|
private $features_controller;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The orders cache object to use.
|
||||||
|
*
|
||||||
|
* @var OrderCache
|
||||||
|
*/
|
||||||
|
private $order_cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The orders cache controller object to use.
|
||||||
|
*
|
||||||
|
* @var OrderCacheController
|
||||||
|
*/
|
||||||
|
private $order_cache_controller;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class constructor.
|
* Class constructor.
|
||||||
*/
|
*/
|
||||||
|
@ -114,18 +131,24 @@ class CustomOrdersTableController {
|
||||||
* @param OrdersTableRefundDataStore $refund_data_store The refund data store to use.
|
* @param OrdersTableRefundDataStore $refund_data_store The refund data store to use.
|
||||||
* @param BatchProcessingController $batch_processing_controller The batch processing controller to use.
|
* @param BatchProcessingController $batch_processing_controller The batch processing controller to use.
|
||||||
* @param FeaturesController $features_controller The features controller instance to use.
|
* @param FeaturesController $features_controller The features controller instance to use.
|
||||||
|
* @param OrderCache $order_cache The order cache engine to use.
|
||||||
|
* @param OrderCacheController $order_cache_controller The order cache controller to use.
|
||||||
*/
|
*/
|
||||||
final public function init(
|
final public function init(
|
||||||
OrdersTableDataStore $data_store,
|
OrdersTableDataStore $data_store,
|
||||||
DataSynchronizer $data_synchronizer,
|
DataSynchronizer $data_synchronizer,
|
||||||
OrdersTableRefundDataStore $refund_data_store,
|
OrdersTableRefundDataStore $refund_data_store,
|
||||||
BatchProcessingController $batch_processing_controller,
|
BatchProcessingController $batch_processing_controller,
|
||||||
FeaturesController $features_controller ) {
|
FeaturesController $features_controller,
|
||||||
|
OrderCache $order_cache,
|
||||||
|
OrderCacheController $order_cache_controller ) {
|
||||||
$this->data_store = $data_store;
|
$this->data_store = $data_store;
|
||||||
$this->data_synchronizer = $data_synchronizer;
|
$this->data_synchronizer = $data_synchronizer;
|
||||||
$this->batch_processing_controller = $batch_processing_controller;
|
$this->batch_processing_controller = $batch_processing_controller;
|
||||||
$this->refund_data_store = $refund_data_store;
|
$this->refund_data_store = $refund_data_store;
|
||||||
$this->features_controller = $features_controller;
|
$this->features_controller = $features_controller;
|
||||||
|
$this->order_cache = $order_cache;
|
||||||
|
$this->order_cache_controller = $order_cache_controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -462,12 +485,19 @@ class CustomOrdersTableController {
|
||||||
* @throws \Exception Attempt to change the authoritative orders table while orders sync is pending.
|
* @throws \Exception Attempt to change the authoritative orders table while orders sync is pending.
|
||||||
*/
|
*/
|
||||||
private function process_pre_update_option( $value, $option, $old_value ) {
|
private function process_pre_update_option( $value, $option, $old_value ) {
|
||||||
|
if ( DataSynchronizer::ORDERS_DATA_SYNC_ENABLED_OPTION === $option && $value !== $old_value ) {
|
||||||
|
$this->order_cache->flush();
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
if ( self::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION !== $option || $value === $old_value || false === $old_value ) {
|
if ( self::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION !== $option || $value === $old_value || false === $old_value ) {
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->order_cache->flush();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Commenting out for better testability.
|
* Re-enable the following code once the COT to posts table sync is implemented (it's currently commented out to ease testing).
|
||||||
$sync_is_pending = 0 !== $this->data_synchronizer->get_current_orders_pending_sync_count();
|
$sync_is_pending = 0 !== $this->data_synchronizer->get_current_orders_pending_sync_count();
|
||||||
if ( $sync_is_pending ) {
|
if ( $sync_is_pending ) {
|
||||||
throw new \Exception( "The authoritative table for orders storage can't be changed while there are orders out of sync" );
|
throw new \Exception( "The authoritative table for orders storage can't be changed while there are orders out of sync" );
|
||||||
|
@ -575,4 +605,6 @@ class CustomOrdersTableController {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
namespace Automattic\WooCommerce\Internal\DataStores\Orders;
|
namespace Automattic\WooCommerce\Internal\DataStores\Orders;
|
||||||
|
|
||||||
|
use Automattic\WooCommerce\Caches\OrderCache;
|
||||||
|
use Automattic\WooCommerce\Caches\OrderCacheController;
|
||||||
use Automattic\WooCommerce\Database\Migrations\CustomOrderTable\PostsToOrdersMigrationController;
|
use Automattic\WooCommerce\Database\Migrations\CustomOrderTable\PostsToOrdersMigrationController;
|
||||||
use Automattic\WooCommerce\Internal\BatchProcessing\BatchProcessorInterface;
|
use Automattic\WooCommerce\Internal\BatchProcessing\BatchProcessorInterface;
|
||||||
use Automattic\WooCommerce\Internal\Features\FeaturesController;
|
use Automattic\WooCommerce\Internal\Features\FeaturesController;
|
||||||
|
@ -63,6 +65,13 @@ class DataSynchronizer implements BatchProcessorInterface {
|
||||||
*/
|
*/
|
||||||
private $error_logger;
|
private $error_logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The order cache controller.
|
||||||
|
*
|
||||||
|
* @var OrderCacheController
|
||||||
|
*/
|
||||||
|
private $order_cache_controller;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class constructor.
|
* Class constructor.
|
||||||
*/
|
*/
|
||||||
|
@ -80,13 +89,21 @@ class DataSynchronizer implements BatchProcessorInterface {
|
||||||
* @param DatabaseUtil $database_util The database util class to use.
|
* @param DatabaseUtil $database_util The database util class to use.
|
||||||
* @param PostsToOrdersMigrationController $posts_to_cot_migrator The posts to COT migration class to use.
|
* @param PostsToOrdersMigrationController $posts_to_cot_migrator The posts to COT migration class to use.
|
||||||
* @param LegacyProxy $legacy_proxy The legacy proxy instance to use.
|
* @param LegacyProxy $legacy_proxy The legacy proxy instance to use.
|
||||||
|
* @param OrderCacheController $order_cache_controller The order cache controller instance to use.
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
final public function init( OrdersTableDataStore $data_store, DatabaseUtil $database_util, PostsToOrdersMigrationController $posts_to_cot_migrator, LegacyProxy $legacy_proxy ) {
|
final public function init(
|
||||||
|
OrdersTableDataStore $data_store,
|
||||||
|
DatabaseUtil $database_util,
|
||||||
|
PostsToOrdersMigrationController $posts_to_cot_migrator,
|
||||||
|
LegacyProxy $legacy_proxy,
|
||||||
|
OrderCacheController $order_cache_controller
|
||||||
|
) {
|
||||||
$this->data_store = $data_store;
|
$this->data_store = $data_store;
|
||||||
$this->database_util = $database_util;
|
$this->database_util = $database_util;
|
||||||
$this->posts_to_cot_migrator = $posts_to_cot_migrator;
|
$this->posts_to_cot_migrator = $posts_to_cot_migrator;
|
||||||
$this->error_logger = $legacy_proxy->call_function( 'wc_get_logger' );
|
$this->error_logger = $legacy_proxy->call_function( 'wc_get_logger' );
|
||||||
|
$this->order_cache_controller = $order_cache_controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -329,6 +346,8 @@ WHERE
|
||||||
* @param array $batch Batch details.
|
* @param array $batch Batch details.
|
||||||
*/
|
*/
|
||||||
public function process_batch( array $batch ) : void {
|
public function process_batch( array $batch ) : void {
|
||||||
|
$this->order_cache_controller->temporarily_disable_orders_cache_usage();
|
||||||
|
|
||||||
if ( $this->custom_orders_table_is_authoritative() ) {
|
if ( $this->custom_orders_table_is_authoritative() ) {
|
||||||
foreach ( $batch as $id ) {
|
foreach ( $batch as $id ) {
|
||||||
$order = wc_get_order( $id );
|
$order = wc_get_order( $id );
|
||||||
|
@ -344,6 +363,7 @@ WHERE
|
||||||
}
|
}
|
||||||
if ( 0 === $this->get_total_pending_count() ) {
|
if ( 0 === $this->get_total_pending_count() ) {
|
||||||
$this->cleanup_synchronization_state();
|
$this->cleanup_synchronization_state();
|
||||||
|
$this->order_cache_controller->maybe_restore_orders_cache_usage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
namespace Automattic\WooCommerce\Internal\DataStores\Orders;
|
namespace Automattic\WooCommerce\Internal\DataStores\Orders;
|
||||||
|
|
||||||
use Automattic\Jetpack\Constants;
|
use Automattic\Jetpack\Constants;
|
||||||
|
use Automattic\WooCommerce\Caches\OrderCache;
|
||||||
use Automattic\WooCommerce\Internal\Utilities\DatabaseUtil;
|
use Automattic\WooCommerce\Internal\Utilities\DatabaseUtil;
|
||||||
use Automattic\WooCommerce\Proxies\LegacyProxy;
|
use Automattic\WooCommerce\Proxies\LegacyProxy;
|
||||||
use Automattic\WooCommerce\Utilities\ArrayUtil;
|
use Automattic\WooCommerce\Utilities\ArrayUtil;
|
||||||
|
@ -1974,6 +1975,7 @@ FROM $order_meta_table
|
||||||
*/
|
*/
|
||||||
public function delete_order_data_from_custom_order_tables( $order_id ) {
|
public function delete_order_data_from_custom_order_tables( $order_id ) {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
$order_cache = wc_get_container()->get( OrderCache::class );
|
||||||
|
|
||||||
// Delete COT-specific data.
|
// Delete COT-specific data.
|
||||||
foreach ( $this->get_all_table_names() as $table ) {
|
foreach ( $this->get_all_table_names() as $table ) {
|
||||||
|
@ -1984,6 +1986,7 @@ FROM $order_meta_table
|
||||||
: array( 'order_id' => $order_id ),
|
: array( 'order_id' => $order_id ),
|
||||||
array( '%d' )
|
array( '%d' )
|
||||||
);
|
);
|
||||||
|
$order_cache->remove( $order_id );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ namespace Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders;
|
||||||
|
|
||||||
use Automattic\WooCommerce\DataBase\Migrations\CustomOrderTable\CLIRunner;
|
use Automattic\WooCommerce\DataBase\Migrations\CustomOrderTable\CLIRunner;
|
||||||
use Automattic\WooCommerce\Database\Migrations\CustomOrderTable\PostsToOrdersMigrationController;
|
use Automattic\WooCommerce\Database\Migrations\CustomOrderTable\PostsToOrdersMigrationController;
|
||||||
use Automattic\WooCommerce\Internal\DataStores\Orders\DataSynchronizer;
|
|
||||||
use Automattic\WooCommerce\Internal\DependencyManagement\AbstractServiceProvider;
|
use Automattic\WooCommerce\Internal\DependencyManagement\AbstractServiceProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,7 +24,6 @@ class COTMigrationServiceProvider extends AbstractServiceProvider {
|
||||||
protected $provides = array(
|
protected $provides = array(
|
||||||
PostsToOrdersMigrationController::class,
|
PostsToOrdersMigrationController::class,
|
||||||
CLIRunner::class,
|
CLIRunner::class,
|
||||||
DataSynchronizer::class,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,5 +35,6 @@ class COTMigrationServiceProvider extends AbstractServiceProvider {
|
||||||
*/
|
*/
|
||||||
public function register() {
|
public function register() {
|
||||||
$this->share( PostsToOrdersMigrationController::class );
|
$this->share( PostsToOrdersMigrationController::class );
|
||||||
|
$this->share( CLIRunner::class );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders;
|
namespace Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders;
|
||||||
|
|
||||||
use Automattic\WooCommerce\Caching\WpCacheEngine;
|
use Automattic\WooCommerce\Caching\WPCacheEngine;
|
||||||
use Automattic\WooCommerce\Internal\DependencyManagement\AbstractServiceProvider;
|
use Automattic\WooCommerce\Internal\DependencyManagement\AbstractServiceProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,13 +16,13 @@ class ObjectCacheServiceProvider extends AbstractServiceProvider {
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $provides = array(
|
protected $provides = array(
|
||||||
WpCacheEngine::class,
|
WPCacheEngine::class,
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the classes.
|
* Register the classes.
|
||||||
*/
|
*/
|
||||||
public function register() {
|
public function register() {
|
||||||
$this->share( WpCacheEngine::class );
|
$this->share( WPCacheEngine::class );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
namespace Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders;
|
namespace Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders;
|
||||||
|
|
||||||
use Automattic\Jetpack\Constants;
|
use Automattic\Jetpack\Constants;
|
||||||
|
use Automattic\WooCommerce\Caches\OrderCache;
|
||||||
|
use Automattic\WooCommerce\Caches\OrderCacheController;
|
||||||
|
use Automattic\WooCommerce\Caching\TransientsEngine;
|
||||||
use Automattic\WooCommerce\DataBase\Migrations\CustomOrderTable\CLIRunner;
|
use Automattic\WooCommerce\DataBase\Migrations\CustomOrderTable\CLIRunner;
|
||||||
use Automattic\WooCommerce\Database\Migrations\CustomOrderTable\PostsToOrdersMigrationController;
|
use Automattic\WooCommerce\Database\Migrations\CustomOrderTable\PostsToOrdersMigrationController;
|
||||||
use Automattic\WooCommerce\Internal\BatchProcessing\BatchProcessingController;
|
use Automattic\WooCommerce\Internal\BatchProcessing\BatchProcessingController;
|
||||||
|
@ -36,6 +39,8 @@ class OrdersDataStoreServiceProvider extends AbstractServiceProvider {
|
||||||
CLIRunner::class,
|
CLIRunner::class,
|
||||||
OrdersTableDataStoreMeta::class,
|
OrdersTableDataStoreMeta::class,
|
||||||
OrdersTableRefundDataStore::class,
|
OrdersTableRefundDataStore::class,
|
||||||
|
OrderCache::class,
|
||||||
|
OrderCacheController::class,
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -45,7 +50,15 @@ class OrdersDataStoreServiceProvider extends AbstractServiceProvider {
|
||||||
$this->share( OrdersTableDataStoreMeta::class );
|
$this->share( OrdersTableDataStoreMeta::class );
|
||||||
|
|
||||||
$this->share( OrdersTableDataStore::class )->addArguments( array( OrdersTableDataStoreMeta::class, DatabaseUtil::class, LegacyProxy::class ) );
|
$this->share( OrdersTableDataStore::class )->addArguments( array( OrdersTableDataStoreMeta::class, DatabaseUtil::class, LegacyProxy::class ) );
|
||||||
$this->share( DataSynchronizer::class )->addArguments( array( OrdersTableDataStore::class, DatabaseUtil::class, PostsToOrdersMigrationController::class, LegacyProxy::class ) );
|
$this->share( DataSynchronizer::class )->addArguments(
|
||||||
|
array(
|
||||||
|
OrdersTableDataStore::class,
|
||||||
|
DatabaseUtil::class,
|
||||||
|
PostsToOrdersMigrationController::class,
|
||||||
|
LegacyProxy::class,
|
||||||
|
OrderCacheController::class,
|
||||||
|
)
|
||||||
|
);
|
||||||
$this->share( OrdersTableRefundDataStore::class )->addArguments( array( OrdersTableDataStoreMeta::class, DatabaseUtil::class, LegacyProxy::class ) );
|
$this->share( OrdersTableRefundDataStore::class )->addArguments( array( OrdersTableDataStoreMeta::class, DatabaseUtil::class, LegacyProxy::class ) );
|
||||||
$this->share( CustomOrdersTableController::class )->addArguments(
|
$this->share( CustomOrdersTableController::class )->addArguments(
|
||||||
array(
|
array(
|
||||||
|
@ -54,9 +67,12 @@ class OrdersDataStoreServiceProvider extends AbstractServiceProvider {
|
||||||
OrdersTableRefundDataStore::class,
|
OrdersTableRefundDataStore::class,
|
||||||
BatchProcessingController::class,
|
BatchProcessingController::class,
|
||||||
FeaturesController::class,
|
FeaturesController::class,
|
||||||
|
OrderCache::class,
|
||||||
|
OrderCacheController::class,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
$this->share( OrderCache::class );
|
||||||
|
$this->share( OrderCacheController::class )->addArgument( OrderCache::class );
|
||||||
if ( Constants::is_defined( 'WP_CLI' ) && WP_CLI ) {
|
if ( Constants::is_defined( 'WP_CLI' ) && WP_CLI ) {
|
||||||
$this->share( CLIRunner::class )->addArguments( array( CustomOrdersTableController::class, DataSynchronizer::class, PostsToOrdersMigrationController::class ) );
|
$this->share( CLIRunner::class )->addArguments( array( CustomOrdersTableController::class, DataSynchronizer::class, PostsToOrdersMigrationController::class ) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,10 @@
|
||||||
|
|
||||||
namespace Automattic\WooCommerce\Utilities;
|
namespace Automattic\WooCommerce\Utilities;
|
||||||
|
|
||||||
|
use Automattic\WooCommerce\Caches\OrderCacheController;
|
||||||
use Automattic\WooCommerce\Internal\Admin\Orders\PageController;
|
use Automattic\WooCommerce\Internal\Admin\Orders\PageController;
|
||||||
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
|
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
|
||||||
|
use Automattic\WooCommerce\Internal\Features\FeaturesController;
|
||||||
use Automattic\WooCommerce\Internal\Utilities\COTMigrationUtil;
|
use Automattic\WooCommerce\Internal\Utilities\COTMigrationUtil;
|
||||||
use WC_Order;
|
use WC_Order;
|
||||||
use WP_Post;
|
use WP_Post;
|
||||||
|
@ -35,6 +37,15 @@ final class OrderUtil {
|
||||||
return wc_get_container()->get( CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled();
|
return wc_get_container()->get( CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to get whether the orders cache should be used or not.
|
||||||
|
*
|
||||||
|
* @return bool True if the orders cache should be used, false otherwise.
|
||||||
|
*/
|
||||||
|
public static function orders_cache_usage_is_enabled() : bool {
|
||||||
|
return wc_get_container()->get( OrderCacheController::class )->orders_cache_usage_is_enabled();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if posts and order custom table sync is enabled and there are no pending orders.
|
* Checks if posts and order custom table sync is enabled and there are no pending orders.
|
||||||
*
|
*
|
||||||
|
|
|
@ -230,8 +230,9 @@ class WC_REST_Orders_Controller_Tests extends WC_REST_Unit_Test_Case {
|
||||||
$order = new \WC_Order();
|
$order = new \WC_Order();
|
||||||
$order->set_status( 'completed' );
|
$order->set_status( 'completed' );
|
||||||
$order->save();
|
$order->save();
|
||||||
|
$order_id = $order->get_id();
|
||||||
|
|
||||||
$request = new \WP_REST_Request( 'DELETE', '/wc/v3/orders/' . $order->get_id() );
|
$request = new \WP_REST_Request( 'DELETE', '/wc/v3/orders/' . $order_id );
|
||||||
$response = $this->server->dispatch( $request );
|
$response = $this->server->dispatch( $request );
|
||||||
|
|
||||||
$this->assertEquals( 200, $response->get_status() );
|
$this->assertEquals( 200, $response->get_status() );
|
||||||
|
@ -239,13 +240,13 @@ class WC_REST_Orders_Controller_Tests extends WC_REST_Unit_Test_Case {
|
||||||
// Check that the response includes order data from the order (before deletion).
|
// Check that the response includes order data from the order (before deletion).
|
||||||
$data = $response->get_data();
|
$data = $response->get_data();
|
||||||
$this->assertArrayHasKey( 'id', $data );
|
$this->assertArrayHasKey( 'id', $data );
|
||||||
$this->assertEquals( $data['id'], $order->get_id() );
|
$this->assertEquals( $data['id'], $order_id );
|
||||||
$this->assertEquals( 'completed', $data['status'] );
|
$this->assertEquals( 'completed', $data['status'] );
|
||||||
|
|
||||||
wp_cache_flush();
|
wp_cache_flush();
|
||||||
|
|
||||||
// Check the order was actually deleted.
|
// Check the order was actually deleted.
|
||||||
$order = wc_get_order( $order->get_id() );
|
$order = wc_get_order( $order_id );
|
||||||
$this->assertEquals( 'trash', $order->get_status( 'edit' ) );
|
$this->assertEquals( 'trash', $order->get_status( 'edit' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,15 @@ class CacheExceptionTest extends \WC_Unit_Test_Case {
|
||||||
public function get_object_type(): string {
|
public function get_object_type(): string {
|
||||||
return 'the_type';
|
return 'the_type';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function get_object_id( $object ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function validate( $object ): ?array {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function get_from_datastore( $id ) {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// phpcs:enable Squiz.Commenting
|
// phpcs:enable Squiz.Commenting
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Automattic\WooCommerce\Tests\Caching;
|
|
||||||
|
|
||||||
use Automattic\WooCommerce\Caching\CacheEngine;
|
|
||||||
use Automattic\WooCommerce\Utilities\ArrayUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An implementation of CacheEngine that simply stores cached objects in an array.
|
|
||||||
*/
|
|
||||||
class InMemoryObjectCacheEngine implements CacheEngine {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The cached objects.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public $cache = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether calls to 'cache_object' will succeed or not.
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
public $caching_succeeds = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Value of the expiration time that was passed to 'cache_object' the last time it was called.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $last_expiration;
|
|
||||||
|
|
||||||
// phpcs:disable Squiz.Commenting
|
|
||||||
|
|
||||||
public function get_cached_object( string $key ) {
|
|
||||||
return ArrayUtil::get_value_or_default( $this->cache, $key, null );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function cache_object( string $key, $object, int $expiration ): bool {
|
|
||||||
if ( ! $this->caching_succeeds ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->cache[ $key ] = $object;
|
|
||||||
$this->last_expiration = $expiration;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete_cached_object( string $key ): bool {
|
|
||||||
if ( array_key_exists( $key, $this->cache ) ) {
|
|
||||||
unset( $this->cache[ $key ] );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function is_cached( $key ): bool {
|
|
||||||
return array_key_exists( $key, $this->cache );
|
|
||||||
}
|
|
||||||
|
|
||||||
// phpcs:enable Squiz.Commenting
|
|
||||||
}
|
|
|
@ -11,10 +11,18 @@ class InvalidObjectCacheClass extends ObjectCache {
|
||||||
|
|
||||||
|
|
||||||
// phpcs:disable Squiz.Commenting
|
// phpcs:disable Squiz.Commenting
|
||||||
|
|
||||||
public function get_object_type(): string {
|
public function get_object_type(): string {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function get_object_id( $object ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function validate( $object ): ?array {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function get_from_datastore( $id ) {
|
||||||
|
}
|
||||||
// phpcs:enable Squiz.Commenting
|
// phpcs:enable Squiz.Commenting
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ namespace Automattic\WooCommerce\Tests\Caching;
|
||||||
use Automattic\WooCommerce\Caching\CacheException;
|
use Automattic\WooCommerce\Caching\CacheException;
|
||||||
use Automattic\WooCommerce\Caching\ObjectCache;
|
use Automattic\WooCommerce\Caching\ObjectCache;
|
||||||
use Automattic\WooCommerce\Caching\CacheEngine;
|
use Automattic\WooCommerce\Caching\CacheEngine;
|
||||||
use Automattic\WooCommerce\Caching\WpCacheEngine;
|
use Automattic\WooCommerce\Caching\WPCacheEngine;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for the ObjectCache class.
|
* Tests for the ObjectCache class.
|
||||||
|
@ -19,23 +19,10 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
|
||||||
*/
|
*/
|
||||||
private $sut;
|
private $sut;
|
||||||
|
|
||||||
/**
|
|
||||||
* The cache engine to use.
|
|
||||||
*
|
|
||||||
* @var CacheEngine
|
|
||||||
*/
|
|
||||||
private $cache_engine;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs before each test.
|
* Runs before each test.
|
||||||
*/
|
*/
|
||||||
public function setUp(): void {
|
public function setUp(): void {
|
||||||
$cache_engine = new InMemoryObjectCacheEngine();
|
|
||||||
$this->cache_engine = $cache_engine;
|
|
||||||
|
|
||||||
$container = wc_get_container();
|
|
||||||
$container->replace( WpCacheEngine::class, $cache_engine );
|
|
||||||
$this->reset_container_resolutions();
|
|
||||||
|
|
||||||
// phpcs:disable Squiz.Commenting
|
// phpcs:disable Squiz.Commenting
|
||||||
|
|
||||||
|
@ -50,9 +37,22 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
|
||||||
$this->random_string_index++;
|
$this->random_string_index++;
|
||||||
return 'random_' . $this->random_string_index;
|
return 'random_' . $this->random_string_index;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
protected function get_object_id( $object ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function validate( $object ): ?array {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function get_from_datastore( $id ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
// phpcs:enable Squiz.Commenting
|
// phpcs:enable Squiz.Commenting
|
||||||
|
|
||||||
|
$this->sut->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -94,7 +94,7 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
|
||||||
$this->expectException( CacheException::class );
|
$this->expectException( CacheException::class );
|
||||||
$this->expectExceptionMessage( "Can't cache a null value" );
|
$this->expectExceptionMessage( "Can't cache a null value" );
|
||||||
|
|
||||||
$this->sut->set( 'the_id', null );
|
$this->sut->set( null );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -104,7 +104,7 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
|
||||||
$this->expectException( CacheException::class );
|
$this->expectException( CacheException::class );
|
||||||
$this->expectExceptionMessage( "Can't cache a non-object, non-array value" );
|
$this->expectExceptionMessage( "Can't cache a non-object, non-array value" );
|
||||||
|
|
||||||
$this->sut->set( 'the_id', 1234 );
|
$this->sut->set( 1234 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -114,7 +114,7 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
|
||||||
$this->expectException( CacheException::class );
|
$this->expectException( CacheException::class );
|
||||||
$this->expectExceptionMessage( "Object id must be an int, a string, or null for 'set'" );
|
$this->expectExceptionMessage( "Object id must be an int, a string, or null for 'set'" );
|
||||||
|
|
||||||
$this->sut->set( array( 1, 2 ), array( 'foo' ) );
|
$this->sut->set( array( 'foo' ), array( 1, 2 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -130,7 +130,7 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
|
||||||
$this->expectException( CacheException::class );
|
$this->expectException( CacheException::class );
|
||||||
$this->expectExceptionMessage( 'Invalid expiration value, must be ObjectCache::DEFAULT_EXPIRATION or a value between 1 and ObjectCache::MAX_EXPIRATION' );
|
$this->expectExceptionMessage( 'Invalid expiration value, must be ObjectCache::DEFAULT_EXPIRATION or a value between 1 and ObjectCache::MAX_EXPIRATION' );
|
||||||
|
|
||||||
$this->sut->set( 'the_id', array( 'foo' ), $expiration );
|
$this->sut->set( array( 'foo' ), 'the_id', $expiration );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -139,7 +139,7 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
|
||||||
public function try_set_when_cache_engine_fails() {
|
public function try_set_when_cache_engine_fails() {
|
||||||
$this->cache_engine->caching_succeeds = false;
|
$this->cache_engine->caching_succeeds = false;
|
||||||
|
|
||||||
$result = $this->sut->set( 'the_id', array( 'foo' ) );
|
$result = $this->sut->set( array( 'foo' ), 'the_id' );
|
||||||
$this->assertFalse( $result );
|
$this->assertFalse( $result );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,16 +148,14 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
|
||||||
*/
|
*/
|
||||||
public function test_set_new_object_with_id_caches_with_expected_key() {
|
public function test_set_new_object_with_id_caches_with_expected_key() {
|
||||||
$object = array( 'foo' );
|
$object = array( 'foo' );
|
||||||
$result = $this->sut->set( 'the_id', $object );
|
$result = $this->sut->set( $object, 'the_id' );
|
||||||
|
|
||||||
$this->assertTrue( $result );
|
$this->assertTrue( $result );
|
||||||
|
|
||||||
$expected_prefix = 'woocommerce_object_cache|the_type|random_1|';
|
$expected_prefix = \WC_Cache_Helper::get_cache_prefix( 'the_type' );
|
||||||
$this->assertEquals( $expected_prefix, get_option( 'wp_object_cache_key_prefix_the_type' ) );
|
|
||||||
|
|
||||||
$key = $expected_prefix . 'the_id';
|
$key = $expected_prefix . 'the_id';
|
||||||
$expected_cached = array( 'data' => $object );
|
|
||||||
$this->assertEquals( $expected_cached, $this->cache_engine->cache[ $key ] );
|
$this->assertEquals( $object, wp_cache_get( $key, 'the_type' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -165,34 +163,16 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
|
||||||
*/
|
*/
|
||||||
public function test_setting_two_objects_result_in_same_prefix() {
|
public function test_setting_two_objects_result_in_same_prefix() {
|
||||||
$object_1 = array( 'foo' );
|
$object_1 = array( 'foo' );
|
||||||
$this->sut->set( 'the_id_1', $object_1 );
|
$this->sut->set( $object_1, 'the_id_1' );
|
||||||
$object_2 = array( 1, 2, 3, 4 );
|
$object_2 = array( 1, 2, 3, 4 );
|
||||||
$this->sut->set( 9999, $object_2 );
|
$this->sut->set( $object_2, 9999 );
|
||||||
|
|
||||||
$prefix = 'woocommerce_object_cache|the_type|random_1|';
|
$prefix = \WC_Cache_Helper::get_cache_prefix( 'the_type' );
|
||||||
|
|
||||||
$key_1 = $prefix . 'the_id_1';
|
$key_1 = $prefix . 'the_id_1';
|
||||||
$expected_cached = array( 'data' => $object_1 );
|
$this->assertEquals( $object_1, wp_cache_get( $key_1, 'the_type' ) );
|
||||||
$this->assertEquals( $expected_cached, $this->cache_engine->cache[ $key_1 ] );
|
|
||||||
$key_2 = $prefix . '9999';
|
$key_2 = $prefix . '9999';
|
||||||
$expected_cached = array( 'data' => $object_2 );
|
$this->assertEquals( $object_2, wp_cache_get( $key_2, 'the_type' ) );
|
||||||
$this->assertEquals( $expected_cached, $this->cache_engine->cache[ $key_2 ] );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @testdox 'set' uses the default expiration value if no explicit value is passed.
|
|
||||||
*/
|
|
||||||
public function test_set_with_default_expiration() {
|
|
||||||
$this->sut->set( 'the_id', array( 'foo' ) );
|
|
||||||
$this->assertEquals( $this->sut->get_default_expiration_value(), $this->cache_engine->last_expiration );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @testdox 'set' uses the explicitly passed expiration value.
|
|
||||||
*/
|
|
||||||
public function test_set_with_explicit_expiration() {
|
|
||||||
$this->sut->set( 'the_id', array( 'foo' ), 1234 );
|
|
||||||
$this->assertEquals( 1234, $this->cache_engine->last_expiration );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -202,7 +182,7 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
|
||||||
$this->expectException( CacheException::class );
|
$this->expectException( CacheException::class );
|
||||||
$this->expectExceptionMessage( "Null id supplied and the cache class doesn't implement get_object_id" );
|
$this->expectExceptionMessage( "Null id supplied and the cache class doesn't implement get_object_id" );
|
||||||
|
|
||||||
$this->sut->set( null, array( 'foo' ) );
|
$this->sut->set( array( 'foo' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -223,21 +203,28 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
|
||||||
protected function get_random_string(): string {
|
protected function get_random_string(): string {
|
||||||
return 'random';
|
return 'random';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function validate( $object ): ?array {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function get_from_datastore( $id ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// phpcs:enable Squiz.Commenting
|
// phpcs:enable Squiz.Commenting
|
||||||
|
|
||||||
$sut->set( null, array( 'id' => 1234 ) );
|
$sut->set( array( 'id' => 1234 ) );
|
||||||
|
|
||||||
$this->assertEquals( 'woocommerce_object_cache|the_type|random|1235', array_keys( $this->cache_engine->cache )[0] );
|
$this->assertEquals( array( 'id' => 1234 ), $sut->get( '1235' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @testdox 'set' caches the value returned by 'serialize'.
|
* @testdox 'update_if_cached' does nothing if no object is cached with the passed (or obtained) id.
|
||||||
*/
|
*/
|
||||||
public function test_set_with_custom_serialization() {
|
public function test_update_if_cached_does_nothing_for_not_cached_id() {
|
||||||
$object = array( 'foo' );
|
$id = 1234;
|
||||||
|
|
||||||
// phpcs:disable Squiz.Commenting
|
// phpcs:disable Squiz.Commenting
|
||||||
|
|
||||||
$sut = new class() extends ObjectCache {
|
$sut = new class() extends ObjectCache {
|
||||||
|
@ -245,18 +232,77 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
|
||||||
return 'the_type';
|
return 'the_type';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function serialize( $object ): array {
|
protected function get_object_id( $object ) {
|
||||||
return array( 'the_data' => $object );
|
return $object['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function validate( $object ): ?array {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function get_from_datastore( $id ) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// phpcs:enable Squiz.Commenting
|
// phpcs:enable Squiz.Commenting
|
||||||
|
|
||||||
$sut->set( 1234, $object );
|
$result = $sut->update_if_cached( array( 'id' => 1234 ), $id );
|
||||||
|
$this->assertFalse( $result );
|
||||||
|
$this->assertEmpty( $sut->get( $id ) );
|
||||||
|
}
|
||||||
|
|
||||||
$cached = array_values( $this->cache_engine->cache )[0];
|
/**
|
||||||
$expected = array( 'the_data' => $object );
|
* @testdox 'update_if_cached' updates an already cached object the same way as 'set'.
|
||||||
$this->assertEquals( $expected, $cached );
|
*/
|
||||||
|
public function test_update_if_cached_updates_already_cached_object() {
|
||||||
|
$id = 1234;
|
||||||
|
// phpcs:disable Squiz.Commenting
|
||||||
|
|
||||||
|
$sut = new class() extends ObjectCache {
|
||||||
|
public function get_object_type(): string {
|
||||||
|
return 'the_type';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function get_object_id( $object ) {
|
||||||
|
return $object['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function get_random_string(): string {
|
||||||
|
return 'random';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function validate( $object ): ?array {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function get_from_datastore( $id ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// phpcs:enable Squiz.Commenting
|
||||||
|
|
||||||
|
$sut->set( array( 'id' => 1234 ), $id );
|
||||||
|
$this->assertEquals( array( 'id' => 1234 ), $sut->get( $id ) );
|
||||||
|
|
||||||
|
$new_value = array(
|
||||||
|
'id' => 1234,
|
||||||
|
'foo' => 'bar',
|
||||||
|
);
|
||||||
|
$result = $sut->update_if_cached( $new_value, $id );
|
||||||
|
$this->assertTrue( $result );
|
||||||
|
$this->assertEquals( $new_value, $sut->get( $id ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @testdox 'update_if_cached' throws an exception if no object id is passed and the class doesn't implement 'get_object_id'.
|
||||||
|
*/
|
||||||
|
public function test_update_if_cached_null_id_without_id_retrieval_implementation() {
|
||||||
|
$this->expectException( CacheException::class );
|
||||||
|
$this->expectExceptionMessage( "Null id supplied and the cache class doesn't implement get_object_id" );
|
||||||
|
|
||||||
|
$this->sut->update_if_cached( array( 'foo' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -288,12 +334,20 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
|
||||||
protected function validate( $object ): array {
|
protected function validate( $object ): array {
|
||||||
return $this->errors;
|
return $this->errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function get_from_datastore( $id ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function get_object_id( $object ) {
|
||||||
|
return $object['id'];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// phpcs:enable Squiz.Commenting
|
// phpcs:enable Squiz.Commenting
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$sut->set( 1234, $object );
|
$sut->set( $object, 1234 );
|
||||||
} catch ( CacheException $thrown ) {
|
} catch ( CacheException $thrown ) {
|
||||||
$exception = $thrown;
|
$exception = $thrown;
|
||||||
}
|
}
|
||||||
|
@ -343,7 +397,7 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
|
||||||
*/
|
*/
|
||||||
public function test_try_getting_previously_cached_object() {
|
public function test_try_getting_previously_cached_object() {
|
||||||
$object = array( 'foo' );
|
$object = array( 'foo' );
|
||||||
$this->sut->set( 'the_id', $object );
|
$this->sut->set( $object, 'the_id' );
|
||||||
|
|
||||||
$result = $this->sut->get( 'the_id' );
|
$result = $this->sut->get( 'the_id' );
|
||||||
$this->assertEquals( $object, $result );
|
$this->assertEquals( $object, $result );
|
||||||
|
@ -369,115 +423,15 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
|
||||||
|
|
||||||
$expected = array( 'id' => 'the_id' );
|
$expected = array( 'id' => 'the_id' );
|
||||||
$this->assertEquals( $expected, $result );
|
$this->assertEquals( $expected, $result );
|
||||||
$this->assertEquals( array( 'data' => $expected ), array_values( $this->cache_engine->cache )[0] );
|
$this->assertEquals( $expected, $this->sut->get( 'the_id' ) );
|
||||||
$this->assertEquals( $this->sut->get_default_expiration_value(), $this->cache_engine->last_expiration );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @testdox 'get' uses the passed object retrieval callback if there's no object cached under the passed id, and caches the object retrieved using the passed expiration value.
|
|
||||||
*/
|
|
||||||
public function test_try_getting_not_cached_object_with_callback_and_explicit_expiration() {
|
|
||||||
$expiration = 1234;
|
|
||||||
|
|
||||||
$callback = function( $id ) {
|
|
||||||
return array( 'id' => $id );
|
|
||||||
};
|
|
||||||
|
|
||||||
$result = $this->sut->get( 'the_id', $expiration, $callback );
|
|
||||||
|
|
||||||
$expected = array( 'id' => 'the_id' );
|
|
||||||
$this->assertEquals( $expected, $result );
|
|
||||||
$this->assertEquals( array( 'data' => $expected ), array_values( $this->cache_engine->cache )[0] );
|
|
||||||
$this->assertEquals( $expiration, $this->cache_engine->last_expiration );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @testdox 'get' uses the 'get_from_datastore' method if there's no object cached under the passed id, and caches the object retrieved.
|
|
||||||
*/
|
|
||||||
public function test_try_getting_not_cached_object_get_from_datastore_implemented() {
|
|
||||||
// phpcs:disable Squiz.Commenting
|
|
||||||
|
|
||||||
$sut = new class() extends ObjectCache {
|
|
||||||
public function get_object_type(): string {
|
|
||||||
return 'the_type';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function get_from_datastore( $id ) {
|
|
||||||
return array( 'id' => $id );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// phpcs:enable Squiz.Commenting
|
|
||||||
|
|
||||||
$result = $sut->get( 'the_id' );
|
|
||||||
|
|
||||||
$expected = array( 'id' => 'the_id' );
|
|
||||||
$this->assertEquals( $expected, $result );
|
|
||||||
$this->assertEquals( array( 'data' => $expected ), array_values( $this->cache_engine->cache )[0] );
|
|
||||||
$this->assertEquals( $this->sut->get_default_expiration_value(), $this->cache_engine->last_expiration );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @testdox 'get' uses the 'get_from_datastore' method if there's no object cached under the passed id, and caches the object retrieved using the passed expiration value.
|
|
||||||
*/
|
|
||||||
public function test_try_getting_not_cached_object_get_from_datastore_implemented_and_explicit_expiration() {
|
|
||||||
$expiration = 1234;
|
|
||||||
|
|
||||||
// phpcs:disable Squiz.Commenting
|
|
||||||
|
|
||||||
$sut = new class() extends ObjectCache {
|
|
||||||
public function get_object_type(): string {
|
|
||||||
return 'the_type';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function get_from_datastore( $id ) {
|
|
||||||
return array( 'id' => $id );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// phpcs:enable Squiz.Commenting
|
|
||||||
|
|
||||||
$result = $sut->get( 'the_id', $expiration );
|
|
||||||
|
|
||||||
$expected = array( 'id' => 'the_id' );
|
|
||||||
$this->assertEquals( $expected, $result );
|
|
||||||
$this->assertEquals( array( 'data' => $expected ), array_values( $this->cache_engine->cache )[0] );
|
|
||||||
$this->assertEquals( $expiration, $this->cache_engine->last_expiration );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @testdox 'get' applies 'deserialize' to the object returned by the cache engine before returning it.
|
|
||||||
*/
|
|
||||||
public function test_custom_deserialization() {
|
|
||||||
// phpcs:disable Squiz.Commenting
|
|
||||||
|
|
||||||
$sut = new class() extends ObjectCache {
|
|
||||||
public function get_object_type(): string {
|
|
||||||
return 'the_type';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function deserialize( array $serialized ) {
|
|
||||||
$object = $serialized['data'];
|
|
||||||
$object[] = 3;
|
|
||||||
return $object;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// phpcs:enable Squiz.Commenting
|
|
||||||
|
|
||||||
$sut->set( 'the_id', array( 1, 2 ) );
|
|
||||||
|
|
||||||
$result = $sut->get( 'the_id' );
|
|
||||||
$expected = array( 1, 2, 3 );
|
|
||||||
$this->assertEquals( $expected, $result );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @testdox 'remove' removes a cached object and returns true, or returns false if there's no cached object under the passed id.
|
* @testdox 'remove' removes a cached object and returns true, or returns false if there's no cached object under the passed id.
|
||||||
*/
|
*/
|
||||||
public function test_remove() {
|
public function test_remove() {
|
||||||
$this->sut->set( 'the_id_1', array( 'foo' ) );
|
$this->sut->set( array( 'foo' ), 'the_id_1' );
|
||||||
$this->sut->set( 'the_id_2', array( 'bar' ) );
|
$this->sut->set( array( 'bar' ), 'the_id_2' );
|
||||||
|
|
||||||
$result_1 = $this->sut->remove( 'the_id_1' );
|
$result_1 = $this->sut->remove( 'the_id_1' );
|
||||||
$result_2 = $this->sut->remove( 'the_id_X' );
|
$result_2 = $this->sut->remove( 'the_id_X' );
|
||||||
|
@ -493,15 +447,17 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
|
||||||
* @testdox 'flush' deletes the stored cache key prefix, effectively rendering the cached objects inaccessible.
|
* @testdox 'flush' deletes the stored cache key prefix, effectively rendering the cached objects inaccessible.
|
||||||
*/
|
*/
|
||||||
public function test_flush() {
|
public function test_flush() {
|
||||||
$this->sut->set( 'the_id', array( 'foo' ) );
|
$this->sut->set( array( 'foo' ), 'the_id' );
|
||||||
|
|
||||||
|
$current_prefix_key = \WC_Cache_Helper::get_cache_prefix( 'the_type' );
|
||||||
$this->sut->flush();
|
$this->sut->flush();
|
||||||
$this->assertFalse( get_option( 'wp_object_cache_key_prefix_the_type' ) );
|
$this->assertFalse( $this->sut->is_cached( 'the_id' ) );
|
||||||
|
$expected_new_prefix = \WC_Cache_Helper::get_cache_prefix( 'the_type' );
|
||||||
|
$this->assertNotEquals( $current_prefix_key, $expected_new_prefix );
|
||||||
|
|
||||||
$this->sut->set( 'the_id_2', array( 'bar' ) );
|
$this->sut->set( array( 'bar' ), 'the_id_2' );
|
||||||
|
|
||||||
$expected_new_prefix = 'woocommerce_object_cache|the_type|random_2|';
|
$this->assertEquals( $expected_new_prefix, \WC_Cache_Helper::get_cache_prefix( 'the_type' ) );
|
||||||
$this->assertEquals( $expected_new_prefix, get_option( 'wp_object_cache_key_prefix_the_type' ) );
|
|
||||||
$this->assertFalse( $this->sut->is_cached( 'the_id' ) );
|
$this->assertFalse( $this->sut->is_cached( 'the_id' ) );
|
||||||
$this->assertTrue( $this->sut->is_cached( 'the_id_2' ) );
|
$this->assertTrue( $this->sut->is_cached( 'the_id_2' ) );
|
||||||
}
|
}
|
||||||
|
@ -510,10 +466,9 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
|
||||||
* @testdox A custom cache engine instance can be used by overriding 'get_cache_engine_instance'.
|
* @testdox A custom cache engine instance can be used by overriding 'get_cache_engine_instance'.
|
||||||
*/
|
*/
|
||||||
public function test_custom_cache_engine_via_protected_method() {
|
public function test_custom_cache_engine_via_protected_method() {
|
||||||
$engine = new InMemoryObjectCacheEngine();
|
$engine = new WPCacheEngine();
|
||||||
|
|
||||||
// phpcs:disable Squiz.Commenting
|
// phpcs:disable Squiz.Commenting
|
||||||
|
|
||||||
$sut = new class($engine) extends ObjectCache {
|
$sut = new class($engine) extends ObjectCache {
|
||||||
public function get_object_type(): string {
|
public function get_object_type(): string {
|
||||||
return 'the_type';
|
return 'the_type';
|
||||||
|
@ -529,25 +484,51 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
|
||||||
protected function get_cache_engine_instance(): CacheEngine {
|
protected function get_cache_engine_instance(): CacheEngine {
|
||||||
return $this->engine;
|
return $this->engine;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
protected function get_object_id( $object ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function validate( $object ): ?array {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function get_from_datastore( $id ) {
|
||||||
|
}
|
||||||
|
};
|
||||||
// phpcs:enable Squiz.Commenting
|
// phpcs:enable Squiz.Commenting
|
||||||
|
|
||||||
$object = array( 'foo' );
|
$object = array( 'foo' );
|
||||||
$sut->set( 'the_id', $object );
|
$sut->set( $object, 'the_id' );
|
||||||
|
|
||||||
$expected_cached = array( 'data' => $object );
|
$this->assertEquals( $object, $sut->get( 'the_id' ) );
|
||||||
$this->assertEquals( $expected_cached, array_values( $engine->cache )[0] );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @testdox A custom cache engine instance can be used via 'wc_object_cache_get_engine' filter.
|
* @testdox A custom cache engine instance can be used via 'wc_object_cache_get_engine' filter.
|
||||||
*/
|
*/
|
||||||
public function test_custom_cache_engine_via_hook() {
|
public function test_custom_cache_engine_via_hook() {
|
||||||
$engine = new InMemoryObjectCacheEngine();
|
$engine = new class() extends WPCacheEngine {};
|
||||||
$engine_passed_to_filter = null;
|
$engine_passed_to_filter = null;
|
||||||
$cache_passed_to_filter = null;
|
$cache_passed_to_filter = null;
|
||||||
|
|
||||||
|
$sut = new class() extends ObjectCache {
|
||||||
|
// phpcs:disable Squiz.Commenting
|
||||||
|
public function get_object_type(): string {
|
||||||
|
return 'the_type';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function get_object_id( $object ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function validate( $object ): ?array {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function get_from_datastore( $id ) {
|
||||||
|
}
|
||||||
|
// phpcs:enable Squiz.Commenting
|
||||||
|
};
|
||||||
|
|
||||||
add_filter(
|
add_filter(
|
||||||
'wc_object_cache_get_engine',
|
'wc_object_cache_get_engine',
|
||||||
function( $old_engine, $cache ) use ( $engine, &$engine_passed_to_filter, &$cache_passed_to_filter ) {
|
function( $old_engine, $cache ) use ( $engine, &$engine_passed_to_filter, &$cache_passed_to_filter ) {
|
||||||
|
@ -560,131 +541,11 @@ class ObjectCacheTest extends \WC_Unit_Test_Case {
|
||||||
);
|
);
|
||||||
|
|
||||||
$object = array( 'foo' );
|
$object = array( 'foo' );
|
||||||
$this->sut->set( 'the_id', $object );
|
$sut->set( $object, 'the_id' );
|
||||||
|
|
||||||
$expected_cached = array( 'data' => $object );
|
$this->assertEquals( $object, $sut->get( 'the_id' ) );
|
||||||
$this->assertEquals( $expected_cached, array_values( $engine->cache )[0] );
|
|
||||||
|
|
||||||
$this->assertEquals( $engine_passed_to_filter, wc_get_container()->get( WpCacheEngine::class ) );
|
$this->assertEquals( $engine_passed_to_filter, wc_get_container()->get( WPCacheEngine::class ) );
|
||||||
$this->assertEquals( $cache_passed_to_filter, $this->sut );
|
$this->assertEquals( $cache_passed_to_filter, $sut );
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @testdox 'woocommerce_after_serializing_{type}_for_caching' allows to modify the serialized object before being cached.
|
|
||||||
*/
|
|
||||||
public function test_modifying_serialized_object_via_filter() {
|
|
||||||
$object_passed_to_filter = null;
|
|
||||||
$id_passed_to_filter = null;
|
|
||||||
|
|
||||||
add_filter(
|
|
||||||
'woocommerce_after_serializing_the_type_for_caching',
|
|
||||||
function( $data, $object, $id ) use ( &$object_passed_to_filter, &$id_passed_to_filter ) {
|
|
||||||
$object_passed_to_filter = $object;
|
|
||||||
$id_passed_to_filter = $id;
|
|
||||||
|
|
||||||
$data['foo'] = 'bar';
|
|
||||||
return $data;
|
|
||||||
},
|
|
||||||
10,
|
|
||||||
3
|
|
||||||
);
|
|
||||||
|
|
||||||
$object = array( 'fizz' );
|
|
||||||
$this->sut->set( 'the_id', $object );
|
|
||||||
|
|
||||||
$expected_cached = array(
|
|
||||||
'data' => $object,
|
|
||||||
'foo' => 'bar',
|
|
||||||
);
|
|
||||||
$this->assertEquals( $expected_cached, array_values( $this->cache_engine->cache )[0] );
|
|
||||||
|
|
||||||
$this->assertEquals( $object, $object_passed_to_filter );
|
|
||||||
$this->assertEquals( 'the_id', $id_passed_to_filter );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @testdox 'woocommerce_after_deserializing_{type}_from_cache' allows to modify the deserialized object before it's returned by 'get'.
|
|
||||||
*/
|
|
||||||
public function test_modifying_deserialized_object_via_filter() {
|
|
||||||
$object_passed_to_filter = null;
|
|
||||||
$id_passed_to_filter = null;
|
|
||||||
$data_passed_to_filter = null;
|
|
||||||
|
|
||||||
$original_object = array( 'foo' );
|
|
||||||
$replacement_object = array( 'bar' );
|
|
||||||
|
|
||||||
add_filter(
|
|
||||||
'woocommerce_after_deserializing_the_type_from_cache',
|
|
||||||
function( $object, $data, $id ) use ( &$object_passed_to_filter, &$id_passed_to_filter, &$data_passed_to_filter, $replacement_object ) {
|
|
||||||
$object_passed_to_filter = $object;
|
|
||||||
$id_passed_to_filter = $id;
|
|
||||||
$data_passed_to_filter = $data;
|
|
||||||
|
|
||||||
return $replacement_object;
|
|
||||||
},
|
|
||||||
10,
|
|
||||||
3
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->sut->set( 'the_id', $original_object );
|
|
||||||
$retrieved_object = $this->sut->get( 'the_id' );
|
|
||||||
|
|
||||||
$this->assertEquals( $replacement_object, $retrieved_object );
|
|
||||||
|
|
||||||
$this->assertEquals( $original_object, $object_passed_to_filter );
|
|
||||||
$this->assertEquals( array( 'data' => $original_object ), $data_passed_to_filter );
|
|
||||||
$this->assertEquals( 'the_id', $id_passed_to_filter );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @testdox 'remove' triggers the 'woocommerce_after_removing_{type}_from_cache' action.
|
|
||||||
*
|
|
||||||
* @testWith [true]
|
|
||||||
* [false]
|
|
||||||
*
|
|
||||||
* @param bool $operation_succeeds Whether the removal operation succeeds or not.
|
|
||||||
*/
|
|
||||||
public function test_action_triggered_on_object_removed_from_cache( bool $operation_succeeds ) {
|
|
||||||
$id_passed_to_action = null;
|
|
||||||
$result_passed_to_action = null;
|
|
||||||
|
|
||||||
add_action(
|
|
||||||
'woocommerce_after_removing_the_type_from_cache',
|
|
||||||
function( $id, $result ) use ( &$id_passed_to_action, &$result_passed_to_action ) {
|
|
||||||
$id_passed_to_action = $id;
|
|
||||||
$result_passed_to_action = $result;
|
|
||||||
},
|
|
||||||
10,
|
|
||||||
2
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->sut->set( 'the_id', array( 'foo' ) );
|
|
||||||
$this->sut->remove( $operation_succeeds ? 'the_id' : 'INVALID_ID' );
|
|
||||||
|
|
||||||
$this->assertEquals( $operation_succeeds ? 'the_id' : 'INVALID_ID', $id_passed_to_action );
|
|
||||||
$this->assertEquals( $operation_succeeds, $result_passed_to_action );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @testdox 'flush' triggers the 'woocommerce_after_flushing_{type}_cache' action.
|
|
||||||
*/
|
|
||||||
public function test_action_triggered_on_cache_flushed() {
|
|
||||||
$cache_passed_to_action = null;
|
|
||||||
$engine_passed_to_action = null;
|
|
||||||
|
|
||||||
add_action(
|
|
||||||
'woocommerce_after_flushing_the_type_cache',
|
|
||||||
function( $cache, $engine ) use ( &$cache_passed_to_action, &$engine_passed_to_action ) {
|
|
||||||
$cache_passed_to_action = $cache;
|
|
||||||
$engine_passed_to_action = $engine;
|
|
||||||
},
|
|
||||||
10,
|
|
||||||
2
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->sut->flush();
|
|
||||||
|
|
||||||
$this->assertEquals( $this->sut, $cache_passed_to_action );
|
|
||||||
$this->assertEquals( $this->cache_engine, $engine_passed_to_action );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue