Skip to content

Developer notes

  1. init.php runs while the add-ons load, at priority 100. The session and storefront are known; language, currency, route and user are not.
  2. Request::bypassReason() looks at the URL and the session. Unknown means bypass.
  3. Request::key() builds an HMAC (with the store’s crypt_key) of host, scheme, storefront, company, normalised URL, what decides the language (session choice, browser language), the session currency, device class, WebP support and crawler flag.
  4. A valid file is sent, with the visitor’s form token put in, and the request ends. The other add-ons never load.
  5. On a miss, before_dispatch (priority 99999) decides whether the render may be stored and starts an output buffer. Block cache is off for that render, so every product of the page passes through get_products_post.
  6. The complete hook checks the response and stores it.

Language is never guessed: the same inputs give the same core result, so the inputs form the key. sl and currency in a URL always bypass, because core must write the choice to the session.

Dispatch on the list and section on; start of request: no merchant-listed session key, no advertising parameter, no unknown cookie; end of request: status 200, index.tpl, HTML content type, complete document, no notification, no PHP message, session still acceptable, no cookie set except the session cookie, no session id and no visitor IP in the page, no form token other than the visitor’s, no product purged during the render, no Recently viewed block for a visitor with history.

Response headers are not stored, except Content-Security-Policy, X-Frame-Options, Strict-Transport-Security, Referrer-Policy, Permissions-Policy and X-Content-Type-Options.

Any session key that is not on the list stops the cache for that visitor, and the admin page names the key. That is the safe result. If the key has no effect on pages, list it in Additional safe session keys.

Print it from AJAX or JavaScript after load. What the core 4.21 Full page cache needs also works here. Do not read a cookie in a template: a visitor with that cookie never creates a stored page, but does receive one.

schemas/kv_full_page_cache/compat.php lists two groups:

  • late: access_restrictions, kv_bot_country_blocker. Pages are sent in before_dispatch, after all add-ons have loaded.
  • off: age_verification. The cache stays off.

Pseudo-cron style work that runs on storefront page views does not run on a cache hit.

db_query_executed watches writes. schemas/kv_full_page_cache/invalidation.php lists product tables (the statement must name product_id, otherwise all pages are dropped), shared tables whose product rows count as product changes, and global tables. Purges run once at the end of the request. Writes that bypass db_* are not seen; the page lifetime covers them.

On a stored product page view the add-on still records the recently viewed list and popularity. Other server side work of a product page does not run, the same as under the core Varnish cache.

var/kv_full_page_cache/generation current generation
var/kv_full_page_cache/g<generation>/aa/<key>.kvc magic line, JSON meta line, gzip body
var/kv_full_page_cache/warm.json warmer progress

Tables: ?:kv_fpc_entries, ?:kv_fpc_tags, ?:kv_fpc_purges, ?:kv_fpc_stats.

Classes (Tygh\Addons\KvFullPageCache): Engine, Request, Store, Invalidator, Warmer, Stats, Dashboard, Conflicts, Config, License\Client, Upgrade\Synchronizer.

db_query_executed, before_dispatch, dispatch_before_display, render_block_pre, get_products_post, get_product_data_post, complete, clear_cache_post, update_product_amount. All exist with the same arguments in 4.17.2 and 4.21.2.

addon.xml uses priority 100 on purpose: everything loaded after it is skipped on a hit. init.php loads func.php itself, because core loads it after init.php and a query in between already fires db_query_executed.