diff --git a/plugins/woocommerce/src/Caching/CacheNameSpaceTrait.php b/plugins/woocommerce/src/Caching/CacheNameSpaceTrait.php index bf61e19a129..7c4f4cbb8c2 100644 --- a/plugins/woocommerce/src/Caching/CacheNameSpaceTrait.php +++ b/plugins/woocommerce/src/Caching/CacheNameSpaceTrait.php @@ -49,7 +49,7 @@ trait CacheNameSpaceTrait { * @since 3.9.0 */ public static function invalidate_cache_group( $group ) { - wp_cache_set( 'wc_' . $group . '_cache_prefix', microtime(), $group ); + return wp_cache_set( 'wc_' . $group . '_cache_prefix', microtime(), $group ); } /** diff --git a/plugins/woocommerce/src/Caching/ObjectCache.php b/plugins/woocommerce/src/Caching/ObjectCache.php index 23bc6728c71..e116997c38a 100644 --- a/plugins/woocommerce/src/Caching/ObjectCache.php +++ b/plugins/woocommerce/src/Caching/ObjectCache.php @@ -244,7 +244,6 @@ abstract class ObjectCache { if ( null === $object ) { return null; } - $this->set( $object, $id, $expiration ); $data = $this->last_cached_data; } diff --git a/plugins/woocommerce/src/Caching/WPCacheEngine.php b/plugins/woocommerce/src/Caching/WPCacheEngine.php index f3f2b7716cf..c59448dded9 100644 --- a/plugins/woocommerce/src/Caching/WPCacheEngine.php +++ b/plugins/woocommerce/src/Caching/WPCacheEngine.php @@ -63,6 +63,6 @@ class WPCacheEngine implements CacheEngine { * @return bool True if the group is deleted successfully, false otherwise. */ public function delete_cache_group( string $group = '' ): bool { - self::invalidate_cache_group( $group ); + return self::invalidate_cache_group( $group ); } } diff --git a/plugins/woocommerce/tests/php/src/Caching/CacheExceptionTest.php b/plugins/woocommerce/tests/php/src/Caching/CacheExceptionTest.php index 936d596e30d..3a6b3910078 100644 --- a/plugins/woocommerce/tests/php/src/Caching/CacheExceptionTest.php +++ b/plugins/woocommerce/tests/php/src/Caching/CacheExceptionTest.php @@ -26,7 +26,21 @@ class CacheExceptionTest extends \WC_Unit_Test_Case { public function get_object_type(): string { return 'the_type'; } - }; + + protected function get_object_id( $object ) { + } + + protected function validate( $object ): ?array { + } + + /** + * @param $id + * + * @return mixed + */ + protected function get_from_datastore( $id ) { + // TODO: Implement get_from_datastore() method. +}}; // phpcs:enable Squiz.Commenting } diff --git a/plugins/woocommerce/tests/php/src/Caching/InMemoryObjectCacheEngine.php b/plugins/woocommerce/tests/php/src/Caching/InMemoryObjectCacheEngine.php deleted file mode 100644 index 51d3ce2de0d..00000000000 --- a/plugins/woocommerce/tests/php/src/Caching/InMemoryObjectCacheEngine.php +++ /dev/null @@ -1,63 +0,0 @@ -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 -} diff --git a/plugins/woocommerce/tests/php/src/Caching/InvalidObjectCacheClass.php b/plugins/woocommerce/tests/php/src/Caching/InvalidObjectCacheClass.php index 42f0ba1620b..7cd3f413838 100644 --- a/plugins/woocommerce/tests/php/src/Caching/InvalidObjectCacheClass.php +++ b/plugins/woocommerce/tests/php/src/Caching/InvalidObjectCacheClass.php @@ -17,4 +17,25 @@ class InvalidObjectCacheClass extends ObjectCache { } // phpcs:enable Squiz.Commenting + + /** + * @inheritDoc + */ + protected function get_object_id( $object ) { + // TODO: Implement get_object_id() method. + } + + /** + * @inheritDoc + */ + protected function validate( $object ): ?array { + // TODO: Implement validate() method. + } + + /** + * @inheritDoc + */ + protected function get_from_datastore( $id ) { + // TODO: Implement get_from_datastore() method. + } } diff --git a/plugins/woocommerce/tests/php/src/Caching/ObjectCacheTest.php b/plugins/woocommerce/tests/php/src/Caching/ObjectCacheTest.php index 27979185713..c01ed5f5721 100644 --- a/plugins/woocommerce/tests/php/src/Caching/ObjectCacheTest.php +++ b/plugins/woocommerce/tests/php/src/Caching/ObjectCacheTest.php @@ -5,7 +5,7 @@ namespace Automattic\WooCommerce\Tests\Caching; use Automattic\WooCommerce\Caching\CacheException; use Automattic\WooCommerce\Caching\ObjectCache; use Automattic\WooCommerce\Caching\CacheEngine; -use Automattic\WooCommerce\Caching\WpCacheEngine; +use Automattic\WooCommerce\Caching\WPCacheEngine; /** * Tests for the ObjectCache class. @@ -19,23 +19,10 @@ class ObjectCacheTest extends \WC_Unit_Test_Case { */ private $sut; - /** - * The cache engine to use. - * - * @var CacheEngine - */ - private $cache_engine; - /** * Runs before each test. */ 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 @@ -50,9 +37,22 @@ class ObjectCacheTest extends \WC_Unit_Test_Case { $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 + + $this->sut->flush(); } /** @@ -152,12 +152,10 @@ class ObjectCacheTest extends \WC_Unit_Test_Case { $this->assertTrue( $result ); - $expected_prefix = 'woocommerce_object_cache|the_type|random_1|'; - $this->assertEquals( $expected_prefix, get_option( 'wp_object_cache_key_prefix_the_type' ) ); + $expected_prefix = \WC_Cache_Helper::get_cache_prefix( 'the_type' ); $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' ) ); } /** @@ -169,30 +167,12 @@ class ObjectCacheTest extends \WC_Unit_Test_Case { $object_2 = array( 1, 2, 3, 4 ); $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'; - $expected_cached = array( 'data' => $object_1 ); - $this->assertEquals( $expected_cached, $this->cache_engine->cache[ $key_1 ] ); + $this->assertEquals( $object_1, wp_cache_get( $key_1, 'the_type' ) ); $key_2 = $prefix . '9999'; - $expected_cached = array( 'data' => $object_2 ); - $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( array( 'foo' ), 'the_id' ); - $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( array( 'foo' ), 'the_id', 1234 ); - $this->assertEquals( 1234, $this->cache_engine->last_expiration ); + $this->assertEquals( $object_2, wp_cache_get( $key_2, 'the_type' ) ); } /** @@ -223,24 +203,28 @@ class ObjectCacheTest extends \WC_Unit_Test_Case { 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 ) ); - $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 'update_if_cached' does nothing if no object is cached with the passed (or obtained) id. - * - * @testWith [1234] - * [null] - * - * @param ?int $id Id to pass to update_if_cached. */ - public function test_update_if_cached_does_nothing_for_not_cached_id( ?int $id ) { + public function test_update_if_cached_does_nothing_for_not_cached_id() { + $id = 1234; // phpcs:disable Squiz.Commenting $sut = new class() extends ObjectCache { @@ -251,24 +235,30 @@ class ObjectCacheTest extends \WC_Unit_Test_Case { protected function get_object_id( $object ) { return $object['id']; } + + protected function validate( $object ): ?array { + return null; + } + + protected function get_from_datastore( $id ) { + return null; + } }; // phpcs:enable Squiz.Commenting $result = $sut->update_if_cached( array( 'id' => 1234 ), $id ); $this->assertFalse( $result ); - $this->assertEmpty( $this->cache_engine->cache ); + $this->assertEmpty( $sut->get( $id ) ); } /** * @testdox 'update_if_cached' updates an already cached object the same way as 'set'. * - * @testWith [1234] - * [null] - * * @param ?int $id Id to pass to update_if_cached. */ - public function test_update_if_cached_updates_already_cached_object( ?int $id ) { + public function test_update_if_cached_updates_already_cached_object() { + $id = 1234; // phpcs:disable Squiz.Commenting $sut = new class() extends ObjectCache { @@ -283,12 +273,20 @@ class ObjectCacheTest extends \WC_Unit_Test_Case { 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( 'woocommerce_object_cache|the_type|random|1234', array_keys( $this->cache_engine->cache )[0] ); + $this->assertEquals( array( 'id' => 1234 ), $sut->get( $id ) ); $new_value = array( 'id' => 1234, @@ -296,7 +294,7 @@ class ObjectCacheTest extends \WC_Unit_Test_Case { ); $result = $sut->update_if_cached( $new_value, $id ); $this->assertTrue( $result ); - $this->assertEquals( $this->cache_engine->cache['woocommerce_object_cache|the_type|random|1234'], array( 'data' => $new_value ) ); + $this->assertEquals( $new_value, $sut->get( $id ) ); } /** @@ -309,33 +307,6 @@ class ObjectCacheTest extends \WC_Unit_Test_Case { $this->sut->update_if_cached( array( 'foo' ) ); } - /** - * @testdox 'set' caches the value returned by 'serialize'. - */ - public function test_set_with_custom_serialization() { - $object = array( 'foo' ); - - // phpcs:disable Squiz.Commenting - - $sut = new class() extends ObjectCache { - public function get_object_type(): string { - return 'the_type'; - } - - protected function serialize( $object ): array { - return array( 'the_data' => $object ); - } - }; - - // phpcs:enable Squiz.Commenting - - $sut->set( $object, 1234 ); - - $cached = array_values( $this->cache_engine->cache )[0]; - $expected = array( 'the_data' => $object ); - $this->assertEquals( $expected, $cached ); - } - /** * @testdox 'set' throws an exception if 'validate' returns errors. * @@ -365,6 +336,14 @@ class ObjectCacheTest extends \WC_Unit_Test_Case { protected function validate( $object ): array { return $this->errors; } + + protected function get_from_datastore( $id ) { + return null; + } + + protected function get_object_id( $object ) { + return $object['id']; + } }; // phpcs:enable Squiz.Commenting @@ -446,26 +425,7 @@ class ObjectCacheTest extends \WC_Unit_Test_Case { $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 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 ); + $this->assertEquals( $expected, $this->sut->get( 'the_id' ) ); } /** @@ -482,6 +442,13 @@ class ObjectCacheTest extends \WC_Unit_Test_Case { protected function get_from_datastore( $id ) { return array( 'id' => $id ); } + + protected function get_object_id( $object ) { + } + + protected function validate( $object ) : ?array { + return null; + } }; // phpcs:enable Squiz.Commenting @@ -490,63 +457,7 @@ class ObjectCacheTest extends \WC_Unit_Test_Case { $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( array( 1, 2 ), 'the_id' ); - - $result = $sut->get( 'the_id' ); - $expected = array( 1, 2, 3 ); - $this->assertEquals( $expected, $result ); + $this->assertEquals( $expected, $sut->get( 'the_id' ) ); } /** @@ -572,13 +483,15 @@ class ObjectCacheTest extends \WC_Unit_Test_Case { public function test_flush() { $this->sut->set( array( 'foo' ), 'the_id' ); + $current_prefix_key = \WC_Cache_Helper::get_cache_prefix( 'the_type' ); $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( array( 'bar' ), 'the_id_2' ); - $expected_new_prefix = 'woocommerce_object_cache|the_type|random_2|'; - $this->assertEquals( $expected_new_prefix, get_option( 'wp_object_cache_key_prefix_the_type' ) ); + $this->assertEquals( $expected_new_prefix, \WC_Cache_Helper::get_cache_prefix( 'the_type' ) ); $this->assertFalse( $this->sut->is_cached( 'the_id' ) ); $this->assertTrue( $this->sut->is_cached( 'the_id_2' ) ); } @@ -587,7 +500,7 @@ class ObjectCacheTest extends \WC_Unit_Test_Case { * @testdox A custom cache engine instance can be used by overriding 'get_cache_engine_instance'. */ public function test_custom_cache_engine_via_protected_method() { - $engine = new InMemoryObjectCacheEngine(); + $engine = new WPCacheEngine(); // phpcs:disable Squiz.Commenting @@ -606,6 +519,16 @@ class ObjectCacheTest extends \WC_Unit_Test_Case { protected function get_cache_engine_instance(): CacheEngine { 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 @@ -613,18 +536,33 @@ class ObjectCacheTest extends \WC_Unit_Test_Case { $object = array( 'foo' ); $sut->set( $object, 'the_id' ); - $expected_cached = array( 'data' => $object ); - $this->assertEquals( $expected_cached, array_values( $engine->cache )[0] ); + $this->assertEquals( $object, $sut->get( 'the_id' ) ); } /** * @testdox A custom cache engine instance can be used via 'wc_object_cache_get_engine' filter. */ public function test_custom_cache_engine_via_hook() { - $engine = new InMemoryObjectCacheEngine(); + $engine = new class extends WPCacheEngine {}; $engine_passed_to_filter = null; $cache_passed_to_filter = null; + $sut = new class() extends ObjectCache { + 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 ) { + } + }; + add_filter( 'wc_object_cache_get_engine', function( $old_engine, $cache ) use ( $engine, &$engine_passed_to_filter, &$cache_passed_to_filter ) { @@ -637,131 +575,11 @@ class ObjectCacheTest extends \WC_Unit_Test_Case { ); $object = array( 'foo' ); - $this->sut->set( $object, 'the_id' ); + $sut->set( $object, 'the_id' ); - $expected_cached = array( 'data' => $object ); - $this->assertEquals( $expected_cached, array_values( $engine->cache )[0] ); + $this->assertEquals( $object, $sut->get( 'the_id' ) ); - $this->assertEquals( $engine_passed_to_filter, wc_get_container()->get( WpCacheEngine::class ) ); - $this->assertEquals( $cache_passed_to_filter, $this->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( $object, 'the_id' ); - - $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( $original_object, 'the_id' ); - $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( array( 'foo' ), 'the_id' ); - $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 ); + $this->assertEquals( $engine_passed_to_filter, wc_get_container()->get( WPCacheEngine::class ) ); + $this->assertEquals( $cache_passed_to_filter, $sut ); } }