关键点:由于需要缓存页面,因此页面中不能包含个人数据及动态数据。
使用<esi:include>标签优化
1、通过使用shortcode([esi src=”/esi/cart-fragment/”])或者函数(<?php esi_include(‘/esi/cart-fragment/’);?>)的方式,在个人数据/动态数据部分进行替换。
2、通过注册endpoints,使varnish访问该endpoints时,获取动态数据,替换esi标签内容。
<?php
/*
Plugin Name: ESI for Varnish - Simple Fragments
Description: Provide simple ESI fragment endpoints and helpers for WordPress + WooCommerce to be used with Varnish ESI. Adds endpoints: /esi/cart-fragment/, /esi/header-user/, /esi/wc-mini-cart/. Includes template helper esi_include() and shortcode [esi src="/esi/..."].
Version: 0.9
Author: RQQ (generated)
License: GPLv2+
*/
if (! defined('ABSPATH')) {
exit;
}
/**
* -----------------------
* Configuration (adjust)
* -----------------------
*/
if (! defined('ESI_CACHE_TTL')) define('ESI_CACHE_TTL', 30); // seconds for dynamic fragments (keep small)
if (! defined('ESI_CACHE_PRIVATE')) define('ESI_CACHE_PRIVATE', true); // fragments often per-user -> private or no-cache
if (! defined('ESI_VARNISH_PURGE_URL')) define('ESI_VARNISH_PURGE_URL', ''); // e.g. http://127.0.0.1:80/ (empty = disabled)
if (! defined('ESI_ALLOWED_ORIGINS')) define('ESI_ALLOWED_ORIGINS', ''); // optional for CORS if needed
/**
* -----------------------
* Helpers
* -----------------------
*/
function esi_send_fragment_headers( $is_private = true, $ttl = ESI_CACHE_TTL ) {
// Surrogate-Control indicates to Varnish that the response may contain ESI or be handled specially
header_remove('Cache-Control'); // ensure consistent
if ( $is_private ) {
// private fragment (per user) -> don't be cached by shared caches for others; but Varnish can still request it and insert into user response
header('Cache-Control: private, max-age=' . intval($ttl));
} else {
header('Cache-Control: public, max-age=' . intval($ttl));
}
// Informative header for Varnish ESI usage (some setups use Surrogate-Control)
header('Surrogate-Control: ESI/1.0');
if (ESI_ALLOWED_ORIGINS) {
header('Access-Control-Allow-Origin: ' . ESI_ALLOWED_ORIGINS);
}
header('Content-Type: text/html; charset=UTF-8');
}
/**
* Utility: purge key on Varnish (optional)
* Tries to call a simple PURGE (or BAN) URL if configured. Very simple implementation.
*/
function esi_varnish_purge( $path ) {
if ( empty(ESI_VARNISH_PURGE_URL) ) return false;
$url = rtrim(ESI_VARNISH_PURGE_URL, '/') . $path;
// do a simple request; suppress failures
$args = array(
'method' => 'PURGE',
'timeout' => 2,
'redirection' => 0,
);
// WP HTTP may not allow PURGE method; try with custom header POST fallback
$response = wp_remote_request( $url, $args );
return $response;
}
/**
* -----------------------
* Register endpoints (rewrite + template redirect)
* -----------------------
*/
// Add rewrite rules on init
add_action('init', function () {
add_rewrite_rule('^esi/cart-fragment/?$', 'index.php?esi_fragment=cart-fragment', 'top');
add_rewrite_rule('^esi/header-user/?$', 'index.php?esi_fragment=header-user', 'top');
add_rewrite_rule('^esi/wc-mini-cart/?$', 'index.php?esi_fragment=wc-mini-cart', 'top');
add_rewrite_tag('%esi_fragment%', '([^&]+)');
});
// Handle template redirect for endpoints
add_action('template_redirect', function () {
$frag = get_query_var('esi_fragment');
if ( ! $frag ) return;
// Prevent WP themes from sending their normal HTML wrapper
// We respond and exit.
if ($frag === 'cart-fragment') {
// Very small HTML fragment: cart count + mini html
// Use WC functions if available
if ( function_exists('WC') ) {
$count = WC()->cart ? WC()->cart->get_cart_contents_count() : 0;
} else {
$count = 0;
}
esi_send_fragment_headers( true, ESI_CACHE_TTL );
// Example structure — keep minimal so insertion is fast
echo '<div class="esi-cart-fragment" data-esi="cart-fragment">';
echo '<a class="esi-mini-cart" href="' . esc_url( wc_get_cart_url() ? wc_get_cart_url() : '/cart/' ) . '">';
echo '<span class="esi-cart-count">' . intval( $count ) . '</span>';
echo '</a></div>';
exit;
}
if ($frag === 'header-user') {
esi_send_fragment_headers( true, ESI_CACHE_TTL );
if ( is_user_logged_in() ) {
$current = wp_get_current_user();
echo '<div class="esi-header-user">Hello, ' . esc_html( $current->display_name ) . ' | <a href="' . esc_url( wp_logout_url() ) . '">Logout</a></div>';
} else {
echo '<div class="esi-header-user"><a href="' . esc_url( wp_login_url() ) . '">Login</a></div>';
}
exit;
}
if ($frag === 'wc-mini-cart') {
// More advanced mini-cart HTML — keep concise
esi_send_fragment_headers( true, ESI_CACHE_TTL );
if ( function_exists('WC') ) {
$items = WC()->cart->get_cart();
echo '<div class="esi-wc-mini-cart">';
if ( empty( $items ) ) {
echo '<div class="empty">Cart empty</div>';
} else {
echo '<ul>';
foreach ( $items as $cart_item_key => $cart_item ) {
$p = $cart_item['data'];
$title = $p ? $p->get_name() : '';
echo '<li>' . esc_html( $title ) . ' x ' . intval( $cart_item['quantity'] ) . '</li>';
}
echo '</ul>';
}
echo '</div>';
} else {
echo '<div class="esi-wc-mini-cart empty">Cart unavailable</div>';
}
exit;
}
// // default: 404
// status_header(404);
// exit;
});
/**
* -----------------------
* Template helper and shortcode
* -----------------------
*/
/**
* Output an ESI include tag (prints raw <esi:include ... />).
* Use this in theme templates where you want a fragment.
*
* Example: <?php esi_include('/esi/cart-fragment/'); ?>
*/
function esi_include( $path ) {
$path = esc_url( $path );
echo '<esi:include src="' . esc_attr( $path ) . '" />';
return;
}
/**
* Shortcode [esi src="/esi/cart-fragment/"]
*/
add_shortcode('esi', function ( $atts ) {
$a = shortcode_atts( array(
'src' => '',
), $atts, 'esi' );
$src = trim( $a['src'] );
if ( empty( $src ) ) return '';
return esi_include($src); //只需内部的相对url
});
/**
* -----------------------
* Auto-insert into Astra header (best-effort, non-destructive)
* -----------------------
* If Astra provides hook 'astra_header_after' we add an ESI cart fragment there.
* This is optional and only runs if the hook exists.
*/
// if ( has_action('astra_header_after') ) {
// add_action('astra_header_after', function () {
// // print ESI include for cart fragment — keep minimal HTML wrapper
// esi_include('/esi/cart-fragment/');
// }, 5 );
// }
/**
* -----------------------
* Cache invalidation hooks
* -----------------------
* When post saved / product updated / order updated, remove fragment transients and optionally call Varnish purge.
*/
function esi_purge_fragments_for_post( $post_id ) {
// delete any fragment caches if you use object cache keys here (example)
// This plugin uses live generation for fragments, but we still trigger varnish purge if configured
if ( defined('ESI_VARNISH_PURGE_URL') && ESI_VARNISH_PURGE_URL ) {
// purge specific fragment URLs
$paths = array(
'/esi/cart-fragment/',
'/esi/header-user/',
'/esi/wc-mini-cart/',
);
foreach ( $paths as $p ) {
esi_varnish_purge( $p );
}
}
}
add_action('save_post', 'esi_purge_fragments_for_post', 10, 1);
add_action('woocommerce_update_product', 'esi_purge_fragments_for_post', 10, 1);
add_action('woocommerce_cart_updated', 'esi_purge_fragments_for_post', 10, 1);
add_action('woocommerce_update_order', 'esi_purge_fragments_for_post', 10, 1);
/**
* -----------------------
* Admin: simple settings link
* -----------------------
*/
add_filter('plugin_action_links_' . plugin_basename(__FILE__), function ( $links ) {
$links[] = '<a href="' . admin_url( 'options-general.php' ) . '">Settings</a>';
return $links;
});
/**
* -----------------------
* Activation: flush rewrite rules
* -----------------------
*/
register_activation_hook( __FILE__, function () {
// flush rewrite rules so our /esi/* rules take effect
flush_rewrite_rules();
});
register_deactivation_hook( __FILE__, function () {
flush_rewrite_rules();
});
使用cart_fragments方式处理动态数据
1、页面中个人数据及动态数据留空,并做好css标记,以便ajax进行替换
2、注册fragments
/**
该代码执行后,会向后台发送ajax请求,到 /?wc-ajax=get_refreshed_fragments ,返回数据格式示例:
{
"fragments": {
"h2.elementor-heading-title": "aaaaaaaaaaaaaaaaa"
},
"cart_hash": "bc522a0236b33854f5bc36d6f538f6a7"
}
DOM树的h2.elementor-heading-title选择器的内容,会被替换为aaaaaaaaaaaaaaaaa
*/
add_filter('woocommerce_add_to_cart_fragments',function($fragments){
$fragments['h2.elementor-heading-title'] = 'aaaaaaaaaaaaaaaaa';
return $fragments;
}, 20);