Tag: api

  • Managing Cacheability With LSCWP API

    Managing Cacheability With LSCWP API

    LSCWP API Cache Tags

    The LiteSpeed Cache plugin for WordPress includes an API that you can use to customize cache behavior. If you’re a developer, the LSCWP API will help you to achieve LiteSpeed compatibility within your plugins. If you’re a site owner who enjoys customizing every little aspect of your site, maybe you have some unique reason to use one of our API hooks in your child theme’s functions.php file.

    Whatever your actual purpose is, today we’re going to look at three LSCWP API cache hooks and show you how to use them to control the cacheability of a page:

    • litespeed_control_set_nocache
    • litespeed_control_set_cacheable
    • litespeed_control_force_cacheable

    Plus, we’ll look at a couple of bonus hooks that are useful for setting cache scope:

    • litespeed_control_set_private
    • litespeed_control_force_public

    To cache, or not to cache

    First, let’s look at how LSCWP decides which pages to cache and which pages not to cache.

    By default, the wp action hook is run on each page of a WordPress site, after the query has been parsed and the post has been loaded, but before template functionality is executed. (There may be exceptions to this rule, but we’ll talk about that later.)

    LSCWP considers all pages that have had the wp action run on them to be cacheable. Essentially, every page is cacheable. Unless it falls into one of these categories:

    • It is an admin page
    • It is a POST request
    • is_trackback() is true
    • is_search() is true
    • No theme is used

    Site administrators may also set some content to be non-cacheable, so LSCWP checks the values of the settings in LiteSpeed Cache > Cache > Excludes, and considers a page non-cacheable if:

    • The URI is found in the Do Not Cache URIs list
    • The URL has a query string found in the Do Not Cache Query Strings list
    • The post has a category found in the Do Not Cache Categories list
    • The post has a tag found in the Do Not Cache Tags list
    • The request has a cookie found in the Do Not Cache Cookies list
    • The request has a user agent found in the Do Not Cache User Agents list
    • The request is being made by a user whose role is checked in the Do Not Cache Roles list

    This is enough for most admins, but maybe you have a special case that you want to handle programmatically. Or maybe you have written a plugin that generates its own pages and doesn’t follow the usual wp action hook conventions. What do you do then?

    You use LSCWP’s cache control API hooks.

    Cache control API hooks

    Mark the current page as cacheable

    If your plugin generates pages and does not invoke the wp action hook, then LSCWP may not know that the page should be cached. Use the litespeed_control_set_cacheable action with a ‘reason’ parameter, like so:

    do_action(
    	'litespeed_control_set_cacheable',
    	'The lady doth protest too much, methinks.'
    );
    

    Note that if the page fits one of the criteria specified in the the plugin’s Cache > Excludes settings, then the page may ultimately be set as non-cacheable, despite this hook.

    Mark the current page as non-cacheable

    The litespeed_control_set_nocache action is the opposite of the previous one. Use it in your plugins or snippets to inform LSCWP not to cache the page.

    do_action(
    	'litespeed_control_set_nocache',
    	'And flights of angels sing thee to thy rest!'
    );
    

    Force the current page to be cacheable

    The litespeed_control_force_cacheable action informs LSCWP to cache the page, and it will override anything the user may enter in the Cache > Excludes settings.

    do_action(
    	'litespeed_control_force_cacheable',
    	'if it be not to come, it will be now'
    );
    

    What have I done?

    With all the settings and API hooks available, it can be difficult to know whether the current page is set to be cached or whether it is non-cacheable. Use the litespeed_control_cacheable filter to get a definitive answer.

    apply_filters( 'litespeed_control_cacheable', false );
    

    BONUS

    Once you set a page as cacheable, you might want to further define the scope of that cache. Do you want to store the cache object in a logged-in user’s private cache, or do you want all visitors to view a publicly cached copy? By default, LSCWP caches pages publicly. Use these action hooks to explicitly define cache scope:

    do_action(
    	'litespeed_control_set_private',
    	'Listen to many, speak to a few.'
    );
    
    do_action(
    	'litespeed_control_force_public',
    	'Brevity is the soul of wit.'
    );
    

    Conclusion

    We hope this is helpful to you plugin developers and site tinkerers out there. If you want to learn more about LSCWP’s API hooks, take a look at our API Reference.


    Thank you to Tynan Beatty for his numerous contributions to this post, including all of the sample code!

  • Customized Cache Tags With LSCWP API

    Customized Cache Tags With LSCWP API

    Customized Cache Tags With LSCWP API

    LiteSpeed Cache, the powerful cache engine that ships with all LiteSpeed server products, features the clever “smart purge” technology. This system uses tags to group pages together based on a particular set of rules. This grouping then allows them to all be purged together at one time after a single triggering event. In the LiteSpeed Cache plugin for WordPress, this tagging system is at work behind the scenes, using the rules of WordPress publishing to know exactly when to purge posts, pages, products, categories, and more.

    But imagine that you have a plugin, or some customized behavior for your WordPress site, and you would like to be able to group pages together in your own special way, so that they may be purged by your own unique triggering event. How cool would that be?

    Well, it turns out, you can build this functionality into your plugin with the tag-based actions of the LSCWP API.

    LSCache Tags

    Before we get into creating your own custom tags, let’s look at LSCWP’s built-in tagging behavior.

    Here’s an example that shows the “smart purge” system in action:

    You have the following post on your WordPress site:

    • Title: Unique American Towns
    • Category: United States

    People visit your site, and LiteSpeed caches all of the pages that your visitors read, including the Unique American Towns post page.

    Later, you edit that post, change the Title and add a second Category:

    • Title: Unique North American Towns
    • Category: United States, Canada

    Since there have been changes to the post, the following pages are purged from the cache according to the rules set up in LSCWP admin:

    • The post itself
    • The front page and/or home page of your site
    • The author archive page
    • The post type archive page
    • The monthly archive page
    • The United States and Canada category pages

    You can change the rules to purge even more pages than this (or fewer), if it’s appropriate for your website.

    This purge tag functionality is very powerful. It means that LSCache can do a targeted purge like we saw above, removing the edited post, as well as every other page that is influenced by that post. Without a tagging system, we would have to either purge too little (just the post) or too much (all of the site’s pages).

    This functionality allows you to specify relatively long TTLs (Times to Live) for your pages, knowing that LSCache will step in and purge any pages that are affected by recent activities.

    Customized cache tags with LSCWP API

    Now imagine you have your own plugin that generates its own content using custom URLs. LSCWP will cache those pages, but it has only the most basic concept of when to purge them: it will purge the pages when the TTL is reached (that is, when the cache expires).

    If your plugin was using custom cache tags, you could group pages together, and purge them together before they expire, using whatever logic makes sense for your plugin.

    Here’s a simple example: Let’s say you have a widget with a countdown for the number of days remaining until a big event that your site promotes. The widget only changes once per day, but when it does, you would want to purge any page where the widget is displayed. Using the following LSCWP API hooks, this is possible:

    Add a custom $tag to the cached page:

    do_action( 'litespeed_tag_add', $tag );
    

    Purge all pages tagged with the custom $tag:

    do_action( 'litespeed_purge', $tag );
    

    Example

    Here’s a more in-depth example that shows you how to create a basic set of functions which will create a form, and cache or purge that form based on the form’s input values.

    Basic Plugin

    For this plugin, we’ll use a query string parameter called greet, which will store the visitor’s name.

    <?php
    /**
     * Plugin Name:  LiteSpeed Cache API Tag Demo
     * ...
     */
    namespace LscwpApiTagDemo;
    defined( 'WPINC' ) || exit;
    add_action( 'init', function () {
    	if ( ! isset( $_GET['greet'] ) ) {
    		return;
    	}
    	$visitor = $_GET['greet'];
    	# ...
    

    Dynamic Tag Name

    We create a custom tag based on the value of the greet parameter. So, if our URL was https://.example.com/?greet=Lauren, our custom tag would be LscwpApiTagDemo\greet.lauren, and we would add that tag to the current page.

    add_action( 'init', function () {
    	if ( ! isset( $_GET['greet'] ) ) {
    		return;
    	}
    	$visitor = $_GET['greet'];
    	$tag =
    		__NAMESPACE__ . '\greet.'
    		. mb_strtolower( $visitor, 'UTF-8' );
    	$visitor = stripslashes( $visitor );
    	do_action( 'litespeed_tag_add', $tag );
    	# ...
    

    Greeting Content

    We replace WordPress’s content loop with our greeting.

    add_action( 'init', function () {
    	# ...
    	add_action( 'loop_start', 'ob_start', 0, 0 );
    	add_action( 'loop_end', function () use ( $visitor ) {
    		ob_end_clean();
    		?>
    		<article class="page entry">
    			<div class="entry-content">
    				<?php greet( $visitor ); ?>
    			</div>
    		</article>
    		<?php
    	}, 0, 999 );
    } );
    

    Greeting Function

    We display a message and include a form that accepts a reply.

    function greet( $visitor ) {
    	?>
    	<h2><?php
    		printf( esc_html__( 'Howdy, %1$s!' ), $visitor );
    	?></h2>
    	<form method="get">
    		<input name="greet" type="hidden"
    			value="<?php echo $visitor; ?>">
    		<label for="reply">
    			<h3><?php esc_html_e( 'Care to Respond?' ); ?></h3>
    		</label>
    		<input id="reply" name="reply" type="text" value=""
    			placeholder="<?php
    				esc_html_e( 'Type your reply here ...' );
    			?>">
    		<button type="submit"><?php
    			esc_html_e( 'Send' );
    		?></button>
    	</form>
    	<?php
    }
    

    Reply Content

    We revise our main function to add a condition that displays alternative content if the user replied to our greeting.

    add_action( 'init', function () {
    	# ...
    		?>
    		<article class="page entry">
    			<div class="entry-content">
    				<?php
    				if ( isset( $_GET['reply'] ) ) {
    					reply( $visitor );
    				} else {
    					greet( $visitor );
    				}
    				?>
    			</div>
    		</article>
    		<?php
    	}, 0, 999 );
    } );
    

    Reply Function

    We display a thank you note and show that we remember the user’s reply.

    function reply( $visitor ) {
    	?>
    	<h2><?php
    		printf( esc_html__( 'Thank you, %1$s.' ), $visitor );
    	?></h2>
    	<h3><?php
    		esc_html_e( "I'll always remember your kind words." );
    	?></h3>
    	<?php
    	$reply = stripslashes( $_GET['reply'] );
    	if ( ! empty( $reply ) ) {
    		?>
    		<blockquote><?php
    			echo esc_html( $reply );
    		?></blockquote>
    		<?php
    	}
    	# ...
    

    And we add a Forget about me … button that will trigger a cache purge targeting every item associated with the visitor’s name.

    function reply( $visitor ) {
    	# ...
    	}
    	?>
    	<form method="post" action="<?php
    			echo drop_query_var(
    				$_SERVER['REQUEST_URI'],
    				'reply'
    			);
    		?>">
    		<input name="reset" type="hidden">
    		<button type="submit"><?php
    			esc_html_e( 'Forget about me ...' );
    		?></button>
    	</form>
    	<?php
    }
    

    Putting It All Together

    We update our main function once more, so that any page tagged LscwpApiTagDemo\greet.lauren will be deleted if the button is pressed.

    add_action( 'init', function () {
    	# ...
    	$tag =
    		__NAMESPACE__ . '\greet.'
    		. mb_strtolower( $visitor, 'UTF-8' );
    	$visitor = stripslashes( $visitor );
    	if ( isset( $_POST['reset'] ) ) {
    		do_action( 'litespeed_purge', $tag );
    	}
    	do_action( 'litespeed_tag_add', $tag );
    	# ...
    

    Behavior

    So, here’s how that looks in action.:

    Lauren visits the site and provides her name. LiteSpeed doesn’t find the page in cache, so the page is generated, served to Lauren, and cached:

    https://example.com/?greet=lauren
    	x-litespeed-cache: miss
    

    She fills in the form saying, Hello world. LiteSpeed doesn’t find the page in cache, so the page is generated, served to Lauren, and cached:

    https://example.com/?greet=Lauren&reply=Hello%20world
    	x-litespeed-cache: miss
    

    She fills in the form again saying, I love LiteSpeed. LiteSpeed doesn’t find the page in cache, so the page is generated, served to Lauren, and cached:

    https://example.com/?greet=LAUREN&reply=I%20love%20LiteSpeed
    	x-litespeed-cache: miss
    

    Note that the greet parameter is not case sensitive. Our plugin converts it to lowercase before setting the $tag value..

    She goes back to the form again saying, Hello world one more time. LiteSpeed does find the page in cache, so the cached page is served to Lauren:

    https://example.com/?greet=Lauren&reply=Hello%20world
    	x-litespeed-cache: hit
    

    Lauren goes back and presses the Forget about me … button, which triggers a purge of every page where LscwpApiTagDemo\greet.lauren is a tag.

    Because of this, when she goes back to the form and repeats I love LiteSpeed, the page is not found in the cache.

    https://example.com/?greet=LAUREN&reply=I%20love%20LiteSpeed
    	x-litespeed-cache: miss
    

    Conclusion

    As you can see, the API tag functions are a powerful way to customize the caching and purging behavior of your plugins. Without the API, your plugin would have to maintain its own record of which pages need to be purged from the cache when some event mandates it, and then send individual purge requests for each of those pages.

    Using LiteSpeed’s API hooks takes this burden off of your plugin and allows you to let the cache engine do the work.

    For more information about customized cache tags with LSCWP API and other API functions, please see our documentation.


    Thank you to Tynan Beatty for his contributions to this post, including the sample plugin code.

  • WpW: LSCache and your plugin – perfect together

    WpW: LSCache and your plugin – perfect together

    Welcome to another installment of WordPress Wednesday!

    Disclaimer: The information contained in this post is accurate for LSCWP v1.4. If you are using a newer version of the plugin, some details may have changed. Please refer to our wiki for the latest!

    In most of our previous posts we’ve talked to site owners and hosting providers. Today, we’d like to address our fellow plugin developers. Let’s talk about how to ensure your plugin is compatible with LiteSpeed Cache for WordPress. And for those of you who are developers and would like to help make someone else’s plugin work well with LSCache, you’ll want to check out “What if it’s not your plugin?” below.

    To know if your plugin is compatible with LSCache, it helps to understand a bit about caching in general, as well as LiteSpeed Cache in particular. For that, we recommend you have a look at our Caching 101 series.

    Are we compatible?

    If your plugin doesn’t generate site content, or do anything out of the ordinary with existing front-end content, then chances are it is 100% compatible with LiteSpeed Cache for WordPress already. The vast majority of plugins will fall into this “already compatible” category. If, however, you have a plugin that makes changes to front-end-displayed content, and it does so in a non-traditional way, you’ll want to read on.

    LSCWP purges pages from the cache when certain events are triggered. If your plugin generates content that triggers those same events, then we’re already compatible in that area.

    LSCWP follows a set of rules to determine whether a page is cacheable. If your plugin generates content that also abides by those same rules, then we’re already compatible in that area as well.

    Even if your plugin generates cacheable content that doesn’t trigger the standard events, or it generates non-cacheable content that doesn’t abide by the same rules that we do, we can still get along. You may just need to invoke our API first.

    First, let’s look at purging rules and exclude rules to understand what is possible, and then we can look at the API functions that allow you to realize those possibilities.

    Purge rules

    LSCache will automatically purge a page from the cache if any of the following events are triggered:

    • edit_post
    • save_post
    • deleted_post
    • trashed_post
    • delete_attachment

    LSCache’s “smart purge” feature works by assigning tags to each cacheable page, and then using those tags to intelligently group together a collection of pages to be purged later.

    In its most basic form, each page is tagged with its Post ID, and then sent to the server to be cached. When one of the above events is triggered for the page, that request will notify the server to purge all of the cache’s items that are also tagged with that page’s Post ID. Depending on the purge rules that have been defined by the site owner, this could include related category pages, tag pages, and monthly archives, among other things.

    If your plugin makes modifications that trigger those same events, then you should be good. If, however, you have an application that changes what appears on a page without triggering any of those events, then you will need to inform LiteSpeed Cache that a purge is necessary.

    Our API gives you the ability to customize the notifications sent to the server. As the page is stored in the cache, you can assign your own tags to the page so that later, it may be purged as part of a custom group. Multiple tags can be set on a single page, and a single tag may be used on multiple pages. This many-to-many mapping provides a flexible system enabling you to group pages in a variety of useful ways.

    Example:

    • Page #1 is tagged with MTPP_F.1, MTPP_G.4, MTPP_S.wyoming (because the page is in forum 1, group 4, and related to the state of Wyoming).
    • Page #2 is tagged with MTPP_F.1, MTPP_G.2, MTPP_S.iowa (because the page is in forum 1, group 2, and related to the state of Iowa).

    If a change is made where all pages tagged MTPP_F.1 need to be purged, the tag system enables the server to easily purge both Page #1 and Page #2. If a request is sent to the server indicating that pages tagged MTPP_S.wyoming need to be purged, then the tagging system knows to only purge Page #1.

    For this functionality, you’ll want to pay special attention to the API functions tag_add and purge.

    TIP: When defining tags, it is a good idea to give them a prefix that is unique to your application, that way you avoid stepping on anyone else’s tags. In this example, we’ve used MTPP_ for “my third-party plugin.”

    Exclude rules

    As of v1.4, LSCache considers a page to be non-cacheable if

    • It is an Admin page
    • It is a post request
    • is_trackback() is true
    • is_search() is true
    • No theme is used
    • The URI is found in the Do Not Cache URIs List
    • The post URL has a query string found in the Do Not Cache Query Strings List
    • The post has a category found in the Do Not Cache Categories List
    • The post has a tag found in the Do Not Cache Tags List
    • The request has a cookie found in the Do Not Cache Cookies List
    • The request has a user agent found in the Do Not Cache User Agents List

    If your plugin generates content that is non-cacheable but doesn’t fall into one of the above categories, you will need to inform LiteSpeed Cache that the content cannot be cached. Our API gives you the ability to do so via the hook_control and set_nocache API hook and function.

    API

    LiteSpeed Cache for WordPress’ API provides functions and hooks that allow you to customize the aspects of cache management that we discussed above. Listed below are eight useful API functions and hooks. There is more to our API, though, and you can get all of the latest details and available functions on our wiki.

    These functions are defined in plugins/litespeed-cache/includes/litespeed-cache-api.class.php and may be used in any hook point prior to the shutdown hook point.

    Customized Purging

    Use these functions to implement your own purge rules, as in our forum/group/state example above.

    • LiteSpeed_Cache_API::tag_add($tag)
      When a page is created or edited, use this function to add a single cache tag (or group of cache tags) to the list of cache tags associated with the current page. These tags are appended the list of built-in tags generated by LSCWP.
    • LiteSpeed_Cache_API::purge($tag)
      When you need to purge a page and its related pages, use this function to add a single purge tag (or group of purge tags) to the list of tags to be purged with the request.

    Customized Exclusions

    If you plugin generates content that should not be cached, use these functions to implement your own exclude rules.

    • LiteSpeed_Cache_API::set_nocache()
      Use this function to mark the current page as non-cacheable.
    • LiteSpeed_Cache_API::hook_control($hook)
      Specify a hook for cache control with this filter. The hook will be triggered when the cache plugin is checking whether the current page is cacheable. This filter will not trigger on admin pages nor any page that has previously been marked as non-cacheable.

    Customized Admin Handling

    If your plugin makes adjustments to content through WordPress Admin (for example, you change a product description and then need to purge that product) these are the functions to use.

    • LiteSpeed_Cache_API::purge_post($id)
      Purge a single post by id with this function.
    • LiteSpeed_Cache_API::hook_purge($hook)
      This hook is called at the end of every cacheable request, and gives you the ability to add purge tags to the current request.

    ESI Blocks

    If you’d like to use ESI blocks in your plugin, you’ll want to employ the following functions:

    • LiteSpeed_Cache_API::esi_url($block_id, $wrapper, $params = array(), $control = 'private,no-vary')
      Creates a new ESI block. Use this if you wish to punch a hole for differently-cached content (i.e. a privately-cached individualized greeting on a publicly-cached page).
    • LiteSpeed_Cache_API::hook_tpl_esi($block, $hook)
      Use this hook to display an ESI block.

    For some detailed ESI examples, visit our wiki.

    What if it’s not your plugin?

    Not every plugin author is interested in modifying their plugin for LSCache compatibility, and that’s ok. You don’t have to be the author of the plugin to make sure it is compatible with LiteSpeed Cache. In fact, we’ve been adding support for well-known plugins ourselves since the beginning.

    If you are currently using an incompatible plugin, and you have some PHP skills, you can contribute to our project. Take a look at our plugin source code, and you’ll see a thirdparty directory where you’ll find our current integrations. Use these examples to help you write up some code that will make your own favorite plugin compatible with LSCache.

    And then get in touch! We’d love to integrate your working solution into our plugin so that everyone may benefit.

    Conclusion

    The majority of plugins get along just fine with LSCache, and those that don’t generally just need a line or two of code to make them compatible. If the advice we’ve shared today doesn’t help you with your particular application, or you’d like to discuss any of these ideas in more depth, feel free to get in touch with us! Visit the WordPress support forum for LSCache, or go through our ticket system. We’d be happy to help.


    Have some of your own ideas for future WordPress Wednesday topics? Leave us a comment!

    Don’t forget to meet us back here next week for the next installment. In the meantime, here are a few other things you can do: