Skip to content

Commit

Permalink
Merge pull request #53 from CodeProKid/feature/51
Browse files Browse the repository at this point in the history
Feature/51 - adding support for using modifiers on object type caches
  • Loading branch information
hughdevore authored Nov 27, 2018
2 parents f93007b + 5a2a600 commit 6a5f5a1
Show file tree
Hide file tree
Showing 11 changed files with 700 additions and 154 deletions.
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -78,7 +81,7 @@ $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 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).
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).

## 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.
Expand Down
5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.*"
}
}
3 changes: 2 additions & 1 deletion dfm-transients.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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' );
Expand Down
11 changes: 8 additions & 3 deletions includes/class-dfm-async-handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' ) );
Expand Down Expand Up @@ -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,
Expand Down
121 changes: 91 additions & 30 deletions includes/class-dfm-transient-hook.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 );

Expand All @@ -70,9 +89,14 @@ 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 );
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( '' ) ) {
Expand All @@ -83,7 +107,8 @@ 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();
Expand Down Expand Up @@ -116,30 +141,66 @@ 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 ) {
new DFM_Async_Handler( $this->transient, $modifier );
/**
* 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 ) ) {
foreach ( $modifier as $key_modifier ) {
$this->dispatch_update( $key_modifier, $key );
}
} 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 ) {
$this->run_update( $modifier );
}

/**
* 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 ) {

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 );
}
} else {
$this->run_update( '', $object_id );
}
} else {
$this->run_update( $modifiers );
$this->run_update( $modifier, $object_id );
}

} else {
$this->run_update( $modifier );
}

}
Expand Down
13 changes: 9 additions & 4 deletions includes/class-dfm-transient-scheduler.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
}

}
Expand Down Expand Up @@ -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'] );

Expand All @@ -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 ) ) {
Expand All @@ -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();
Expand Down
38 changes: 38 additions & 0 deletions includes/class-dfm-transient-utils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

if ( ! class_exists( 'DFM_Transient_Utils' ) ) {

/**
* Class DFM_Transient_Utils
* Helper methods for the DFM_Transients plugin
*/
class DFM_Transient_Utils {

/**
* Return the *_metadata() cache type translated from the registered `cache_type`.
*
* @param string $cache_type Name of the cache type to translate to the friendlier version
*
* @return string|WP_Error
*/
public static function get_meta_type( $cache_type ) {

switch ( $cache_type ) {
case 'post_meta' :
$meta_type = 'post';
break;
case 'term_meta' :
$meta_type = 'term';
break;
case 'user_meta';
$meta_type = 'user';
break;
default:
return 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' ) );
}

return $meta_type;

}
}
}
Loading

0 comments on commit 6a5f5a1

Please sign in to comment.