From 422354ca8e79e594108ef3dddb030a2d90cf1fd1 Mon Sep 17 00:00:00 2001 From: CodeProKid Date: Thu, 25 Oct 2018 09:48:44 -0600 Subject: [PATCH 01/11] adding a new param to allow for using a modifier on object type storage --- includes/class-dfm-transients.php | 32 ++++++++++--- includes/template-tags.php | 74 +++++++++++++++++-------------- 2 files changed, 65 insertions(+), 41 deletions(-) diff --git a/includes/class-dfm-transients.php b/includes/class-dfm-transients.php index 4c42956..e584f64 100644 --- a/includes/class-dfm-transients.php +++ b/includes/class-dfm-transients.php @@ -38,6 +38,13 @@ class DFM_Transients { */ public $modifier = ''; + /** + * Stored the object ID where the transient data is stored + * + * @var int|null $object_id + */ + public $object_id = 0; + /** * The storage key for the transient * @@ -81,12 +88,13 @@ class DFM_Transients { /** * DFM_Transients constructor. * - * @param string $transient Name of the transient - * @param string|int $modifier Unique modifier for the transient + * @param string $transient Name of the transient + * @param string|int $modifier Unique modifier for the transient + * @param null|int $object_id The ID of the object the transient data is related to * * @throws Exception */ - function __construct( $transient, $modifier ) { + function __construct( $transient, $modifier, $object_id ) { global $dfm_transients; @@ -96,11 +104,20 @@ function __construct( $transient, $modifier ) { $this->transient = $transient; $this->modifier = $modifier; + $this->object_id = $object_id; $this->transient_object = $dfm_transients[ $this->transient ]; $this->key = $this->cache_key(); $this->lock_key = uniqid( 'dfm_lt_' ); $this->prefix = apply_filters( 'dfm_transient_prefix', 'dfm_transient_' ); + /** + * For backwards compatibility, use the modifier value as the object ID. + */ + if ( 'transient' !== $this->transient_object->cache_type && empty( $object_id ) ) { + $this->object_id = absint( $modifier ); + $this->modifier = ''; + } + } /** @@ -267,7 +284,7 @@ private function get_from_transient() { */ private function get_from_meta( $type ) { - $data = get_metadata( $type, $this->modifier, $this->key, true ); + $data = get_metadata( $type, $this->object_id, $this->key, true ); if ( empty( $data ) ) { $data_exists = metadata_exists( $type, $this->modifier, $this->key ); @@ -359,7 +376,7 @@ private function save_to_metadata( $data, $type ) { ); } - update_metadata( $type, $this->modifier, $this->key, $data ); + update_metadata( $type, $this->object_id, $this->key, $data ); } @@ -383,7 +400,7 @@ private function delete_from_transient() { * @access private */ private function delete_from_metadata( $type ) { - delete_metadata( $type, $this->modifier, $this->key ); + delete_metadata( $type, $this->object_id, $this->key ); } /** @@ -393,6 +410,7 @@ private function delete_from_metadata( $type ) { * * @access private * @return void + * @throws Exception */ private function facilitate_retry() { @@ -472,7 +490,7 @@ private function cache_key() { $key = $this->transient_object->key; - if ( 'transient' === $this->transient_object->cache_type && ! empty( $this->modifier ) ) { + if ( ! empty( $this->modifier ) ) { // Add the unique modifier to the key for regular transients $key = $key . '_' . $this->modifier; } diff --git a/includes/template-tags.php b/includes/template-tags.php index d727117..cd51b12 100644 --- a/includes/template-tags.php +++ b/includes/template-tags.php @@ -1,8 +1,6 @@ get(), $transient, $modifier ); @@ -116,16 +117,18 @@ function dfm_get_transient( $transient, $modifier = '' ) { } /** - * dfm_set_transient - * * Handles the setting of data to a particular transient * - * @param string $transient The name of the transient we would like to set data to - * @param string $data The data we want to set to the transient - * @param string|int $modifier The unique modifier for the transient. In the case of transients stored in metadata, - * this value should be the object ID related to this piece of metadata. + * @param string $transient The name of the transient we would like to set data to + * @param string $data The data we want to set to the transient + * @param string|int $modifier The unique modifier for the transient to be appended to the + * transient name for the key + * @param null|int $object_id ID of the object the transient data is related to + * + * @throws Exception + * @return void */ -function dfm_set_transient( $transient, $data, $modifier = '' ) { +function dfm_set_transient( $transient, $data, $modifier = '', $object_id = null ) { $transients = new DFM_Transients( @@ -133,8 +136,9 @@ function dfm_set_transient( $transient, $data, $modifier = '' ) { * Filters the name of the transient to set * * @param string $transient The name of the transient - * @param string $modifier The unique modifier - * @param mixed $data The data you want to save to your transient + * @param string $modifier The unique modifier + * @param mixed $data The data you want to save to your transient + * * @return string $transient The name of the transient to set data to */ apply_filters( 'dfm_transients_set_transient_name', $transient, $modifier, $data ), @@ -142,12 +146,14 @@ function dfm_set_transient( $transient, $data, $modifier = '' ) { /** * Filters the unique modifier for the transient * - * @param string $modifier The unique modifier + * @param string $modifier The unique modifier * @param string $transient The name of the transient we want to save data to - * @param mixed $data The data that we want to save to the transient + * @param mixed $data The data that we want to save to the transient + * * @return string $modifier The unique modifier for the transient */ - apply_filters( 'dfm_transients_set_transient_modifier', $modifier, $transient, $data ) + apply_filters( 'dfm_transients_set_transient_modifier', $modifier, $transient, $data ), + $object_id ); // Invoke the set method @@ -156,18 +162,17 @@ function dfm_set_transient( $transient, $data, $modifier = '' ) { } /** - * dfm_delete_transient - * * Handles the deletion of a single transient * - * @param string $transient Name of the transient you want to delete. Must match what is set when registering - * the transient. - * @param string|int $modifier Unique modifier for the transient that you want to delete. In the case of a transient stored - * in metadata it must be the object ID that the metadata is related to. + * @param string $transient Name of the transient you want to delete. Must match what is set + * when registering the transient. + * @param string|int $modifier Unique modifier for the transient that you want to delete. + * @param null|int $object_id ID of the object the transient data is related to + * * @return void - * @access public + * @throws Exception */ -function dfm_delete_transient( $transient, $modifier = '' ) { +function dfm_delete_transient( $transient, $modifier = '', $object_id = null ) { $transients = new DFM_Transients( @@ -187,7 +192,8 @@ function dfm_delete_transient( $transient, $modifier = '' ) { * @param string $transient Name of the transient to be deleted * @return string $modifier Unique modifier for the transient to be deleted */ - apply_filters( 'dfm_transients_delete_transient_modifier', $modifier, $transient ) + apply_filters( 'dfm_transients_delete_transient_modifier', $modifier, $transient ), + $object_id ); // Invoke the delete method From a625fe7454ac25110f36544bc24a680edea62dd0 Mon Sep 17 00:00:00 2001 From: CodeProKid Date: Thu, 8 Nov 2018 09:08:14 -0700 Subject: [PATCH 02/11] fixes #51 adding support for modifiers on object type cache --- includes/class-dfm-async-handler.php | 11 ++++++++--- includes/class-dfm-transient-hook.php | 16 +++++++++++++--- includes/class-dfm-transient-scheduler.php | 9 +++++++-- includes/class-dfm-transients.php | 16 ++++++++++------ 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/includes/class-dfm-async-handler.php b/includes/class-dfm-async-handler.php index 49bdf72..007354f 100644 --- a/includes/class-dfm-async-handler.php +++ b/includes/class-dfm-async-handler.php @@ -35,13 +35,17 @@ class DFM_Async_Handler { * DFM_Async_Handler constructor. * * @param string $transient Name of the transient - * @param string $modifier Unique modifier for the transient - * @param string $lock_key Key for matching the update locking + * @param string $modifier Unique modifier for the transient + * @param int $object_id ID of the object where the transient data is stored + * @param string $lock_key Key for matching the update locking + * + * @return void */ - function __construct( $transient, $modifier, $lock_key = '' ) { + function __construct( $transient, $modifier, $object_id = 0, $lock_key = '' ) { $this->transient_name = $transient; $this->modifier = $modifier; + $this->object_id = $object_id; $this->lock_key = $lock_key; // Spawn the event on shutdown so we are less likely to run into timeouts, or block other processes add_action( 'shutdown', array( $this, 'spawn_event' ) ); @@ -73,6 +77,7 @@ public function spawn_event() { 'body' => array( 'transient_name' => $this->transient_name, 'modifier' => $this->modifier, + 'object_id' => $this->object_id, 'action' => 'dfm_' . $this->transient_name, '_nonce' => $nonce, 'async_action' => true, diff --git a/includes/class-dfm-transient-hook.php b/includes/class-dfm-transient-hook.php index 7089084..c17ce9b 100644 --- a/includes/class-dfm-transient-hook.php +++ b/includes/class-dfm-transient-hook.php @@ -70,9 +70,9 @@ private function add_hook( $hook ) { * @return void * @access private */ - private function run_update( $modifier ) { + private function run_update( $modifier, $object_id = null ) { - $transient_obj = new DFM_Transients( $this->transient, $modifier ); + $transient_obj = new DFM_Transients( $this->transient, $modifier, $object_id ); // Bail if another process is already trying to update this transient. if ( $transient_obj->is_locked() && ! $transient_obj->owns_lock( '' ) ) { @@ -83,7 +83,7 @@ private function run_update( $modifier ) { $transient_obj->lock_update(); } - $data = call_user_func( $transient_obj->transient_object->callback, $modifier ); + $data = call_user_func( $transient_obj->transient_object->callback, $modifier, $object_id ); $transient_obj->set( $data ); $transient_obj->unlock_update(); @@ -122,6 +122,11 @@ public function spawn() { // If we have an array of modifiers, update each of them. if ( is_array( $modifiers ) ) { foreach ( $modifiers as $modifier ) { + if ( is_array( $modifier ) ) { + foreach ( $modifier as $object_id => $key_modifier ) { + new DFM_Async_Handler( $this->transient, $modifier, $object_id ); + } + } new DFM_Async_Handler( $this->transient, $modifier ); } } else { @@ -134,6 +139,11 @@ public function spawn() { // If we have an array of modifiers, update each of them. if ( is_array( $modifiers ) ) { foreach ( $modifiers as $modifier ) { + if ( is_array( $modifier ) ) { + foreach ( $modifier as $object_id => $key_modifier ) { + $this->run_update( $modifier, $object_id ); + } + } $this->run_update( $modifier ); } } else { diff --git a/includes/class-dfm-transient-scheduler.php b/includes/class-dfm-transient-scheduler.php index 306385f..30c7e73 100644 --- a/includes/class-dfm-transient-scheduler.php +++ b/includes/class-dfm-transient-scheduler.php @@ -90,6 +90,7 @@ public function run_update() { $transient_name = empty( $_POST['transient_name'] ) ? false : sanitize_text_field( $_POST['transient_name'] ); $modifier = empty( $_POST['modifier'] ) ? '' : sanitize_text_field( $_POST['modifier'] ); + $object_id = empty( $_POST['object_id'] ) ? null : sanitize_text_field( $_POST['object_id'] ); $nonce = empty( $_POST['_nonce'] ) ? '' : sanitize_text_field( $_POST['_nonce'] ); $lock_key = empty( $_POST['lock_key'] ) ? '' : sanitize_text_field( $_POST['lock_key'] ); @@ -105,7 +106,11 @@ public function run_update() { return; } - $transient_obj = new DFM_Transients( $transient_name, $modifier ); + $transient_obj = new DFM_Transients( $transient_name, $modifier, $object_id ); + + if ( 'transient' !== $transient_obj->transient_object->cache_type && empty( $object_id ) ) { + $object_id = absint( $modifier ); + } // Bail if another process is already trying to update this transient. if ( $transient_obj->is_locked() && ! $transient_obj->owns_lock( $lock_key ) ) { @@ -116,7 +121,7 @@ public function run_update() { $transient_obj->lock_update(); } - $data = call_user_func( $transient_obj->transient_object->callback, $modifier ); + $data = call_user_func( $transient_obj->transient_object->callback, $modifier, $object_id ); $transient_obj->set( $data ); $transient_obj->unlock_update(); diff --git a/includes/class-dfm-transients.php b/includes/class-dfm-transients.php index e584f64..7dc2075 100644 --- a/includes/class-dfm-transients.php +++ b/includes/class-dfm-transients.php @@ -94,7 +94,7 @@ class DFM_Transients { * * @throws Exception */ - function __construct( $transient, $modifier, $object_id ) { + function __construct( $transient, $modifier, $object_id = null ) { global $dfm_transients; @@ -106,18 +106,22 @@ function __construct( $transient, $modifier, $object_id ) { $this->modifier = $modifier; $this->object_id = $object_id; $this->transient_object = $dfm_transients[ $this->transient ]; - $this->key = $this->cache_key(); $this->lock_key = uniqid( 'dfm_lt_' ); - $this->prefix = apply_filters( 'dfm_transient_prefix', 'dfm_transient_' ); + $this->prefix = apply_filters( 'dfm_transient_prefix', 'dfm_transient_', $this->transient, $this->modifier, $this->object_id ); /** - * For backwards compatibility, use the modifier value as the object ID. + * For backwards compatibility, use the modifier value as the object ID if no ID is supplied, but it's an object type cache. */ if ( 'transient' !== $this->transient_object->cache_type && empty( $object_id ) ) { $this->object_id = absint( $modifier ); $this->modifier = ''; } + /** + * Generate the cache key after we've applied the backward compat fix above ^ + */ + $this->key = $this->cache_key(); + } /** @@ -314,9 +318,9 @@ private function get_transient_data( $data ) { } elseif ( $this->is_expired( $data ) && ! $this->is_locked() ) { $this->lock_update(); if ( $this->should_soft_expire() ) { - new DFM_Async_Handler( $this->transient, $this->modifier, $this->lock_key ); + new DFM_Async_Handler( $this->transient, $this->modifier, $this->object_id, $this->lock_key ); } else { - $data = call_user_func( $this->transient_object->callback, $this->modifier ); + $data = call_user_func( $this->transient_object->callback, $this->modifier, $this->object_id ); $this->set( $data ); $this->unlock_update(); } From e212a7e4133e5c71c76a4e31de0989c7fabe6e85 Mon Sep 17 00:00:00 2001 From: CodeProKid Date: Fri, 16 Nov 2018 14:15:33 -0700 Subject: [PATCH 03/11] adding new utils class --- includes/class-dfm-transient-utils.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 includes/class-dfm-transient-utils.php diff --git a/includes/class-dfm-transient-utils.php b/includes/class-dfm-transient-utils.php new file mode 100644 index 0000000..f0fd5fd --- /dev/null +++ b/includes/class-dfm-transient-utils.php @@ -0,0 +1,26 @@ + Date: Fri, 16 Nov 2018 14:16:08 -0700 Subject: [PATCH 04/11] Updating readme for changes with --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1b70d8a..3e77b63 100644 --- a/README.md +++ b/README.md @@ -58,9 +58,12 @@ dfm_transient_meta_update_cb( $args ) { } ``` ## Retrieve the transient data -You can use the `dfm_get_transient()` function to retrieve the data for a transient. The first parameter passed to this function is the name of the transient you are trying to retrieve (should be the same name that you registered the transient with). The second parameter is the "modifier" for the transient. You can read more about modifiers below. +You can use the `dfm_get_transient()` function to retrieve the data for a transient. The first parameter passed to this function is the name of the transient you are trying to retrieve (should be the same name that you registered the transient with). The second parameter is the "modifier" for the transient. You can read more about modifiers below. The third parameter is the object_id (if you are using post_meta, term_meta, or user_meta as the cache type). ```php -$result = dfm_get_transient( 'sample_transient', '' ); +$result = dfm_get_transient( 'sample_transient' ); +``` +```php +$post_transient = dfm_get_transient( 'sample_transient', '', $post_id ); ``` ## Arguments for registering a transient @@ -78,6 +81,8 @@ $result = dfm_get_transient( 'sample_transient', '' ); 8. **soft_expiration** (bool) - Whether or not the data should soft expire or not. If this is set to true, it will check to see if the data has expired when retrieving it. If it is expired, it will spawn a background process to update the transient data. *Default: False* ## Transient Modifier +The Transient modifier (second parameter passed to the `dfm_get_transient` function) is used to store variations of the same type of transient. It will append the $modifier to the end of the transient key. This way you could store and retrieve different variations of the same transient that are mostly the same without registering a whole new transient. You can use the modifier to change the data saved to the transient by using it to alter your logic in your callback (the modifier is passed as the first argument to your callback function). + The transient modifier, (second parameter passed to the `dfm_get_transient` function) is used in a variety of different ways throughout this library. For a transient stored in metadata, it will be used as the object ID the transient is attached to. It will be used when using the `get_metadata()` and `save_metadata()` functions, so it is crucial that it is passed for transients stored in metadata. For global transients, it can be used to store variations of the same type of transient. It will append the `$modifier` to the end of the transient key. This way you could store and retrieve different variations of the same transient that are mostly the same without registering a whole new transient. You can use the modifier to change the data saved to the transient by using it to alter your logic in your callback (the modifier is passed as the only argument to your callback function). ## Debugging From d0b0e01f31795b5a5c0947026ae914782e9f945f Mon Sep 17 00:00:00 2001 From: CodeProKid Date: Fri, 16 Nov 2018 14:16:28 -0700 Subject: [PATCH 05/11] Including new Utils class --- dfm-transients.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dfm-transients.php b/dfm-transients.php index ad33872..253b914 100644 --- a/dfm-transients.php +++ b/dfm-transients.php @@ -12,6 +12,7 @@ exit; } +require_once( plugin_dir_path( __FILE__ ) . 'includes/class-dfm-transient-utils.php' ); require_once( plugin_dir_path( __FILE__ ) . 'includes/class-dfm-async-nonce.php' ); require_once( plugin_dir_path( __FILE__ ) . 'includes/class-dfm-async-handler.php' ); require_once( plugin_dir_path( __FILE__ ) . 'includes/class-dfm-transient-hook.php' ); From 780f9020c4e06dfb97d931afeb40472af5eaace8 Mon Sep 17 00:00:00 2001 From: CodeProKid Date: Fri, 16 Nov 2018 14:17:00 -0700 Subject: [PATCH 06/11] Adding phpunit as a dev dependency to run tests --- composer.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 7a663bf..e83ca6b 100644 --- a/composer.json +++ b/composer.json @@ -3,5 +3,8 @@ "type": "wordpress-plugin", "description": "Get better control over your transients", "homepage": "https://github.com/dfmedia/DFM-Transients", - "license": "GPLv3" + "license": "GPLv3", + "require-dev": { + "phpunit/phpunit": "6.1.*" + } } From f62e60f92c49615966f4a0a3919e5c7eca4c1c36 Mon Sep 17 00:00:00 2001 From: CodeProKid Date: Fri, 16 Nov 2018 14:18:22 -0700 Subject: [PATCH 07/11] Adding support for modifiers on object type caches --- includes/class-dfm-transient-hook.php | 102 +++++++++------ includes/class-dfm-transient-scheduler.php | 4 +- includes/class-dfm-transients.php | 137 +++++++++++++++------ 3 files changed, 165 insertions(+), 78 deletions(-) diff --git a/includes/class-dfm-transient-hook.php b/includes/class-dfm-transient-hook.php index c17ce9b..2d80e05 100644 --- a/includes/class-dfm-transient-hook.php +++ b/includes/class-dfm-transient-hook.php @@ -14,7 +14,15 @@ class DFM_Transient_Hook { * @var string * @access private */ - private $transient; + private $transient_name; + + /** + * The settings registered to the transient + * + * @var object $transient_obj + * @access private + */ + private $transient_obj; /** * Name of hook we want to hook into @@ -32,20 +40,31 @@ class DFM_Transient_Hook { */ private $callback; + /** + * Whether or not the update should happen asynchronously + * + * @var bool $async + * @access private + * + */ + private $async = false; + /** * DFM_Transient_Hook constructor. * - * @param string $transient - * @param string $hook - * @param bool $async_update - * @param string $callback + * @param string $transient_name Name of the transient to add the hook callback for + * @param object $transient_obj Settings that were added when the transient was registered + * @param string $hook Name of the hook + * @param bool $async_update Whether or not the update should happen asynchronously or not + * @param string $callback The callable method to be added to the hook */ - public function __construct( $transient, $hook, $async_update, $callback = '' ) { + public function __construct( $transient_name, $transient_obj, $hook, $async_update, $callback = '' ) { - $this->transient = $transient; - $this->hook = $hook; - $this->callback = $callback; - $this->async = $async_update; + $this->transient_name = $transient_name; + $this->transient_obj = $transient_obj; + $this->hook = $hook; + $this->callback = $callback; + $this->async = $async_update; $this->add_hook( $hook ); @@ -72,7 +91,12 @@ private function add_hook( $hook ) { */ private function run_update( $modifier, $object_id = null ) { - $transient_obj = new DFM_Transients( $this->transient, $modifier, $object_id ); + if ( $this->async ) { + new DFM_Async_Handler( $this->transient_name, $modifier, $object_id ); + return; + } + + $transient_obj = new DFM_Transients( $this->transient_name, $modifier, $object_id ); // Bail if another process is already trying to update this transient. if ( $transient_obj->is_locked() && ! $transient_obj->owns_lock( '' ) ) { @@ -84,6 +108,7 @@ private function run_update( $modifier, $object_id = null ) { } $data = call_user_func( $transient_obj->transient_object->callback, $modifier, $object_id ); + $transient_obj->set( $data ); $transient_obj->unlock_update(); @@ -116,40 +141,45 @@ public function spawn() { } } - // If we are running an async task, instantiate a new async handler. - if ( $this->async ) { - - // If we have an array of modifiers, update each of them. - if ( is_array( $modifiers ) ) { - foreach ( $modifiers as $modifier ) { - if ( is_array( $modifier ) ) { - foreach ( $modifier as $object_id => $key_modifier ) { - new DFM_Async_Handler( $this->transient, $modifier, $object_id ); - } + if ( is_array( $modifiers ) && ! empty( $modifiers ) ) { + foreach ( $modifiers as $key => $modifier ) { + if ( is_array( $modifier ) && ! empty( $modifier ) ) { + foreach ( $modifier as $key_modifier ) { + $this->dispatch_update( $key_modifier, $key ); } - new DFM_Async_Handler( $this->transient, $modifier ); + } else { + $this->dispatch_update( $modifier, $key ); } - } else { - new DFM_Async_Handler( $this->transient, $modifiers ); } - - // Run the update in real time if we aren't using an async process } else { + $this->dispatch_update( $modifiers ); + } - // If we have an array of modifiers, update each of them. - if ( is_array( $modifiers ) ) { - foreach ( $modifiers as $modifier ) { - if ( is_array( $modifier ) ) { - foreach ( $modifier as $object_id => $key_modifier ) { - $this->run_update( $modifier, $object_id ); - } + } + + private function dispatch_update( $modifier, $object_id = null ) { + + if ( 'transient' !== $this->transient_obj->cache_type ) { + + if ( empty( $object_id ) ) { + $object_id = absint( $modifier ); + $translated_obj_id = true; + } + + if ( empty( $modifier ) || isset( $translated_obj_id ) ) { + $modifier_map = DFM_Transients::get_meta_map( DFM_Transient_Utils::get_meta_type( $this->transient_obj->cache_type ), $object_id, $this->transient_obj->key ); + if ( ! empty( $modifier_map ) && is_array( $modifier_map ) ) { + foreach ( $modifier_map as $modifier => $modifier_key ) { + $this->run_update( $modifier, $object_id ); } - $this->run_update( $modifier ); + } else { + $this->run_update( '', $object_id ); } } else { - $this->run_update( $modifiers ); + $this->run_update( $modifier, $object_id ); } - + } else { + $this->run_update( $modifier ); } } diff --git a/includes/class-dfm-transient-scheduler.php b/includes/class-dfm-transient-scheduler.php index 30c7e73..d9b8ea8 100644 --- a/includes/class-dfm-transient-scheduler.php +++ b/includes/class-dfm-transient-scheduler.php @@ -53,10 +53,10 @@ public function get_transients() { // If there are multiple hooks where this should fire, loop through all of them, and build a hook for each. if ( is_array( $transient_args->update_hooks ) ) { foreach ( $transient_args->update_hooks as $hook_name => $callback ) { - new DFM_Transient_Hook( $transient_id, $hook_name, $async_update, $callback ); + new DFM_Transient_Hook( $transient_id, $transient_args, $hook_name, $async_update, $callback ); } } else { - new DFM_Transient_Hook( $transient_id, $transient_args->update_hooks, $async_update ); + new DFM_Transient_Hook( $transient_id, $transient_args, $transient_args->update_hooks, $async_update ); } } diff --git a/includes/class-dfm-transients.php b/includes/class-dfm-transients.php index 7dc2075..dc5f8d6 100644 --- a/includes/class-dfm-transients.php +++ b/includes/class-dfm-transients.php @@ -12,7 +12,7 @@ class DFM_Transients { * @var string * @access private */ - private $prefix = 'dfm_transient_'; + private static $prefix = 'dfm_transient_'; /** * Name of the transient @@ -45,6 +45,14 @@ class DFM_Transients { */ public $object_id = 0; + /** + * Modifier added for backwards compatibility + * + * @var string $bc_modifier + * @access private + */ + private $bc_modifier = ''; + /** * The storage key for the transient * @@ -107,13 +115,14 @@ function __construct( $transient, $modifier, $object_id = null ) { $this->object_id = $object_id; $this->transient_object = $dfm_transients[ $this->transient ]; $this->lock_key = uniqid( 'dfm_lt_' ); - $this->prefix = apply_filters( 'dfm_transient_prefix', 'dfm_transient_', $this->transient, $this->modifier, $this->object_id ); + self::$prefix = apply_filters( 'dfm_transient_prefix', 'dfm_transient_', $this->transient, $this->modifier, $this->object_id ); /** * For backwards compatibility, use the modifier value as the object ID if no ID is supplied, but it's an object type cache. */ if ( 'transient' !== $this->transient_object->cache_type && empty( $object_id ) ) { $this->object_id = absint( $modifier ); + $this->bc_modifier = $modifier; $this->modifier = ''; } @@ -132,6 +141,17 @@ function __construct( $transient, $modifier, $object_id = null ) { */ public function get() { + if ( 'transient' === $this->transient_object->cache_type ) { + $this->get_from_transient(); + } else { + $meta_type = DFM_Transient_Utils::get_meta_type( $this->transient_object->cache_type ); + if ( is_wp_error( $meta_type ) ) { + return $meta_type; + } else { + $this->get_from_meta( $meta_type ); + } + } + switch ( $this->transient_object->cache_type ) { case 'transient': return $this->get_from_transient(); @@ -156,7 +176,7 @@ public function get() { * * @param string|array $data The data to add to the transient * @access public - * @return void + * @return mixed|WP_Error|Void * @throws Exception */ public function set( $data ) { @@ -166,21 +186,15 @@ public function set( $data ) { return; } - switch ( $this->transient_object->cache_type ) { - case 'transient': - $this->save_to_transient( $data ); - break; - case 'post_meta': - $this->save_to_metadata( $data, 'post' ); - break; - case 'term_meta': - $this->save_to_metadata( $data, 'term' ); - break; - case 'user_meta': - $this->save_to_metadata( $data, 'user' ); - break; - default: - throw new Exception( __( 'When registering your transient, you used an invalid cache type. Valid options are transient, post_meta, term_meta.', 'dfm-transients' ) ); + if ( 'transient' === $this->transient_object->cache_type ) { + $this->save_to_transient( $data ); + } else { + $meta_type = DFM_Transient_Utils::get_meta_type( $this->transient_object->cache_type ); + if ( is_wp_error( $meta_type ) ) { + return $meta_type; + } else { + $this->save_to_metadata( $data, $meta_type ); + } } } @@ -188,27 +202,21 @@ public function set( $data ) { /** * This method handles the deletion of a transient * - * @return void + * @return mixed|void|WP_Error * @access public * @throws Exception */ public function delete() { - switch ( $this->transient_object->cache_type ) { - case 'transient': - $this->delete_from_transient(); - break; - case 'post_meta': - $this->delete_from_metadata( 'post' ); - break; - case 'term_meta': - $this->delete_from_metadata( 'term' ); - break; - case 'user_meta': - $this->delete_from_metadata( 'user' ); - break; - default: - throw new Exception( __( 'When registering your transient, you used an invalid cache type. Valid options are transient, post_meta, term_meta.', 'dfm-transients' ) ); + if ( 'transient' === $this->transient_object->cache_type ) { + $this->delete_from_transient(); + } else { + $meta_type = DFM_Transient_Utils::get_meta_type( $this->transient_object->cache_type ); + if ( is_wp_error( $meta_type ) ) { + return $meta_type; + } else { + $this->delete_from_metadata( $meta_type ); + } } } @@ -308,19 +316,26 @@ private function get_from_meta( $type ) { */ private function get_transient_data( $data ) { + // Check to see if we set a backwards compatible modifier + if ( 'transient' !== $this->transient_object->cache_type && ! empty( $this->bc_modifier ) ) { + $modifier = $this->bc_modifier; + } else { + $modifier = $this->modifier; + } + if ( false === $data || ( defined( 'DFM_TRANSIENTS_HOT_RELOAD' ) && true === DFM_TRANSIENTS_HOT_RELOAD ) ) { if ( true === $this->doing_retry() ) { return false; } - $data = call_user_func( $this->transient_object->callback, $this->modifier ); + $data = call_user_func( $this->transient_object->callback, $modifier, $this->object_id ); $this->set( $data ); } elseif ( $this->is_expired( $data ) && ! $this->is_locked() ) { $this->lock_update(); if ( $this->should_soft_expire() ) { - new DFM_Async_Handler( $this->transient, $this->modifier, $this->object_id, $this->lock_key ); + new DFM_Async_Handler( $this->transient, $modifier, $this->object_id, $this->lock_key ); } else { - $data = call_user_func( $this->transient_object->callback, $this->modifier, $this->object_id ); + $data = call_user_func( $this->transient_object->callback, $modifier, $this->object_id ); $this->set( $data ); $this->unlock_update(); } @@ -380,7 +395,11 @@ private function save_to_metadata( $data, $type ) { ); } - update_metadata( $type, $this->object_id, $this->key, $data ); + $r = update_metadata( $type, $this->object_id, $this->key, $data ); + + if ( ! empty( $this->modifier ) ) { + $this->add_meta_map( $type, $this->transient_object->key ); + } } @@ -404,9 +423,47 @@ private function delete_from_transient() { * @access private */ private function delete_from_metadata( $type ) { + if ( ! empty( $this->modifier ) ) { + $meta_map = $this->get_meta_map( $type, $this->object_id, $this->key ); + if ( ! empty( $meta_map ) && is_array( $meta_map ) ) { + foreach ( $meta_map as $key ) { + delete_metadata( $type, $this->object_id, $key ); + } + } + } delete_metadata( $type, $this->object_id, $this->key ); } + private function add_meta_map( $type, $transient_key ) { + + $map = get_metadata( $type, $this->object_id, self::meta_map_key( $transient_key ), true ); + if ( ! empty( $map ) ) { + $map[ $this->modifier ] = $this->key; + } else { + $map = [ $this->modifier => $this->key ]; + } + + update_metadata( $type, $this->object_id, $this->meta_map_key( $transient_key ), array_unique( $map ) ); + + } + + public static function get_meta_map( $type, $object_id, $transient_key ) { + + if ( is_wp_error( $type ) ) { + return; + } + + $map = get_metadata( $type, $object_id, self::meta_map_key( $transient_key ), true ); + if ( empty( $map ) ) { + $map = []; + } + return $map; + } + + public static function meta_map_key( $transient_key ) { + return self::$prefix . $transient_key . '_map'; + } + /** * If a callback function fails to return the correct data, this will store the stale data back into the * transient, and then set the expiration of the data at an exponential scale, so we are not constantly @@ -477,7 +534,7 @@ private function hash_key( $key ) { * If the storage type is *_meta then prepend the prefix after we hash so we can * still find it for debugging */ - $hashed_key = $this->prefix . $hashed_key; + $hashed_key = self::$prefix . $hashed_key; } return $hashed_key; @@ -505,7 +562,7 @@ private function cache_key() { if ( in_array( $this->transient_object->cache_type, $this->meta_types, true ) ) { // Add the prefix to transients stored in meta only so they can be identified - $key = $this->prefix . $key; + $key = self::$prefix . $key; } return $key; From 0efb9b64c4f12197453ba87c4da70a2eacb228d9 Mon Sep 17 00:00:00 2001 From: CodeProKid Date: Fri, 16 Nov 2018 14:18:34 -0700 Subject: [PATCH 08/11] Adding and adjusting tests --- tests/test-class-dfm-transient-hook.php | 196 +++++++++++++++++++++++- tests/test-class-dfm-transients.php | 175 ++++++++++++++++++--- 2 files changed, 340 insertions(+), 31 deletions(-) diff --git a/tests/test-class-dfm-transient-hook.php b/tests/test-class-dfm-transient-hook.php index 555cb68..d6d15dc 100644 --- a/tests/test-class-dfm-transient-hook.php +++ b/tests/test-class-dfm-transient-hook.php @@ -5,7 +5,14 @@ class Test_Class_DFM_Transient_Hook extends WP_UnitTestCase { * Test to make sure the hooks get setup correctly */ public function testHookAddition() { - $hook_obj = new DFM_Transient_Hook( 'testName', 'testHook', true ); + + $transient_object = [ + 'cache_type' => 'transient', + 'callback' => '__return_false', + 'key' => 'test', + ]; + + $hook_obj = new DFM_Transient_Hook( 'testName', (object) $transient_object, 'testHook', true ); $this->assertTrue( has_filter( 'testHook' ) ); } @@ -22,7 +29,10 @@ public function testNonAsyncHandlerUpdate() { }, ] ); - $hook_obj = new DFM_Transient_Hook( $transient_name, 'testHook', false ); + global $dfm_transients; + $transient_obj = $dfm_transients[ $transient_name ]; + + $hook_obj = new DFM_Transient_Hook( $transient_name, $transient_obj, 'testHook', false ); $hook_obj->spawn(); @@ -42,12 +52,15 @@ public function testUpdateFailureBecauseOfLock() { }, ] ); + global $dfm_transients; + $transient_obj = $dfm_transients[ $transient_name ]; + /** * Lock the update manually */ set_transient( 'dfm_lt_' . $transient_name, '1234' ); - $hook_obj = new DFM_Transient_Hook( $transient_name, 'testHook', false ); + $hook_obj = new DFM_Transient_Hook( $transient_name, $transient_obj, 'testHook', false ); $hook_obj->spawn(); $this->assertFalse( get_transient( $transient_name ) ); @@ -66,7 +79,10 @@ public function testNonAsyncUpdateMultipleModifiers() { }, ] ); - $hook_obj = new DFM_Transient_Hook( $transient_name, 'testHook', false, function() { + global $dfm_transients; + $transient_obj = $dfm_transients[ $transient_name ]; + + $hook_obj = new DFM_Transient_Hook( $transient_name, $transient_obj, 'testHook', false, function() { return [ 1, 2, 3 ]; } ); @@ -85,7 +101,10 @@ public function testShortCircuitHookUpdate() { $transient_name = 'testShortCircuitHookUpdate'; dfm_register_transient( $transient_name, [ 'callback' => '__return_true' ] ); - $hook_obj = new DFM_Transient_Hook( $transient_name, 'testHook', false, function() { + global $dfm_transients; + $transient_obj = $dfm_transients[ $transient_name ]; + + $hook_obj = new DFM_Transient_Hook( $transient_name, $transient_obj, 'testHook', false, function() { return false; } ); @@ -100,7 +119,12 @@ public function testShortCircuitHookUpdate() { */ public function testAsyncHandlerUpdate() { - $hook_obj = new DFM_Transient_Hook( 'testTransient', 'testHook', true ); + $transient_object = [ + 'cache_type' => 'transient', + 'callback' => '__return_false', + 'key' => 'test', + ]; + $hook_obj = new DFM_Transient_Hook( 'testTransient', (object) $transient_object, 'testHook', true ); $hook_obj->spawn(); global $wp_filter; @@ -113,7 +137,12 @@ public function testAsyncHandlerUpdate() { */ public function testMultipleAsyncHandlerUpdates() { - $hook_obj = new DFM_Transient_Hook( 'testTransient', 'testHook', true, function() { + $transient_object = [ + 'cache_type' => 'transient', + 'callback' => '__return_false', + 'key' => 'test', + ]; + $hook_obj = new DFM_Transient_Hook( 'testTransient', (object) $transient_object, 'testHook', true, function() { return [ 1, 2, 3 ]; } ); @@ -124,4 +153,157 @@ public function testMultipleAsyncHandlerUpdates() { } + /** + * Test that the hook handler returns the proper data to the callback for post object cache transients without modifiers + */ + public function testPostObjectCacheHandlerUpdate() { + + $transient_name = 'testPostObjectCacheHandlerUpdate'; + $post_id = $this->factory->post->create(); + $return_string = 'Post ID: %d, Modifier: %s'; + dfm_register_transient( $transient_name, [ + 'cache_type' => 'post_meta', + 'callback' => function( $modifier, $post_id ) use ( $return_string ) { + return sprintf( $return_string, $post_id, $modifier ); + } + ] ); + + global $dfm_transients; + $transient_obj = $dfm_transients[ $transient_name ]; + + $hook_obj = new DFM_Transient_Hook( $transient_name, (object) $transient_obj, $transient_name, false, function( $id ) { + return $id[0]; + } ); + + do_action( $transient_name, $post_id ); + + $expected = sprintf( $return_string, $post_id, '' ); + $transient_obj = new DFM_Transients( $transient_name, '', $post_id ); + $actual = get_post_meta( $post_id, $transient_obj->key, true ); + $this->assertEquals( $expected, $actual ); + $this->assertEquals( [], DFM_Transients::get_meta_map( 'post', $post_id, $transient_name ) ); + $this->assertFalse( metadata_exists( 'post', $post_id, 'dfm_transient_' . $transient_name . '_map' ) ); + + } + + /** + * Test that all transients within a transient group are refreshed when just the object ID is returned from the hook callback + */ + public function testTermObjectCacheHandlerWithModifiers() { + + $transient_name = 'testTermObjectCacheHandlerWithModifiers'; + $term_id = $this->factory->category->create(); + $return_string = 'Term ID: %d, Modifier: %s'; + + dfm_register_transient( $transient_name, [ + 'cache_type' => 'term_meta', + 'callback' => function( $modifier, $term_id ) use ( $return_string ) { + return sprintf( $return_string, $term_id, $modifier ); + }, + 'hash_key' => true, + ] ); + + $modifiers = [ + 'rainbow', + 'apples', + 'unicorn', + ]; + + /** + * Add some data to start out with, so we get a map. + */ + foreach ( $modifiers as $modifier ) { + $transient_data = dfm_get_transient( $transient_name, $modifier, $term_id ); + $this->assertEquals( sprintf( $return_string, $term_id, $modifier ), $transient_data ); + } + + $transient_obj = new DFM_Transients( $transient_name, $modifiers[0], $term_id ); + $transient_obj->delete(); + + global $dfm_transients; + $transient_obj = $dfm_transients[ $transient_name ]; + + $hook_obj = new DFM_Transient_Hook( $transient_name, (object) $transient_obj, $transient_name, false, function( $term_id ) { + return $term_id[0]; + } ); + + do_action( $transient_name, $term_id ); + + $transient_keys = DFM_Transients::get_meta_map( 'term', $term_id, $transient_obj->key ); + $this->assertEquals( 3, count( $transient_keys ) ); + + foreach ( $transient_keys as $modifier => $key ) { + $this->assertEquals( sprintf( $return_string, $term_id, $modifier ), get_term_meta( $term_id, $key, true ) ); + } + + } + + public function testUserObjectCacheHandlerWithModifiers() { + + $transient_name = 'testUserObjectCacheHandlerWithModifiers'; + $users = [ + $this->factory->user->create(), + $this->factory->user->create(), + $this->factory->user->create(), + $this->factory->user->create(), + ]; + $return_string = 'User ID: %d, Modifier: %s'; + + dfm_register_transient( $transient_name, [ + 'cache_type' => 'user_meta', + 'callback' => function( $modifier, $user_id ) use ( $return_string ){ + return sprintf( $return_string, $user_id, $modifier ); + } + ] ); + + $modifiers = [ + 'blue', + 'purple', + 'test', + ]; + + foreach ( $users as $user ) { + foreach( $modifiers as $modifier ) { + dfm_set_transient( $transient_name, 'test', $modifier, $user ); + } + } + + global $dfm_transients; + $transient_obj = $dfm_transients[ $transient_name ]; + + $hook_obj = new DFM_Transient_Hook( $transient_name, (object) $transient_obj, $transient_name, false, function( $data ) { + return $data[0]; + } ); + + $hook_data = [ + $users[0] => $modifiers, + $users[1] => $modifiers, + $users[2] => array_slice( $modifiers, 0, 2 ), + $users[3] => [], + ]; + + do_action( $transient_name, $hook_data ); + + $transient_keys = []; + foreach ( $users as $user ) { + $transient_keys[ $user ] = DFM_Transients::get_meta_map( 'user', $user, $transient_name ); + } + + foreach ( $transient_keys as $user_id => $keys ) { + $hook_modifiers = $hook_data[ $user_id ]; + if ( is_array( $hook_modifiers ) && ! empty( $hook_modifiers ) ) { + foreach ( $hook_modifiers as $modifier ) { + $this->assertEquals( sprintf( $return_string, $user_id, $modifier ), get_user_meta( $user_id, $keys[ $modifier ], true ) ); + } + } else { + foreach ( $modifiers as $modifier ) { + $this->assertEquals( sprintf( $return_string, $user_id, $modifier ), get_user_meta( $user_id, $keys[ $modifier ], true ) ); + } + } + } + + $this->assertEquals( 'test', get_user_meta( $users[2], $transient_keys[ $users[2] ]['test'], true ) ); + + } + } diff --git a/tests/test-class-dfm-transients.php b/tests/test-class-dfm-transients.php index 24017f6..d7807e5 100644 --- a/tests/test-class-dfm-transients.php +++ b/tests/test-class-dfm-transients.php @@ -355,13 +355,13 @@ public function testGetTransientFromTermMeta() { $transient_name = 'testGetTransientFromTermMeta'; $this->register_transient( $transient_name, [ - 'callback' => function( $modifier ) { - return 'term id: ' . $modifier; + 'callback' => function( $modifier, $term_id ) { + return 'term id: ' . $term_id; }, 'cache_type' => 'term_meta', ] ); - $transient_obj = new DFM_Transients( $transient_name, $term_id ); + $transient_obj = new DFM_Transients( $transient_name, '', $term_id ); $key = $transient_obj->key; $actual = $transient_obj->get(); @@ -379,6 +379,137 @@ public function testGetTransientFromTermMeta() { } + /** + * Test getting a transient from term meta when there are multiple modifiers + * @throws Exception + */ + public function testGetTransientFromTermMetaWithModifier() { + + $term_id = $this->factory->term->create(); + + $transient_name = 'testGetTransientFromTermMetaWithModifier'; + $return_string = 'Term ID: %d, Modifier: %s'; + $test_value = 'some test value'; + + $this->register_transient( $transient_name, [ + 'callback' => function( $modifier, $term_id ) use ( $return_string ) { + return sprintf( $return_string, $term_id, $modifier ); + }, + 'cache_type' => 'term_meta', + 'hash_key' => true, + ] ); + + $modifier_1 = 'first_mod'; + $modifier_2 = 'second_mod'; + + $transient_obj_1 = new DFM_Transients( $transient_name, $modifier_1, $term_id ); + $transient_obj_2 = new DFM_Transients( $transient_name, $modifier_2, $term_id ); + $key_1 = $transient_obj_1->key; + $key_2 = $transient_obj_2->key; + + $this->assertEquals( sprintf( $return_string, $term_id, $modifier_1 ), $transient_obj_1->get() ); + $this->assertEquals( sprintf( $return_string, $term_id, $modifier_2 ), $transient_obj_2->get() ); + + $transient_obj_1->set( $test_value ); + $transient_obj_2->set( $test_value ); + $this->assertEquals( $test_value, $transient_obj_1->get() ); + $this->assertEquals( $test_value, $transient_obj_2->get() ); + + $transient_obj_1->delete(); + $this->assertEquals( '', get_term_meta( $term_id, $key_1, true ) ); + $this->assertEquals( sprintf( $return_string, $term_id, $modifier_1 ), $transient_obj_1->get() ); + $this->assertEquals( sprintf( $return_string, $term_id, $modifier_1 ), get_term_meta( $term_id, $key_1, true ) ); + + $transient_obj_2->delete(); + $this->assertEquals( '', get_term_meta( $term_id, $key_2, true ) ); + $this->assertEquals( sprintf( $return_string, $term_id, $modifier_2 ), $transient_obj_2->get() ); + $this->assertEquals( sprintf( $return_string, $term_id, $modifier_2 ), get_term_meta( $term_id, $key_2, true ) ); + + } + + /** + * Test for retrieving transients with a modifier using the post meta cache + */ + public function testGetTransientFromPostMetaWithModifier() { + + $post_id = $this->factory->post->create(); + + $transient_name = 'testGetTransientFromPostMetaWithModifier'; + $return_string = 'Post ID: %d, Modifier: %s'; + $test_value = 'test value'; + + $this->register_transient( $transient_name, [ + 'callback' => function( $modifier, $post_id ) use ( $return_string ) { + return sprintf( $return_string, $post_id, $modifier ); + }, + 'cache_type' => 'post_meta', + 'async_updates' => true, + 'update_hooks' => 'updated_post_meta', + 'expiration' => HOUR_IN_SECONDS, + 'soft_expiration' => true, + ] ); + + $modifier_1 = 'dragons'; + $modifier_2 = 'rainbows'; + + $transient_obj_1 = new DFM_Transients( $transient_name, $modifier_1, $post_id ); + $transient_obj_2 = new DFM_Transients( $transient_name, $modifier_2, $post_id ); + $key_1 = $transient_obj_1->key; + $key_2 = $transient_obj_2->key; + + $this->assertEquals( sprintf( $return_string, $post_id, $modifier_1 ), $transient_obj_1->get() ); + $this->assertEquals( sprintf( $return_string, $post_id, $modifier_2 ), $transient_obj_2->get() ); + + $transient_obj_1->set( $test_value ); + $transient_obj_2->set( $test_value ); + $this->assertEquals( $test_value, $transient_obj_1->get() ); + $this->assertEquals( $test_value, $transient_obj_2->get() ); + + $transient_obj_1->delete(); + $this->assertEquals( '', get_post_meta( $post_id, $key_1, true) ); + $this->assertEquals( sprintf( $return_string, $post_id, $modifier_1 ), $transient_obj_1->get() ); + $this->assertEquals( sprintf( $return_string, $post_id, $modifier_1 ), get_post_meta( $post_id, $key_1, true )['data'] ); + $this->assertArrayHasKey( 'expiration', get_post_meta( $post_id, $key_1, true ) ); + + $transient_obj_2->delete(); + $this->assertEquals( '', get_post_meta( $post_id, $key_2, true) ); + $this->assertEquals( sprintf( $return_string, $post_id, $modifier_2 ), $transient_obj_2->get() ); + $this->assertEquals( sprintf( $return_string, $post_id, $modifier_2 ), get_post_meta( $post_id, $key_2, true )['data'] ); + $this->assertArrayHasKey( 'expiration', get_post_meta( $post_id, $key_2, true ) ); + + } + + /** + * Tests to ensure we haven't broken object type cache transients before we implemented modifiers for them + */ + public function testTermMetaTransientBackwardsCompat() { + + $term_id = $this->factory->term->create(); + $transient_name = 'testTermMetaTransientBackwardsCompat'; + $return_string = 'term id: %d'; + + $this->register_transient( $transient_name, [ + 'callback' => function( $modifier ) use ( $return_string ) { + return sprintf( $return_string, $modifier ); + }, + 'cache_type' => 'term_meta', + ] ); + + $transient_obj = new DFM_Transients( $transient_name, $term_id ); + $key = $transient_obj->key; + + $this->assertEquals( sprintf( $return_string, $term_id ), $transient_obj->get() ); + + $transient_obj->set( 'test data' ); + $this->assertEquals( 'test data', $transient_obj->get() ); + + $transient_obj->delete(); + $this->assertEquals( '', get_term_meta( $term_id, $key, true ) ); + $this->assertEquals( sprintf( $return_string, $term_id ), $transient_obj->get() ); + $this->assertEquals( sprintf( $return_string, $term_id ), get_term_meta( $term_id, $key, true ) ); + + } + /** * Test to make sure getting transient data from post meta works correctly */ @@ -386,19 +517,20 @@ public function testGetTransientFromPostMeta() { $post_id = $this->factory->post->create(); $transient_name = 'testGetTransientFromPostMeta'; + $custom_modifier = 'test'; $this->register_transient( $transient_name, [ 'callback' => function( $modifier ) { - return 'post id: ' . $modifier; + return 'transient data: ' . $modifier; }, 'cache_type' => 'post_meta', ] ); - $transient_obj = new DFM_Transients( $transient_name, $post_id ); + $transient_obj = new DFM_Transients( $transient_name, $custom_modifier, $post_id ); $key = $transient_obj->key; $actual = $transient_obj->get(); - $expected = 'post id: ' . $post_id; + $expected = 'transient data: ' . $custom_modifier; $this->assertEquals( $expected, $actual ); $transient_obj->set( 'some test value' ); @@ -420,8 +552,8 @@ public function testGetTransientFromMetaWithExpiration() { $transient_name = 'testGetTransientFromMetaWithExpiration'; $this->register_transient( $transient_name, [ - 'callback' => function( $modifier ) { - return 'post id: ' . $modifier; + 'callback' => function() { + return 'transient data for post'; }, 'cache_type' => 'post_meta', 'expiration' => DAY_IN_SECONDS, @@ -429,11 +561,11 @@ public function testGetTransientFromMetaWithExpiration() { 'hash_key' => true, ] ); - $transient_obj = new DFM_Transients( $transient_name, $post_id ); + $transient_obj = new DFM_Transients( $transient_name, '', $post_id ); $key = $transient_obj->key; $actual = $transient_obj->get(); - $expected = 'post id: ' . $post_id; + $expected = 'transient data for post'; $this->assertEquals( $expected, $actual ); $transient_obj->set( 'some test value' ); @@ -455,28 +587,27 @@ public function testGetTransientFromUserMeta() { $user_id = $this->factory->user->create(); $transient_name = 'testGetTransientFromUserMeta'; + $return_string = 'User ID: %d, Modifier: %s'; $this->register_transient( $transient_name, [ - 'callback' => function( $modifier ) { - return 'user id: ' . $modifier; + 'callback' => function( $modifier, $user_ID ) use ( $return_string ) { + return sprintf( $return_string, $user_ID, $modifier ); }, 'cache_type' => 'user_meta', ] ); - $transient_obj = new DFM_Transients( $transient_name, $user_id ); + $transient_obj = new DFM_Transients( $transient_name, '', $user_id ); $key = $transient_obj->key; - $actual = $transient_obj->get(); - $expected = 'user id: ' . $user_id; - $this->assertEquals( $actual, $expected ); + $this->assertEquals( sprintf( $return_string, $user_id, '' ), $transient_obj->get() ); $transient_obj->set( 'some test value' ); $this->assertEquals( 'some test value', $transient_obj->get() ); $transient_obj->delete(); $this->assertEquals( '', get_user_meta( $user_id, $key, true ) ); - $this->assertEquals( $expected, $transient_obj->get() ); - $this->assertEquals( $expected, get_user_meta( $user_id, $key, true ) ); + $this->assertEquals( sprintf( $return_string, $user_id, '' ), $transient_obj->get() ); + $this->assertEquals( sprintf( $return_string, $user_id, '' ), get_user_meta( $user_id, $key, true ) ); } @@ -582,10 +713,7 @@ public function testGetTransientFromNonExistentType() { $transient_obj = new DFM_Transients( $transient_name, '' ); $this->assertTrue( is_wp_error( $transient_obj->get() ) ); - - $this->setExpectedException( 'Exception', 'When registering your transient, you used an invalid cache type. Valid options are transient, post_meta, term_meta.' ); - $this->expectException( $transient_obj->set( 'test' ) ); - + $this->assertTrue( is_wp_error( $transient_obj->set( 'test' ) ) ); } @@ -600,8 +728,7 @@ public function testDeleteTransientFromNonExistentType() { $transient_obj = new DFM_Transients( $transient_name, '' ); - $this->setExpectedException( 'Exception', 'When registering your transient, you used an invalid cache type. Valid options are transient, post_meta, term_meta.' ); - $this->expectException( $transient_obj->delete() ); + $this->assertTrue( is_wp_error( $transient_obj->delete() ) ); } From 944b7258c6ac5b30b4c3022eeea85a093613341e Mon Sep 17 00:00:00 2001 From: CodeProKid Date: Fri, 16 Nov 2018 14:45:04 -0700 Subject: [PATCH 09/11] adding some comments --- includes/class-dfm-transient-hook.php | 21 ++++++++++++++++ includes/class-dfm-transient-utils.php | 12 +++++++++ includes/class-dfm-transients.php | 33 ++++++++++++++++++++++++- tests/test-class-dfm-transient-hook.php | 5 ++++ 4 files changed, 70 insertions(+), 1 deletion(-) diff --git a/includes/class-dfm-transient-hook.php b/includes/class-dfm-transient-hook.php index 2d80e05..c671695 100644 --- a/includes/class-dfm-transient-hook.php +++ b/includes/class-dfm-transient-hook.php @@ -141,6 +141,19 @@ public function spawn() { } } + /** + * Sample return formats from the callback + * + * - True/False: If you return false, it won't update anything, if you return true it will continue with the update process + * - 20: Return an int of the object ID to update the transient value for a specific object when the object cache type is being used. If you have transients using modifiers on this object all modifiers will be updated + * - 'blue': Return the string name of the transient modifier you would like to update. This only works for transients using the "transient" storage engine, since it needs the context of the object ID for object type caches + * - [ 10, 20, 30 ]: Return an array of object ID's to update transients on those specific object ID's + * - [ + * 10 => [ 'blue', 'yellow', 'green' ] Update transients with the modifiers blue, yellow, and green in the object ID: 10 + * 20 => [ 'blue', 'green' ] Update transients with the modifiers blue, and green in the object ID: 20 + * 30 => [] Update all transients within the group on object ID: 30 + * ] + */ if ( is_array( $modifiers ) && ! empty( $modifiers ) ) { foreach ( $modifiers as $key => $modifier ) { if ( is_array( $modifier ) && ! empty( $modifier ) ) { @@ -157,6 +170,14 @@ public function spawn() { } + /** + * Retrieve data from the hook callback and process it for updating + * + * @param mixed|string|int|bool $modifier The data passed back from the callback + * @param null $object_id ID of the object an update needs to be performed for + * + * @access private + */ private function dispatch_update( $modifier, $object_id = null ) { if ( 'transient' !== $this->transient_obj->cache_type ) { diff --git a/includes/class-dfm-transient-utils.php b/includes/class-dfm-transient-utils.php index f0fd5fd..a96e963 100644 --- a/includes/class-dfm-transient-utils.php +++ b/includes/class-dfm-transient-utils.php @@ -1,8 +1,20 @@ object_id, $this->key ); } + /** + * Add the transient keys to the mapping so they can be found easily later + * + * @param string $type The type of meta to store the map in + * @param string $transient_key The key for the transient group to create the map key off of + * + * @return void + * @access private + */ private function add_meta_map( $type, $transient_key ) { $map = get_metadata( $type, $this->object_id, self::meta_map_key( $transient_key ), true ); @@ -447,19 +456,41 @@ private function add_meta_map( $type, $transient_key ) { } + /** + * Return a map of meta keys for all of the modifiers for a transient registered in the group + * + * @param string $type The meta type to use for storage + * @param int $object_id The ID of the object to store the meta map in + * @param string $transient_key Key for the transient group the transient belongs to + * + * @return array + * @access public + * @static + */ public static function get_meta_map( $type, $object_id, $transient_key ) { if ( is_wp_error( $type ) ) { - return; + return array(); } $map = get_metadata( $type, $object_id, self::meta_map_key( $transient_key ), true ); if ( empty( $map ) ) { $map = []; } + return $map; + } + /** + * Return the meta map key for the transient group + * + * @param string $transient_key Transient key for the transient group + * + * @return string + * @access public + * @static + */ public static function meta_map_key( $transient_key ) { return self::$prefix . $transient_key . '_map'; } diff --git a/tests/test-class-dfm-transient-hook.php b/tests/test-class-dfm-transient-hook.php index d6d15dc..9f337dc 100644 --- a/tests/test-class-dfm-transient-hook.php +++ b/tests/test-class-dfm-transient-hook.php @@ -238,6 +238,10 @@ public function testTermObjectCacheHandlerWithModifiers() { } + /** + * Test that all the transients on a user type cache are cleared only when the modifiers for the + * object ID are passed back in the callback, or if it returns an empty array for the ID. + */ public function testUserObjectCacheHandlerWithModifiers() { $transient_name = 'testUserObjectCacheHandlerWithModifiers'; @@ -302,6 +306,7 @@ public function testUserObjectCacheHandlerWithModifiers() { } } + // Test to make sure the "test" modifier on the second to last user isn't updated since we didn't pass it to the hook in the $hook_data var $this->assertEquals( 'test', get_user_meta( $users[2], $transient_keys[ $users[2] ]['test'], true ) ); } From cb78cdeb9d9d51cb27e9b5e48199a92c19799b83 Mon Sep 17 00:00:00 2001 From: CodeProKid Date: Fri, 16 Nov 2018 14:46:46 -0700 Subject: [PATCH 10/11] removing some old text from the readme --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 3e77b63..f56127e 100644 --- a/README.md +++ b/README.md @@ -83,8 +83,6 @@ $post_transient = dfm_get_transient( 'sample_transient', '', $post_id ); ## Transient Modifier The Transient modifier (second parameter passed to the `dfm_get_transient` function) is used to store variations of the same type of transient. It will append the $modifier to the end of the transient key. This way you could store and retrieve different variations of the same transient that are mostly the same without registering a whole new transient. You can use the modifier to change the data saved to the transient by using it to alter your logic in your callback (the modifier is passed as the first argument to your callback function). -The transient modifier, (second parameter passed to the `dfm_get_transient` function) is used in a variety of different ways throughout this library. For a transient stored in metadata, it will be used as the object ID the transient is attached to. It will be used when using the `get_metadata()` and `save_metadata()` functions, so it is crucial that it is passed for transients stored in metadata. For global transients, it can be used to store variations of the same type of transient. It will append the `$modifier` to the end of the transient key. This way you could store and retrieve different variations of the same transient that are mostly the same without registering a whole new transient. You can use the modifier to change the data saved to the transient by using it to alter your logic in your callback (the modifier is passed as the only argument to your callback function). - ## Debugging To help with debugging, you can set a constant in your codebase called `DFM_TRANSIENTS_HOT_RELOAD` and set it to `true` to enable "hot reload" mode. This will essentially make it so that transient data will be regenerated every time it is called. This is handy if you are working on adding a transient, and want it to keep regenerating while you are working on it. This saves the need from manually deleting it from your database, or setting an extremely short timeout. **NOTE:** This constant should only ever be used on a development environment. Using this on production could cause serious performance issues depending on the data you are storing in your transients. From 5a2a6001d9c4401e4001e40419624a53fc6280a1 Mon Sep 17 00:00:00 2001 From: CodeProKid Date: Fri, 16 Nov 2018 15:08:36 -0700 Subject: [PATCH 11/11] version bump --- dfm-transients.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dfm-transients.php b/dfm-transients.php index 253b914..2a443c7 100644 --- a/dfm-transients.php +++ b/dfm-transients.php @@ -3,7 +3,7 @@ * Plugin Name: Transient Control * Plugin URI: https://github.com/dfmedia/DFM-Transients * Description: Better control for transients -* Version: 1.2.0 +* Version: 1.3.0 * Author: Ryan Kanner, Digital First Media * License: GPL-3 */