From 86f5ea25ba76bf77462355c1424cb9982f9a2e02 Mon Sep 17 00:00:00 2001 From: Ryan Kanner Date: Fri, 16 Dec 2016 09:21:32 -0700 Subject: [PATCH 01/14] fixing issue with telling if post meta transients exist or not --- includes/class-dfm-transients.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-dfm-transients.php b/includes/class-dfm-transients.php index c9933ed..875e5d8 100644 --- a/includes/class-dfm-transients.php +++ b/includes/class-dfm-transients.php @@ -239,7 +239,7 @@ private function get_from_meta( $type ) { $data = get_metadata( $type, $this->modifier, $this->key, true ); - if ( false === $data ) { + if ( false === $data || empty( $data ) ) { $data = call_user_func( $this->transient_object->callback, $this->modifier ); $this->set( $data ); } elseif ( $this->is_expired( $data ) && ! $this->is_locked() ) { From 7580da4f1c92f38c6da903bb07c08c7d6771e3e8 Mon Sep 17 00:00:00 2001 From: Ryan Kanner Date: Wed, 28 Dec 2016 12:43:36 -0700 Subject: [PATCH 02/14] fixes #17 better checking for if transient stored in metadata actually exist --- includes/class-dfm-transients.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/includes/class-dfm-transients.php b/includes/class-dfm-transients.php index 875e5d8..32f373b 100644 --- a/includes/class-dfm-transients.php +++ b/includes/class-dfm-transients.php @@ -238,8 +238,14 @@ private function get_from_transient() { private function get_from_meta( $type ) { $data = get_metadata( $type, $this->modifier, $this->key, true ); + + $data_exists = true; + + if ( empty( $data ) ) { + $data_exists = metadata_exists( $type, $this->modifier, $this->key ); + } - if ( false === $data || empty( $data ) ) { + if ( false === $data_exists ) { $data = call_user_func( $this->transient_object->callback, $this->modifier ); $this->set( $data ); } elseif ( $this->is_expired( $data ) && ! $this->is_locked() ) { From eecc673bd050d94f40c0d393b385ec961fc6b8e4 Mon Sep 17 00:00:00 2001 From: Ryan Kanner Date: Wed, 28 Dec 2016 12:57:10 -0700 Subject: [PATCH 03/14] fixes #15 adds full support for user meta transients --- includes/class-dfm-transients.php | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/includes/class-dfm-transients.php b/includes/class-dfm-transients.php index 875e5d8..097070c 100644 --- a/includes/class-dfm-transients.php +++ b/includes/class-dfm-transients.php @@ -341,12 +341,17 @@ private function cache_key() { $key = $this->hash_key( $key ); } - if ( 'post_meta' === $this->transient_object->cache_type || 'term_meta' === $this->transient_object->cache_type ) { - $key = $this->prefix . $key; - } - - if ( 'transient' === $this->transient_object->cache_type && ! empty( $this->modifier ) ) { - $key = $key . '_' . $this->modifier; + switch( $this->transient_object->cache_type ) { + case 'post_meta': + case 'term_meta': + case 'user_meta': + $key = $this->prefix . $key; + break; + case 'transient': + if ( ! empty( $this->modifier ) ) { + $key = $key . '_' . $this->modifier; + } + break; } return $key; From 65b554c18805a01634d2f82d74e2b06881c1a747 Mon Sep 17 00:00:00 2001 From: Ryan Kanner Date: Wed, 28 Dec 2016 13:38:44 -0700 Subject: [PATCH 04/14] fixes #14 creates a retry method --- includes/class-dfm-transients.php | 51 ++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/includes/class-dfm-transients.php b/includes/class-dfm-transients.php index 875e5d8..c273fbe 100644 --- a/includes/class-dfm-transients.php +++ b/includes/class-dfm-transients.php @@ -125,7 +125,7 @@ public function set( $data ) { } if ( false === $data || is_wp_error( $data ) ) { - return; + $this->facilitate_retry(); } switch ( $this->transient_object->cache_type ) { @@ -308,6 +308,55 @@ private function save_to_metadata( $data, $type ) { } + /** + * 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 + * retrying to get the data (if an API is down or something). + * + * @access private + * @return void + */ + private function facilitate_retry() { + + // Retrieve the stale data. + $current_data = $this->get(); + + if ( false === $current_data ) { + return; + } + + // Store the expiration set when registering the transient. Our timeout should not exceed this number. + $max_expiration = $this->transient_object->expiration; + + // Retrieve the cache fail amount from the cache + $failed_num = wp_cache_get( $this->key . '_failed', 'dfm_transients_retry' ); + + // Default to 1 failure if there's nothing set, or it's set to zero. This is so it doesn't mess with + // the `pow` func. + if ( false === $failed_num || 0 === $failed_num ) { + $failures = 1; + } else { + $failures = $failed_num; + } + + // Generate the new expiration time. This essentially just muliplies the amount of failures by itself, and + // then multiplies it by one minute to get the expiration, so if it is retrying it for the 5th time, it will + // do 5*5 (which is 25) so it will set the retry to 25 minutes. + $new_expiration = ( pow( $failures, 2 ) * MINUTE_IN_SECONDS ); + + // Only set the new expiration if it's less than the original registered expiration. + if ( $new_expiration < $max_expiration ) { + $this->transient_object->expiration = $new_expiration; + } + + // Save the stale data with the new expiration + $this->set( $current_data ); + + // Add 1 to the the failures in the cache. + wp_cache_set( $this->key . '_failed', ( $failures + 1 ), 'dfm_transients_retry', DAY_IN_SECONDS ); + + } + /** * Hashes storage key * From c6732706650744baaa4444c29d247a1a734873ad Mon Sep 17 00:00:00 2001 From: Ryan Kanner Date: Wed, 28 Dec 2016 13:44:46 -0700 Subject: [PATCH 05/14] preventing infinite loops for retrys --- includes/class-dfm-transients.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/includes/class-dfm-transients.php b/includes/class-dfm-transients.php index 7a8d812..9793670 100644 --- a/includes/class-dfm-transients.php +++ b/includes/class-dfm-transients.php @@ -62,6 +62,14 @@ class DFM_Transients { */ private $lock_key = ''; + /** + * Flag for if we are attempting a retry + * + * @var $doing_retry bool + * @access private + */ + private $doing_retry = false; + /** * DFM_Transients constructor. * @@ -207,6 +215,9 @@ private function get_from_transient() { $data = get_transient( $this->key ); if ( false === $data ) { + if ( true === $this->doing_retry ) { + return false; + } $data = call_user_func( $this->transient_object->callback, $this->modifier ); $this->set( $data ); } elseif ( $this->is_expired( $data ) && ! $this->is_locked() ) { @@ -246,6 +257,9 @@ private function get_from_meta( $type ) { } if ( false === $data_exists ) { + if ( $this->doing_retry ) { + return false; + } $data = call_user_func( $this->transient_object->callback, $this->modifier ); $this->set( $data ); } elseif ( $this->is_expired( $data ) && ! $this->is_locked() ) { @@ -324,9 +338,13 @@ private function save_to_metadata( $data, $type ) { */ private function facilitate_retry() { + // Set flag while doing a retry to prevent infinite loops. + $this->doing_retry = true; + // Retrieve the stale data. $current_data = $this->get(); + // If there is nothing already stored for the transient, bail. if ( false === $current_data ) { return; } From 4febe5b166e17404111d102a0345856326f22a92 Mon Sep 17 00:00:00 2001 From: Ryan Kanner Date: Wed, 28 Dec 2016 13:57:33 -0700 Subject: [PATCH 06/14] adding some new documentation on the retry system --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 942c770..a5a84d7 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,9 @@ $result = dfm_get_transient( 'sample_transient', '' ); ## Transient Modifier 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). + +## Retries +Since version 1.1.0 there is a retry facilitation system for DFM Transients. This is helpful if you are storing data from an external API, and want to serve stale data if the API is down. To use this feature, all you have to do is return `false` or a `wp_error` object in your transient callback if your remote request failed. This will then store the stale expired data back into the transient, and will use an expiration timeout that increases exponentially every time it fails to fetch the data. Essentially it stores a `failed` value in the cache for each transient, and adds one to the value every time the retry method runs. It then mulitplies this number by its self to figure out how many minutes it should set the expiration to. For example, if the fetch has failed 5 times, it will set the timeout to 25 minutes, and will retry again after that. ## Contributing To contribute to this repo, please fork it and submit a pull request. If there is a larger feature you would like to see, or something you would like to discuss, please open an issue. ## Copyright From 6a65fd90bd3cb9d9977ba9e1c10454ab5da209e4 Mon Sep 17 00:00:00 2001 From: Ryan Kanner Date: Wed, 28 Dec 2016 14:02:15 -0700 Subject: [PATCH 07/14] tighter validation around doing_retry for better consistency --- includes/class-dfm-transients.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-dfm-transients.php b/includes/class-dfm-transients.php index 9793670..129410c 100644 --- a/includes/class-dfm-transients.php +++ b/includes/class-dfm-transients.php @@ -257,7 +257,7 @@ private function get_from_meta( $type ) { } if ( false === $data_exists ) { - if ( $this->doing_retry ) { + if ( true === $this->doing_retry ) { return false; } $data = call_user_func( $this->transient_object->callback, $this->modifier ); From d26322e3ea186876dd1422382cc294224a1c48fb Mon Sep 17 00:00:00 2001 From: Ryan Kanner Date: Wed, 28 Dec 2016 15:23:39 -0700 Subject: [PATCH 08/14] fixes #16 adds more template tags for better parity with the built in API --- includes/class-dfm-transients.php | 36 ++++++++++++++ includes/template-tags.php | 82 ++++++++++++++++++++++++++++++- 2 files changed, 117 insertions(+), 1 deletion(-) diff --git a/includes/class-dfm-transients.php b/includes/class-dfm-transients.php index 8dc1a78..b717fbc 100644 --- a/includes/class-dfm-transients.php +++ b/includes/class-dfm-transients.php @@ -147,6 +147,34 @@ public function set( $data ) { } + /** + * @return WP_Error|null + */ + public function delete() { + + if ( ! isset( $this->transient_object ) ) { + return new WP_Error( 'invalid-transient', __( 'You are trying to retrieve a transient that doesn\'t exist', 'dfm-transients' ) ); + } + + 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: + new WP_Error( 'invalid-cache-type', __( 'When registering your transient, you used an invalid cache type. Valid options are transient, post_meta, term_meta.', 'dfm-transients' ) ); + } + + } + /** * Locks the ability to update the transient data. This will prevent race conditions. * @@ -314,6 +342,14 @@ private function save_to_metadata( $data, $type ) { } + private function delete_from_transient() { + delete_transient( $this->key ); + } + + private function delete_from_metadata( $type ) { + delete_metadata( $type, $this->modifier, $this->key ); + } + /** * Hashes storage key * diff --git a/includes/template-tags.php b/includes/template-tags.php index f80a067..0cd7800 100644 --- a/includes/template-tags.php +++ b/includes/template-tags.php @@ -80,7 +80,7 @@ function dfm_register_transient( $transient, $args = array() ) { * * @return mixed|WP_Error|array|string */ -function dfm_get_transient( $transient, $modifier ) { +function dfm_get_transient( $transient, $modifier = '' ) { $transients = new DFM_Transients( /** @@ -113,3 +113,83 @@ function dfm_get_transient( $transient, $modifier ) { return apply_filters( 'dfm_transients_get_result', $transients->get(), $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. + */ +function dfm_set_transient( $transient, $data, $modifier = '' ) { + + $transients = new DFM_Transients( + + /** + * 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 + * @return string $transient The name of the transient to set data to + */ + apply_filters( 'dfm_transients_set_transient_name', $transient, $modifier, $data ), + + /** + * Filters the unique modifier for the transient + * + * @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 + * @return string $modifier The unique modifier for the transient + */ + apply_filters( 'dfm_transients_set_transient_modifier', $modifier, $transient, $data ) + ); + + // Invoke the set method + $transients->set( $data ); + +} + +/** + * 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. + * @return void + * @access public + */ +function dfm_delete_transient( $transient, $modifier = '' ) { + + $transients = new DFM_Transients( + + /** + * Filters the name of the transient to delete + * + * @param string $transient Name of the transient + * @param string $modifier The unique modifier for the transient you want to delete + * @return string $transient Name of the transient to be deleted + */ + apply_filters( 'dfm_transients_delete_transient_name', $transient, $modifier ), + + /** + * Filters the modifier of the transient to delete + * + * @param string $modifier Unique modifier for the transient you want to delete + * @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 ) + ); + + // Invoke the delete method + $transients->delete(); + +} From b0325514fe7024a618a049b3ce67bb1ea123d3a7 Mon Sep 17 00:00:00 2001 From: Ryan Kanner Date: Wed, 28 Dec 2016 15:29:17 -0700 Subject: [PATCH 09/14] adding some documentation --- includes/class-dfm-transients.php | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/includes/class-dfm-transients.php b/includes/class-dfm-transients.php index b717fbc..ed13f13 100644 --- a/includes/class-dfm-transients.php +++ b/includes/class-dfm-transients.php @@ -148,7 +148,10 @@ public function set( $data ) { } /** - * @return WP_Error|null + * This method handles the deletion of a transient + * + * @return WP_Error|void + * @access public */ public function delete() { @@ -342,10 +345,25 @@ private function save_to_metadata( $data, $type ) { } + /** + * Deletes a transient stored in the default transient storage engine + * + * @access private + * @uses delete_transient() + * @return void + */ private function delete_from_transient() { delete_transient( $this->key ); } + /** + * Deletes a transient stored in metadata + * + * @param string $type The object type related to the metadata + * @uses delete_metadata() + * @return void + * @access private + */ private function delete_from_metadata( $type ) { delete_metadata( $type, $this->modifier, $this->key ); } From b68ef31c2bc26bfff26c0318e396ba0cca287357 Mon Sep 17 00:00:00 2001 From: Ryan Kanner Date: Wed, 28 Dec 2016 15:40:48 -0700 Subject: [PATCH 10/14] fixes #18 adds a check for a hot-reload constant --- README.md | 3 +++ includes/class-dfm-transients.php | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 942c770..222b5d4 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,9 @@ $result = dfm_get_transient( 'sample_transient', '' ); ## Transient Modifier 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. ## Contributing To contribute to this repo, please fork it and submit a pull request. If there is a larger feature you would like to see, or something you would like to discuss, please open an issue. ## Copyright diff --git a/includes/class-dfm-transients.php b/includes/class-dfm-transients.php index 8dc1a78..f0bf529 100644 --- a/includes/class-dfm-transients.php +++ b/includes/class-dfm-transients.php @@ -206,7 +206,7 @@ private function get_from_transient() { $data = get_transient( $this->key ); - if ( false === $data ) { + if ( false === $data || ( defined( 'DFM_TRANSIENTS_HOT_RELOAD' ) && true === DFM_TRANSIENTS_HOT_RELOAD ) ) { $data = call_user_func( $this->transient_object->callback, $this->modifier ); $this->set( $data ); } elseif ( $this->is_expired( $data ) && ! $this->is_locked() ) { @@ -245,7 +245,7 @@ private function get_from_meta( $type ) { $data_exists = metadata_exists( $type, $this->modifier, $this->key ); } - if ( false === $data_exists ) { + if ( false === $data_exists || ( defined( 'DFM_TRANSIENTS_HOT_RELOAD' ) && true === DFM_TRANSIENTS_HOT_RELOAD ) ) { $data = call_user_func( $this->transient_object->callback, $this->modifier ); $this->set( $data ); } elseif ( $this->is_expired( $data ) && ! $this->is_locked() ) { From 4b2ae7edbc5c63e95952b45c301082fb3b7b66ce Mon Sep 17 00:00:00 2001 From: Ryan Kanner Date: Wed, 3 May 2017 20:04:16 -0600 Subject: [PATCH 11/14] fixes #27 better validation for checking for transient expiration --- includes/class-dfm-transients.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-dfm-transients.php b/includes/class-dfm-transients.php index 81f7ae1..1d4d271 100644 --- a/includes/class-dfm-transients.php +++ b/includes/class-dfm-transients.php @@ -157,7 +157,7 @@ public function set( $data ) { /** * This method handles the deletion of a transient - * + * * @return WP_Error|void * @access public */ @@ -524,7 +524,7 @@ private function should_soft_expire() { * @return bool */ private function is_expired( $data ) { - if ( '' !== $this->transient_object->expiration && is_array( $data ) && $data['expiration'] < time() ) { + if ( ! empty( $this->transient_object->expiration ) && is_array( $data ) && $data['expiration'] < time() ) { return true; } else { return false; From ac3841484c35a6fda2de1bd0e28740a654f0f681 Mon Sep 17 00:00:00 2001 From: Ryan Kanner Date: Thu, 4 May 2017 13:37:32 -0600 Subject: [PATCH 12/14] start of CLI parity --- dfm-transients.php | 5 + includes/class-dfm-transients.php | 4 +- includes/cli.php | 160 ++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 includes/cli.php diff --git a/dfm-transients.php b/dfm-transients.php index 3be79d8..ca7f9bb 100644 --- a/dfm-transients.php +++ b/dfm-transients.php @@ -23,3 +23,8 @@ if ( is_admin() ) { require_once( plugin_dir_path( __FILE__ ) . 'includes/admin/class-dfm-transient-admin.php' ); } + +// CLI Commands +if ( defined( 'WP_CLI' ) && true === WP_CLI ) { + require_once( plugin_dir_path( __FILE__ ) . 'includes/cli.php' ); +} diff --git a/includes/class-dfm-transients.php b/includes/class-dfm-transients.php index 81f7ae1..0e5040b 100644 --- a/includes/class-dfm-transients.php +++ b/includes/class-dfm-transients.php @@ -36,7 +36,7 @@ class DFM_Transients { * @var string * @access private */ - private $modifier = ''; + public $modifier = ''; /** * The storage key for the transient @@ -44,7 +44,7 @@ class DFM_Transients { * @var string * @access private */ - private $key = ''; + public $key = ''; /** * Lock stored in transient. diff --git a/includes/cli.php b/includes/cli.php new file mode 100644 index 0000000..5646881 --- /dev/null +++ b/includes/cli.php @@ -0,0 +1,160 @@ +transient_object->cache_type ) { + WP_CLI::error( 'Cannot retrieve multiple transients when the modifier isn\'t explicitly passed for transients stored as normal transients since there is a fairly high likelyhood that the transients are only stored in the cache, and not the database.' ); + } + + $modifier_count = $this->get_all_modifiers( $transient_obj->key, $transient_obj->transient_object->cache_type, true ); + $modifier_data = $this->get_all_modifiers( $transient_obj->key, $transient_obj->transient_object->cache_type, false, 100 ); + + if ( ! empty( $modifier_count ) && 100 < $modifier_count[0] ) { + WP_CLI::success( sprintf( '%d modifiers found. Only showing the first 100 below', $modifier_count[0] ) ); + } + + } else { + $modifier_data = array_slice( $args, 1, 999 ); //@TODO make this way less ghetto... + } + } else { + $transient_data = dfm_get_transient( $transient_name, $modifier ); + } + + if ( false === $multiple && isset( $transient_data ) ) { + + $data = array( array( 'modifier' => $modifier, 'data' => $transient_data ) ); + + } else { + + $data = array(); + + if ( ! empty( $modifier_data ) && is_array( $modifier_data ) && isset( $transient_obj ) ) { + foreach ( $modifier_data as $modifier ) { + $transient_obj->modifier = $modifier; + $data[] = array( 'modifier' => $modifier, 'data' => $transient_obj->get() ); + } + } + + } + + WP_CLI\Utils\format_items( 'table', $data, array( 'modifier', 'data' ) ); + + } + + public function set( $args, $assoc_args ) { + + } + + public function delete( $args, $assoc_args ) { + + $transient_name = ( isset( $args[0] ) ) ? $args[0] : ''; + $modifier = ( isset( $args[1] ) ) ? $args[1] : ''; + + if ( empty( $transient_name ) ) { + parent::error( 'A transient name must be passed' ); + } + + $multiple = false; + + } + + public function list( $args, $assoc_args ) { + + global $dfm_transients; + + $transients = $dfm_transients; + + if ( empty( $dfm_transients ) ) { + parent::error( 'Looks like you don\'t have any transients registered' ); + } + + $transient_names = ( isset( $args ) ) ? $args : array(); + + if ( ! empty( $transient_names ) ) { + $transients = array_intersect_key( $transients, array_flip( $transient_names ) ); + } + + $supported_props = array( 'key', 'hash_key', 'cache_type', 'async_updates', 'expiration', 'soft_expiration' ); + + if ( ! empty( $assoc_args ) ) { + $filter_args = array_intersect_key( $assoc_args, array_flip( $supported_props ) ); + $transients = wp_list_filter( $transients, $filter_args ); + } + + if ( empty( $transients ) ) { + parent::error( 'No transients found with this criteria' ); + } + + if ( ! empty( $assoc_args['fields'] ) ) { + if ( is_string( $assoc_args['fields'] ) ) { + $fields = explode( ',', $assoc_args ); + } else { + $fields = $assoc_args['fields']; + } + $fields = array_intersect( $fields, $supported_props ); + } else { + $fields = $supported_props; + } + + $formatter = new \WP_CLI\Formatter( $assoc_args, $fields ); + $formatter->display_items( $transients ); + + } + + private function get_all_modifiers( $meta_key, $type, $count = false, $limit = false ) { + + global $wpdb; + + $object_type = substr( $type, 0, 4 ); + + $table = _get_meta_table( $object_type ); + + $select = ( true === $count ) ? 'count(*)' : $object_type . '_id'; + + $limit = ( false !== $limit ) ? 'LIMIT ' . absint( $limit ) : ''; + + if ( false === $table ) { + return false; + } + + $modifiers = $wpdb->get_col( $wpdb->prepare( " + SELECT $select + FROM $table + WHERE meta_key='%s' + $limit + ", + $meta_key + ) ); + + //print_r( $meta_key ); + return $modifiers; + + } + + } + + WP_CLI::add_command( 'dfm-transients', 'DFM_Transients_CLI' ); + +} From 1fab8dde7e6dac56293399b249c16c253e8ac201 Mon Sep 17 00:00:00 2001 From: Ryan Kanner Date: Fri, 5 May 2017 11:19:25 -0600 Subject: [PATCH 13/14] feature/5 finishing up CLI support --- includes/cli.php | 309 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 255 insertions(+), 54 deletions(-) diff --git a/includes/cli.php b/includes/cli.php index 5646881..ab8cd35 100644 --- a/includes/cli.php +++ b/includes/cli.php @@ -4,88 +4,270 @@ class DFM_Transients_CLI extends WP_CLI { + private $supported_props; + /** - * @param $args + * Retrieves information about a specific DFM Transient + * + * ## OPTIONS + * + * : Name of the transient you want to retrieve information about + * + * [...] + * : List of modifiers to get data about + * + * [--format] + * : Render the output in a particular format + * --- + * default: table + * options: + * - table + * - csv + * - ids + * - json + * - count + * - yaml + * --- + * + * [--limit] + * : The amount of transients to retrieve. Pass -1 for no limit. + * --- + * default: 100 + * --- + * + * [--fields] + * : The fields you would like to return + * + * ## AVAILABLE FIELDS + * * modifier + * * data + * + * @param array $args * @param $assoc_args */ public function get( $args, $assoc_args ) { - $transient_name = ( isset( $args[0] ) ) ? $args[0] : ''; - $modifier = ( isset( $args[1] ) ) ? $args[1] : ''; + $transient_name = array_shift( $args ); + $modifiers = $args; + + $this->supported_props = array( 'modifier', 'data' ); + + $options = wp_parse_args( $assoc_args, array( + 'format' => '', + 'limit' => 100, + ) + ); if ( empty( $transient_name ) ) { parent::error( 'A transient name must be passed' ); } - $multiple = false; - - if ( 'all' === $modifier || ! empty( $args[2] ) ) { - $multiple = true; - $transient_obj = new DFM_Transients( $transient_name, '' ); - if ( 'all' === $modifier ) { - - if ( 'transient' === $transient_obj->transient_object->cache_type ) { - WP_CLI::error( 'Cannot retrieve multiple transients when the modifier isn\'t explicitly passed for transients stored as normal transients since there is a fairly high likelyhood that the transients are only stored in the cache, and not the database.' ); + $transient_obj = new DFM_Transients( $transient_name, '' ); + $transient_type = $transient_obj->transient_object->cache_type; + $transient_key = $transient_obj->key; + + switch ( $options['format'] ) { + case 'ids': + $data = $this->get_all_modifiers( $transient_key, $transient_type, false, $options['limit'] ); + break; + case 'count': + $data = $this->get_all_modifiers( $transient_key, $transient_type, true ); + $data = ( ! empty( $data ) && is_array( $data ) ) ? $data[0] : 0; + break; + default: + if ( 'all' === $modifiers[0] ) { + $modifier_keys = $this->get_all_modifiers( $transient_key, $transient_type, false, $options['limit'] ); + } elseif ( ! empty( $modifiers ) ) { + $modifier_keys = $modifiers; + } else { + $data = array( + array( + 'modifier' => '', + 'data' => dfm_get_transient( $transient_name ), + ), + ); } - $modifier_count = $this->get_all_modifiers( $transient_obj->key, $transient_obj->transient_object->cache_type, true ); - $modifier_data = $this->get_all_modifiers( $transient_obj->key, $transient_obj->transient_object->cache_type, false, 100 ); + if ( ! isset( $data ) ) { - if ( ! empty( $modifier_count ) && 100 < $modifier_count[0] ) { - WP_CLI::success( sprintf( '%d modifiers found. Only showing the first 100 below', $modifier_count[0] ) ); + $data = array(); + + if ( isset( $modifier_keys ) && ! empty( $modifier_keys ) && is_array( $modifier_keys ) ) { + foreach ( $modifier_keys as $modifier_key ) { + $transient_obj->modifier = absint( $modifier_key ); + $data[] = array( + 'modifier' => $modifier_key, + 'data' => $transient_obj->get(), + ); + } + } } + break; + } - } else { - $modifier_data = array_slice( $args, 1, 999 ); //@TODO make this way less ghetto... - } + if ( 'count' !== $options['format'] ) { + $this->format_output( $data, $assoc_args ); + parent::line(); } else { - $transient_data = dfm_get_transient( $transient_name, $modifier ); + parent::success( sprintf( '%d transients found', $data ) ); } - if ( false === $multiple && isset( $transient_data ) ) { + } + + /** + * Sets data for a particular DFM Transient + * + * ## OPTIONS + * + * : Name of the transient you would like to set data for + * + * ... + * : List of modifiers you want to update the transient data for + * + * [--data=] + * : The new data you want to store in the transient + * + * @param $args + * @param $assoc_args + */ + public function set( $args, $assoc_args ) { - $data = array( array( 'modifier' => $modifier, 'data' => $transient_data ) ); + $transient_name = array_shift( $args ); + $modifiers = $args; - } else { + if ( empty( $transient_name ) ) { + parent::error( 'A transient name must be passed' ); + } - $data = array(); + $data = \WP_CLI\Utils\get_flag_value( $assoc_args, 'data', '' ); - if ( ! empty( $modifier_data ) && is_array( $modifier_data ) && isset( $transient_obj ) ) { - foreach ( $modifier_data as $modifier ) { - $transient_obj->modifier = $modifier; - $data[] = array( 'modifier' => $modifier, 'data' => $transient_obj->get() ); - } + if ( ! empty( $modifiers ) && is_array( $modifiers ) ) { + + if ( 10 < count( $modifiers ) ) { + $progress = \WP_CLI\Utils\make_progress_bar( 'Updating Transients', count( $modifiers ) ); } - } + foreach ( $modifiers as $modifier ) { + dfm_set_transient( $transient_name, $data, absint( $modifier ) ); + if ( isset( $progress ) ) { + $progress->tick(); + } + } - WP_CLI\Utils\format_items( 'table', $data, array( 'modifier', 'data' ) ); + if ( isset( $progress ) ) { + $progress->finish(); + } - } + parent::success( sprintf( 'Successfully updated %d transients', count( $modifiers ) ) ); - public function set( $args, $assoc_args ) { + } else { + dfm_set_transient( $transient_name, $data, '' ); + parent::success( sprintf( 'Successfully updated the %s transient', $transient_name ) ); + } } + /** + * Deletes some particular DFM Transients + * + * ## OPTIONS + * + * : Name of the transient you would like to delete data for + * + * ... + * : List of modifiers you want to delete the transients for + * + * @param array $args + * @param array $assoc_args + */ public function delete( $args, $assoc_args ) { - $transient_name = ( isset( $args[0] ) ) ? $args[0] : ''; - $modifier = ( isset( $args[1] ) ) ? $args[1] : ''; + $transient_name = array_shift( $args ); + $modifiers = $args; if ( empty( $transient_name ) ) { parent::error( 'A transient name must be passed' ); } - $multiple = false; + if ( empty( $modifiers ) ) { + dfm_delete_transient( $transient_name ); + parent::success( sprintf( 'Successfully deleted transient: %s', $transient_name ) ); + } else { + + if ( count( $modifiers ) > 10 ) { + $progress = \WP_CLI\Utils\make_progress_bar( 'Deleting transients', count( $modifiers ) ); + } + + if ( is_array( $modifiers ) ) { + + foreach ( $modifiers as $modifier ) { + + dfm_delete_transient( $transient_name, $modifier ); + + if ( isset( $progress ) ) { + $progress->tick(); + } + + } + + if ( isset( $progress ) ) { + $progress->finish(); + } + + } + + parent::success( sprintf( 'Successfully deleted %d transients', count( $modifiers ) ) ); + + } } + /** + * Lists all of the registered transients through DFM Transients + * + * ## OPTIONS + * [...] + * : Optionally pass the names of the transients you want to get information about + * + * [--fields] + * : Fields to return + * --- + * default: all + * options: + * - key + * - hash_key + * - cache_type + * - async_updates + * - expiration + * - soft_expiration + * --- + * + * [--format] + * : Render the output in a particular format + * --- + * default: table + * options: + * - table + * - csv + * - ids + * - json + * - yaml + * --- + * + * [--=] + * : One or more fields to filter the list with + * + * @param $args + * @param $assoc_args + */ public function list( $args, $assoc_args ) { global $dfm_transients; $transients = $dfm_transients; + $this->supported_props = array( 'key', 'hash_key', 'cache_type', 'async_updates', 'expiration', 'soft_expiration' ); + if ( empty( $dfm_transients ) ) { parent::error( 'Looks like you don\'t have any transients registered' ); } @@ -96,10 +278,8 @@ public function list( $args, $assoc_args ) { $transients = array_intersect_key( $transients, array_flip( $transient_names ) ); } - $supported_props = array( 'key', 'hash_key', 'cache_type', 'async_updates', 'expiration', 'soft_expiration' ); - if ( ! empty( $assoc_args ) ) { - $filter_args = array_intersect_key( $assoc_args, array_flip( $supported_props ) ); + $filter_args = array_intersect_key( $assoc_args, array_flip( $this->supported_props ) ); $transients = wp_list_filter( $transients, $filter_args ); } @@ -107,22 +287,20 @@ public function list( $args, $assoc_args ) { parent::error( 'No transients found with this criteria' ); } - if ( ! empty( $assoc_args['fields'] ) ) { - if ( is_string( $assoc_args['fields'] ) ) { - $fields = explode( ',', $assoc_args ); - } else { - $fields = $assoc_args['fields']; - } - $fields = array_intersect( $fields, $supported_props ); - } else { - $fields = $supported_props; - } - - $formatter = new \WP_CLI\Formatter( $assoc_args, $fields ); - $formatter->display_items( $transients ); + $this->format_output( $transients, $assoc_args ); } + /** + * Method to retrieve modifier ID's or the count of modifiers + * + * @param string $meta_key Name of the meta key to look for + * @param string $type Meta type so we know which table to search in + * @param bool $count Whether or not we should return the total count + * @param bool|int $limit Whether or not we should limit results, and if so what that limit is + * + * @return array|bool + */ private function get_all_modifiers( $meta_key, $type, $count = false, $limit = false ) { global $wpdb; @@ -133,7 +311,7 @@ private function get_all_modifiers( $meta_key, $type, $count = false, $limit = f $select = ( true === $count ) ? 'count(*)' : $object_type . '_id'; - $limit = ( false !== $limit ) ? 'LIMIT ' . absint( $limit ) : ''; + $limit = ( false !== $limit && '-1' !== $limit ) ? 'LIMIT ' . absint( $limit ) : ''; if ( false === $table ) { return false; @@ -148,11 +326,34 @@ private function get_all_modifiers( $meta_key, $type, $count = false, $limit = f $meta_key ) ); - //print_r( $meta_key ); return $modifiers; } + /** + * Handles the formatting of output + * + * @param array $transients The data to display + * @param array $assoc_args Args so we know how to display it + */ + private function format_output( $transients, $assoc_args ) { + + if ( ! empty( $assoc_args['fields'] ) ) { + if ( is_string( $assoc_args['fields'] ) ) { + $fields = explode( ',', $assoc_args ); + } else { + $fields = $assoc_args['fields']; + } + $fields = array_intersect( $fields, $this->supported_props ); + } else { + $fields = $this->supported_props; + } + + $formatter = new \WP_CLI\Formatter( $assoc_args, $fields ); + $formatter->display_items( $transients ); + + } + } WP_CLI::add_command( 'dfm-transients', 'DFM_Transients_CLI' ); From 21b401fb57d777adf5828e79236721a78b685ddd Mon Sep 17 00:00:00 2001 From: Ryan Kanner Date: Fri, 5 May 2017 11:23:57 -0600 Subject: [PATCH 14/14] 1.1.0 version bump --- dfm-transients.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dfm-transients.php b/dfm-transients.php index ca7f9bb..523c6c8 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.0.0 +* Version: 1.1.0 * Author: Ryan Kanner, Digital First Media * License: MIT */