{"id":12525,"date":"2023-03-06T06:00:17","date_gmt":"2023-03-06T11:00:17","guid":{"rendered":"https:\/\/blog.litespeedtech.com\/?p=12525"},"modified":"2023-03-06T06:00:17","modified_gmt":"2023-03-06T11:00:17","slug":"customized-cache-tags-with-lscwp-api","status":"publish","type":"post","link":"https:\/\/lswp.store\/index.php\/2023\/03\/06\/customized-cache-tags-with-lscwp-api\/","title":{"rendered":"Customized Cache Tags With LSCWP API"},"content":{"rendered":"<p><img fetchpriority=\"high\" decoding=\"async\" class=\"alignnone size-full wp-image-12527\" src=\"https:\/\/lswp.store\/wp-content\/uploads\/2023\/03\/lscwp-tags-1.png\" alt=\"Customized Cache Tags With LSCWP API\" width=\"1000\" height=\"500\" srcset=\"https:\/\/lswp.store\/wp-content\/uploads\/2023\/03\/lscwp-tags-1.png 1000w, https:\/\/lswp.store\/wp-content\/uploads\/2023\/03\/lscwp-tags-1-300x150.png 300w, https:\/\/lswp.store\/wp-content\/uploads\/2023\/03\/lscwp-tags-1-768x384.png 768w, https:\/\/lswp.store\/wp-content\/uploads\/2023\/03\/lscwp-tags-1-600x300.png 600w\" sizes=\"(max-width: 1000px) 100vw, 1000px\" \/><\/p>\n<p>LiteSpeed Cache, the powerful cache engine that ships with all LiteSpeed server products, features the clever \u201csmart purge\u201d 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.<\/p>\n<p>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 <em>your own<\/em> special way, so that they may be purged by <em>your own<\/em> unique triggering event. How cool would that be?<\/p>\n<p>Well, it turns out, you can build this functionality into your plugin with the tag-based actions of the LSCWP API.<\/p>\n<h2>LSCache Tags<\/h2>\n<p>Before we get into creating your own custom tags, let\u2019s look at LSCWP\u2019s built-in tagging behavior.<\/p>\n<p>Here\u2019s an example that shows the \u201csmart purge\u201d system in action:<\/p>\n<p>You have the following post on your WordPress site:<\/p>\n<ul>\n<li><strong>Title<\/strong>: <code>Unique American Towns<\/code><\/li>\n<li><strong>Category<\/strong>: <code>United States<\/code><\/li>\n<\/ul>\n<p>People visit your site, and LiteSpeed caches all of the pages that your visitors read, including the <code>Unique American Towns<\/code> post page.<\/p>\n<p>Later, you edit that post, change the <strong>Title<\/strong> and add a second <strong>Category<\/strong>:<\/p>\n<ul>\n<li><strong>Title<\/strong>: <code>Unique North American Towns<\/code><\/li>\n<li><strong>Category<\/strong>: <code>United States<\/code>, <code>Canada<\/code><\/li>\n<\/ul>\n<p>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:<\/p>\n<ul>\n<li>The post itself<\/li>\n<li>The front page and\/or home page of your site<\/li>\n<li>The author archive page<\/li>\n<li>The post type archive page<\/li>\n<li>The monthly archive page<\/li>\n<li>The <code>United States<\/code> and <code>Canada<\/code> category pages<\/li>\n<\/ul>\n<p>You can change the rules to purge even more pages than this (or fewer), if it\u2019s appropriate for your website.<\/p>\n<p>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\u2019s pages).<\/p>\n<p>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.<\/p>\n<h2>Customized cache tags with LSCWP API<\/h2>\n<p>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).<\/p>\n<p>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.<\/p>\n<p>Here\u2019s a simple example: Let\u2019s 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:<\/p>\n<p>Add a custom <code>$tag<\/code> to the cached page:<\/p>\n<pre>\ndo_action( 'litespeed_tag_add', $tag );\n<\/pre>\n<p>Purge all pages tagged with the custom <code>$tag<\/code>:<\/p>\n<pre>\ndo_action( 'litespeed_purge', $tag );\n<\/pre>\n<h2>Example<\/h2>\n<p>Here\u2019s 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\u2019s input values.<\/p>\n<h3>Basic Plugin<\/h3>\n<p>For this plugin, we\u2019ll use a query string parameter called <code>greet<\/code>, which will store the visitor\u2019s name.<\/p>\n<pre>\n&lt;?php\n\/**\n * Plugin Name:  LiteSpeed Cache API Tag Demo\n * ...\n *\/\nnamespace LscwpApiTagDemo;\ndefined( 'WPINC' ) || exit;\nadd_action( 'init', function () {\n\tif ( ! isset( $_GET['greet'] ) ) {\n\t\treturn;\n\t}\n\t$visitor = $_GET['greet'];\n\t# ...\n<\/pre>\n<h3>Dynamic Tag Name<\/h3>\n<p>We create a custom tag based on the value of the <code>greet<\/code> parameter. So, if our URL was <code>https:\/\/.example.com\/?greet=Lauren<\/code>, our custom tag would be <code>LscwpApiTagDemo\\greet.lauren<\/code>, and we would add that tag to the current page.<\/p>\n<pre>\nadd_action( 'init', function () {\n\tif ( ! isset( $_GET['greet'] ) ) {\n\t\treturn;\n\t}\n\t$visitor = $_GET['greet'];\n\t$tag =\n\t\t__NAMESPACE__ . '\\greet.'\n\t\t. mb_strtolower( $visitor, 'UTF-8' );\n\t$visitor = stripslashes( $visitor );\n\tdo_action( 'litespeed_tag_add', $tag );\n\t# ...\n<\/pre>\n<h3>Greeting Content<\/h3>\n<p>We replace WordPress&#8217;s content loop with our greeting.<\/p>\n<pre>\nadd_action( 'init', function () {\n\t# ...\n\tadd_action( 'loop_start', 'ob_start', 0, 0 );\n\tadd_action( 'loop_end', function () use ( $visitor ) {\n\t\tob_end_clean();\n\t\t?&gt;\n\t\t&lt;article class=\"page entry\"&gt;\n\t\t\t&lt;div class=\"entry-content\"&gt;\n\t\t\t\t&lt;?php greet( $visitor ); ?&gt;\n\t\t\t&lt;\/div&gt;\n\t\t&lt;\/article&gt;\n\t\t&lt;?php\n\t}, 0, 999 );\n} );\n<\/pre>\n<h3>Greeting Function<\/h3>\n<p>We display a message and include a form that accepts a reply.<\/p>\n<pre>\nfunction greet( $visitor ) {\n\t?&gt;\n\t&lt;h2&gt;&lt;?php\n\t\tprintf( esc_html__( 'Howdy, %1$s!' ), $visitor );\n\t?&gt;&lt;\/h2&gt;\n\t&lt;form method=\"get\"&gt;\n\t\t&lt;input name=\"greet\" type=\"hidden\"\n\t\t\tvalue=\"&lt;?php echo $visitor; ?&gt;\"&gt;\n\t\t&lt;label for=\"reply\"&gt;\n\t\t\t&lt;h3&gt;&lt;?php esc_html_e( 'Care to Respond?' ); ?&gt;&lt;\/h3&gt;\n\t\t&lt;\/label&gt;\n\t\t&lt;input id=\"reply\" name=\"reply\" type=\"text\" value=\"\"\n\t\t\tplaceholder=\"&lt;?php\n\t\t\t\tesc_html_e( 'Type your reply here ...' );\n\t\t\t?&gt;\"&gt;\n\t\t&lt;button type=\"submit\"&gt;&lt;?php\n\t\t\tesc_html_e( 'Send' );\n\t\t?&gt;&lt;\/button&gt;\n\t&lt;\/form&gt;\n\t&lt;?php\n}\n<\/pre>\n<h3>Reply Content<\/h3>\n<p>We revise our main function to add a condition that displays alternative content if the user replied to our greeting.<\/p>\n<pre>\nadd_action( 'init', function () {\n\t# ...\n\t\t?&gt;\n\t\t&lt;article class=\"page entry\"&gt;\n\t\t\t&lt;div class=\"entry-content\"&gt;\n\t\t\t\t&lt;?php\n\t\t\t\tif ( isset( $_GET['reply'] ) ) {\n\t\t\t\t\treply( $visitor );\n\t\t\t\t} else {\n\t\t\t\t\tgreet( $visitor );\n\t\t\t\t}\n\t\t\t\t?&gt;\n\t\t\t&lt;\/div&gt;\n\t\t&lt;\/article&gt;\n\t\t&lt;?php\n\t}, 0, 999 );\n} );\n<\/pre>\n<h3>Reply Function<\/h3>\n<p>We display a thank you note and show that we remember the user&#8217;s reply.<\/p>\n<pre>\nfunction reply( $visitor ) {\n\t?&gt;\n\t&lt;h2&gt;&lt;?php\n\t\tprintf( esc_html__( 'Thank you, %1$s.' ), $visitor );\n\t?&gt;&lt;\/h2&gt;\n\t&lt;h3&gt;&lt;?php\n\t\tesc_html_e( \"I'll always remember your kind words.\" );\n\t?&gt;&lt;\/h3&gt;\n\t&lt;?php\n\t$reply = stripslashes( $_GET['reply'] );\n\tif ( ! empty( $reply ) ) {\n\t\t?&gt;\n\t\t&lt;blockquote&gt;&lt;?php\n\t\t\techo esc_html( $reply );\n\t\t?&gt;&lt;\/blockquote&gt;\n\t\t&lt;?php\n\t}\n\t# ...\n<\/pre>\n<p>And we add a <strong>Forget about me \u2026<\/strong> button that will trigger a cache purge targeting every item associated with the visitor&#8217;s name.<\/p>\n<pre>\nfunction reply( $visitor ) {\n\t# ...\n\t}\n\t?&gt;\n\t&lt;form method=\"post\" action=\"&lt;?php\n\t\t\techo drop_query_var(\n\t\t\t\t$_SERVER['REQUEST_URI'],\n\t\t\t\t'reply'\n\t\t\t);\n\t\t?&gt;\"&gt;\n\t\t&lt;input name=\"reset\" type=\"hidden\"&gt;\n\t\t&lt;button type=\"submit\"&gt;&lt;?php\n\t\t\tesc_html_e( 'Forget about me ...' );\n\t\t?&gt;&lt;\/button&gt;\n\t&lt;\/form&gt;\n\t&lt;?php\n}\n<\/pre>\n<h3>Putting It All Together<\/h3>\n<p>We update our main function once more, so that any page tagged <code>LscwpApiTagDemo\\greet.lauren<\/code> will be deleted if the button is pressed.<\/p>\n<pre>\nadd_action( 'init', function () {\n\t# ...\n\t$tag =\n\t\t__NAMESPACE__ . '\\greet.'\n\t\t. mb_strtolower( $visitor, 'UTF-8' );\n\t$visitor = stripslashes( $visitor );\n\tif ( isset( $_POST['reset'] ) ) {\n\t\tdo_action( 'litespeed_purge', $tag );\n\t}\n\tdo_action( 'litespeed_tag_add', $tag );\n\t# ...\n<\/pre>\n<h3>Behavior<\/h3>\n<p>So, here\u2019s how that looks in action.:<\/p>\n<p>Lauren visits the site and provides her name. LiteSpeed doesn\u2019t find the page in cache, so the page is generated, served to Lauren, and cached:<\/p>\n<pre>\nhttps:\/\/example.com\/?greet=lauren\n\tx-litespeed-cache: miss\n<\/pre>\n<p>She fills in the form saying, <code>Hello world<\/code>. LiteSpeed doesn\u2019t find the page in cache, so the page is generated, served to Lauren, and cached:<\/p>\n<pre>\nhttps:\/\/example.com\/?greet=Lauren&reply=Hello%20world\n\tx-litespeed-cache: miss\n<\/pre>\n<p>She fills in the form again saying, <code>I love LiteSpeed<\/code>. LiteSpeed doesn\u2019t find the page in cache, so the page is generated, served to Lauren, and cached:<\/p>\n<pre>\nhttps:\/\/example.com\/?greet=LAUREN&reply=I%20love%20LiteSpeed\n\tx-litespeed-cache: miss\n<\/pre>\n<p>Note that the <code>greet<\/code> parameter is not case sensitive. Our plugin converts it to lowercase before setting the <code>$tag<\/code> value..<\/p>\n<p>She goes back to the form again saying, <code>Hello world<\/code> one more time. LiteSpeed does find the page in cache, so the cached page is served to Lauren:<\/p>\n<pre>\nhttps:\/\/example.com\/?greet=Lauren&reply=Hello%20world\n\tx-litespeed-cache: hit\n<\/pre>\n<p>Lauren goes back and presses the <strong>Forget about me \u2026<\/strong> button, which triggers a purge of every page where <code>LscwpApiTagDemo\\greet.lauren<\/code> is a tag.<\/p>\n<p>Because of this, when she goes back to the form and repeats <code>I love LiteSpeed<\/code>, the page is not found in the cache.<\/p>\n<pre>\nhttps:\/\/example.com\/?greet=LAUREN&reply=I%20love%20LiteSpeed\n\tx-litespeed-cache: miss\n<\/pre>\n<h2>Conclusion<\/h2>\n<p>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.<\/p>\n<p>Using LiteSpeed\u2019s API hooks takes this burden off of your plugin and allows you to let the cache engine do the work.<\/p>\n<p>For more information about customized cache tags with LSCWP API and other API functions, please see <a href=\"https:\/\/docs.litespeedtech.com\/lscache\/lscwp\/api\/\">our documentation<\/a>.<\/p>\n<p>&#8212;<br \/>\nThank you to Tynan Beatty for his contributions to this post, including the sample plugin code.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Use customized cache tags with LSCWP API to group WordPress pages in your own way and purge together with a unique custom triggering event.<\/p>\n","protected":false},"author":1,"featured_media":12527,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[28],"tags":[38,316],"class_list":["post-12525","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cache","tag-api","tag-wordpress"],"jetpack_featured_media_url":"https:\/\/lswp.store\/wp-content\/uploads\/2023\/03\/lscwp-tags-1.png","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/posts\/12525","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/comments?post=12525"}],"version-history":[{"count":0,"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/posts\/12525\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/media\/12527"}],"wp:attachment":[{"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/media?parent=12525"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/categories?post=12525"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/tags?post=12525"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}