Update the public pages to 0.3.1
This commit is contained in:
@@ -1,11 +1,39 @@
|
||||
# Nolimit Cache — release feed
|
||||
# Nolimit Cache
|
||||
|
||||
Built zips only. The source lives in `kw/nolimit-cache`, which is private.
|
||||

|
||||
|
||||
This repository is **public on purpose**: every WordPress site running the
|
||||
plugin asks it for updates, and it never sends credentials. A private feed
|
||||
answers 404 and the update simply never appears — no error on the site, no
|
||||
entry in the plugins list.
|
||||
A page cache and front-end optimiser for WordPress and WooCommerce. It answers
|
||||
a visitor's request from a file on disk before WordPress boots, and it knows
|
||||
enough about WooCommerce not to do that to a customer holding a basket. WebP
|
||||
and AVIF copies of your images, Redis object cache, preloading and a set of
|
||||
health checks are in the same plugin — with **nothing leaving the site**. No
|
||||
account, no licence check, no telemetry.
|
||||
|
||||
Nothing here is edited by hand. Each release is published by the tag job in
|
||||
the source repository.
|
||||
## Install
|
||||
|
||||
1. Download `nolimit-cache.zip` from the [latest release](https://gitea.nolimit.dk/kw/nolimit-cache-dist/releases/latest).
|
||||
2. **Plugins → Add New → Upload Plugin**, then activate.
|
||||
3. Open **Nolimit → Cache** and let the setup walk you through it — [the setup
|
||||
guide](docs/SETUP.md) explains what it changes and how to tell it is working.
|
||||
|
||||
Later versions arrive in **Dashboard → Updates** like any other plugin.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Overview](docs/README.md) — what it does, and what it deliberately does not
|
||||
- [Installing it, and telling whether it works](docs/SETUP.md) — the settings
|
||||
that need a decision, images in WebP and AVIF, and how to check the cache is
|
||||
really serving
|
||||
- [Hooks and integration](docs/CONTRACT.md) — for developers: what the plugin
|
||||
offers to other plugins and to your own code
|
||||
|
||||
## More of it
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
|  | **Page cache.** How long a page lives, what is never cached whatever you set, and separate copies for phones or signed-in visitors only if you ask for them. |
|
||||
|  | **Images.** WebP and AVIF copies written beside your JPEG and PNG files and offered to the browsers that understand them. Originals are never changed. |
|
||||
|  | **WooCommerce.** The basket, checkout and account pages are never cached, and a product page is cleared when its stock or an order changes. |
|
||||
|  | **Tools.** What it has installed in `wp-content`, written out plainly, and a button to write it again. |
|
||||
|
||||
Requires WordPress 6.0+ and PHP 7.4+. WooCommerce is optional.
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 103 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 143 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 162 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 85 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 155 KiB |
@@ -0,0 +1,91 @@
|
||||
# Hooks and integration
|
||||
|
||||
What Nolimit Cache offers to other code, and what it listens to. Written for
|
||||
developers who want to extend the plugin or sit next to it; nothing here is
|
||||
needed to use it.
|
||||
|
||||
Hooks prefixed `nlcac_` are this plugin's own and are stable: a rename is a
|
||||
breaking change and will be called out in the release notes. Hooks prefixed
|
||||
`wpmgr_` are shared by name with the other Nolimit plugins (see section 2).
|
||||
|
||||
A note on the words: for a filter, `add_filter()` is how you **answer** it, by
|
||||
supplying a value. The headings below describe the mechanism, who calls
|
||||
`apply_filters()` and who calls `add_filter()`, because that cannot be misread.
|
||||
|
||||
---
|
||||
|
||||
## 1. Extension points this plugin OFFERS — `nlcac_*`
|
||||
|
||||
`apply_filters()` / `do_action()` in our code: we run everyone's callbacks.
|
||||
|
||||
| Hook | Type | Signature | Fired from |
|
||||
|---|---|---|---|
|
||||
| `nlcac_reason_not_to_cache` | filter | `( string $reason, … )` — return a non-empty string to refuse to cache this request | page cache |
|
||||
| `nlcac_page_cached` | action | `( string $path )` — a page was just written to disk | page cache |
|
||||
| `nlcac_purge_urls_for_post` | filter | `( array $urls, int $post_id )` — the URLs about to be purged for a post | purging |
|
||||
| `nlcac_preload_urls` | filter | `( array $urls )` — the queue the preloader will walk | preloader |
|
||||
| `nlcac_exclude_script` | filter | `( bool $exclude, string $handle )` — keep a script out of defer | optimiser |
|
||||
| `nlcac_ai_payload` | filter | `( array $payload )` — what the assistant is about to send | assistant |
|
||||
| `nlcac_images_skip` | filter | `( bool $skip, string $img_tag )` — return true to keep one `<img>` out of the WebP/AVIF rewrite | images |
|
||||
|
||||
**If this plugin is absent**, none of these fire. A consumer must not assume
|
||||
its callback ever runs; `has_filter()` is not a substitute for the plugin
|
||||
being active.
|
||||
|
||||
**A consumer must not** do slow or networked work in
|
||||
`nlcac_reason_not_to_cache`. It runs on every request, before WordPress, in
|
||||
the drop-in — which is the whole reason the cache is fast.
|
||||
|
||||
## 2. The family's shared hooks — `wpmgr_*`
|
||||
|
||||
Shared by name across the Nolimit plugins, so **load order decides who wins**
|
||||
for anything that declares itself once.
|
||||
|
||||
### Offered here — we call `apply_filters()` / `do_action()`
|
||||
|
||||
| Hook | Type | Notes |
|
||||
|---|---|---|
|
||||
| `wpmgr_cache_cleared` | action | `( int $removed )`. Fired when the page cache is emptied, so a sibling holding derived state can drop it in the same click. Our own object cache listens to it and only calls `wp_cache_flush()`, so there is no path back into purge. |
|
||||
| `wpmgr_dashboard_cards` | filter | Provided by the shared menu file, which every Nolimit plugin carries identically. |
|
||||
| `wpmgr_menu_title` | filter | Same. |
|
||||
| `wpmgr_menu_glyph` | filter | Same. It exists so the glyph can be overridden. |
|
||||
|
||||
### Answered here — we register a callback with `add_filter()` / `add_action()`
|
||||
|
||||
| Hook | Type | What we do | If nobody offers it |
|
||||
|---|---|---|---|
|
||||
| `wpmgr_dashboard_cards` | filter | Add this plugin's card | The card is not drawn; nothing breaks |
|
||||
| `wpmgr_menu_title` | filter | Answer the shared menu's title | Shared file's default |
|
||||
| `wpmgr_cache_cleared` | action | Flush our object cache | — (we also fire it) |
|
||||
| `wpmgr_signals` | filter | Supply this plugin's health signals to a family-wide collection. We answer it; we never read it | Only Nolimit Assistant applies this filter. With it absent or inactive, our signals are contributed to nothing and no error is raised. |
|
||||
| `wpmgr_setup_recommendations` | filter | Offer three setup items to the Assistant's Setup screen: turn the page cache on, warm it in the background, keep the basket rule on. Every `done` is read from the stored setting, never measured. | Nothing is offered and nothing breaks. Only Nolimit Assistant applies this filter, so without it these items are shown nowhere |
|
||||
|
||||
## 3. WordPress and WooCommerce core
|
||||
|
||||
The plugin also attaches to WordPress and WooCommerce core hooks. They are
|
||||
listed by area rather than one row each, because none of them is a contract
|
||||
anyone else needs to honour: they are how the plugin attaches to WordPress.
|
||||
|
||||
| Area | Hooks |
|
||||
|---|---|
|
||||
| Boot | `init`, `plugins_loaded` |
|
||||
| Admin | `admin_init`, `admin_menu`, `admin_notices`, `admin_head`, `admin_footer`, `admin_bar_menu`, `admin_enqueue_scripts`, `plugin_action_links_{file}` |
|
||||
| Purging | `transition_post_status`, `post_updated`, `deleted_post`, `edit_term`, `delete_term`, `comment_post`, `transition_comment_status`, `update_option_sidebars_widgets`, `shutdown` |
|
||||
| Front end | `template_redirect`, `wp_head`, `wp_enqueue_scripts`, `script_loader_tag`, `script_loader_src`, `style_loader_src`, `the_content`, `wp_get_attachment_url`, `wp_calculate_image_srcset`, `embed_oembed_discover`, `tiny_mce_plugins` |
|
||||
| Updates | `site_transient_update_plugins`, `plugins_api`, `upgrader_process_complete` |
|
||||
| Media | `wp_update_attachment_metadata`, `wp_delete_file` |
|
||||
| Settings | `update_option_nlcac_settings` |
|
||||
| Scheduling | `cron_schedules` |
|
||||
| REST | `rest_api_init` |
|
||||
| WooCommerce | `woocommerce_new_product`, `woocommerce_update_product`, `woocommerce_order_status_changed`, `woocommerce_product_set_stock`, `woocommerce_product_set_stock_status`, `woocommerce_variation_set_stock`, `woocommerce_variation_set_stock_status`, `wc_after_products_starting_sales`, `wc_after_products_ending_sales` |
|
||||
|
||||
🔴 **`site_transient_update_plugins` is the dangerous one.** The filter name
|
||||
is built from the update host, so every plugin updating from
|
||||
gitea.nolimit.dk lands on the same one and WordPress runs every callback for
|
||||
every plugin. Answering unconditionally offers our zip to a sibling. The
|
||||
guard is two halves and both are required — register with **3** arguments,
|
||||
and return `$update` untouched unless `$plugin_file` is ours. Registering
|
||||
with 2 leaves the argument unset and the guard silently does nothing, which
|
||||
is the same shape of bug it exists to fix.
|
||||
|
||||
If you write a plugin that updates from the same host, apply the same guard.
|
||||
@@ -0,0 +1,54 @@
|
||||
# Nolimit Cache
|
||||
|
||||
A page cache and front-end optimiser for WordPress and WooCommerce. It answers
|
||||
a visitor's request from a file on disk before WordPress boots, and it knows
|
||||
enough about WooCommerce not to do that to a customer holding a basket.
|
||||
|
||||
It is one of a family of Nolimit plugins that share an admin menu and a
|
||||
dashboard. [Hooks and integration](CONTRACT.md) lists what it offers to the
|
||||
others, and to your own code.
|
||||
|
||||
## What it does
|
||||
|
||||
- **Page cache.** An `advanced-cache.php` drop-in serves a stored copy of a
|
||||
page. This runs before the theme, the plugins and most of WordPress, which
|
||||
is where the speed comes from and also why it has to be careful.
|
||||
- **Object cache.** An optional `object-cache.php` drop-in backed by Redis.
|
||||
- **Front-end optimisation.** Defers scripts, drops WordPress's emoji and
|
||||
oEmbed machinery, and can rewrite asset URLs to a CDN.
|
||||
- **Images.** Writes WebP (and optionally AVIF) copies beside JPEG and PNG
|
||||
files and offers them with `<picture>`, so the page cache stays correct for
|
||||
every browser. Originals are never touched.
|
||||
- **Preloading.** Walks a queue of URLs on a five-minute cron so the first
|
||||
real visitor does not pay for the first render.
|
||||
- **Purging.** Clears the right pages when a post, a term, a comment or a
|
||||
WooCommerce product or order changes.
|
||||
- **Health checks and an assistant.** A set of signals about whether the cache
|
||||
is actually working, and an ask-box on the admin screens.
|
||||
|
||||
## What it deliberately does not do
|
||||
|
||||
- **No minification or concatenation.** Those break themes in ways that are
|
||||
expensive to debug and are better handled by a build step.
|
||||
- **No CSS "critical path" extraction.** Same reason.
|
||||
- **No telemetry of any kind.** Nothing is reported anywhere. The only
|
||||
outbound requests are the update check against gitea.nolimit.dk and an AI
|
||||
provider if, and only if, one has been configured with a key.
|
||||
- **No account, no licence check, no phone-home.**
|
||||
|
||||
## Where things are
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Admin screen | **Nolimit → Cache** |
|
||||
| Settings | one option, `nlcac_settings` |
|
||||
| Engine config | `wp-content/cache/nolimit/config.php`, written from the settings |
|
||||
| Drop-ins | `wp-content/advanced-cache.php`, `wp-content/object-cache.php` |
|
||||
| REST namespace | `nlcac/v1` |
|
||||
| Requires | WordPress 6.0, PHP 7.4. WooCommerce optional |
|
||||
| Version | 0.3.1 |
|
||||
|
||||
## Reading order
|
||||
|
||||
1. [Installing it, and telling whether it works](SETUP.md)
|
||||
2. [Hooks and integration](CONTRACT.md), for developers
|
||||
@@ -0,0 +1,91 @@
|
||||
# Installing it, and telling whether it works
|
||||
|
||||
## Install
|
||||
|
||||
The plugin updates itself from its release repository, but the **first**
|
||||
install is manual, and on a copy older than 0.2.3 it has to be.
|
||||
|
||||
1. Download `nolimit-cache.zip` from the latest release at
|
||||
`https://gitea.nolimit.dk/kw/nolimit-cache-dist/releases`.
|
||||
2. Plugins → Add New → Upload Plugin → Activate.
|
||||
3. After that, updates appear in the normal update row.
|
||||
|
||||
> **Below 0.2.3, do not trust the update row.** Versions before 0.2.3 could
|
||||
> offer another Nolimit plugin's zip as their own update. Upgrading *to*
|
||||
> 0.2.3 or later has to be done by hand for that reason; after that the update
|
||||
> row is safe.
|
||||
|
||||
## Activation writes two things outside the plugin folder
|
||||
|
||||
- `wp-content/advanced-cache.php` — the drop-in. Without it nothing is served
|
||||
from cache, however the settings look.
|
||||
- `WP_CACHE` in `wp-config.php` — WordPress only loads the drop-in when this
|
||||
constant is true.
|
||||
|
||||
If `wp-config.php` is not writable, the plugin says so on its own screen
|
||||
rather than failing quietly. That is the single most common reason for "it is
|
||||
switched on and nothing is cached".
|
||||
|
||||
## The settings that are a decision
|
||||
|
||||
Everything else has a defensible default. These four do not:
|
||||
|
||||
| Setting | Why it needs a decision |
|
||||
|---|---|
|
||||
| **Cache lifetime** | How stale a page may be. A shop with frequent price changes wants a short one; a mostly-static site can leave it long, because a change purges the affected pages anyway. |
|
||||
| **Excluded URIs / cookies** | Anything that must never be served from a shared copy. The WooCommerce basket, checkout and account pages are already excluded and cannot be un-excluded. |
|
||||
| **Object cache (Redis)** | Off unless there is a Redis to point it at. It replaces `object-cache.php`, so it interacts with anything else that wants that file. |
|
||||
| **CDN rewriting** | Only if assets are genuinely served from the other host. Wrong here means missing images rather than slow ones. |
|
||||
|
||||
## Images: WebP and AVIF
|
||||
|
||||
**Nolimit → Cache → Images** writes WebP (and, if the server can, AVIF) copies
|
||||
next to your JPEG and PNG files and offers them to browsers that understand
|
||||
them. The originals are never touched, so switching it off, or deleting the
|
||||
copies, leaves the site exactly as it was.
|
||||
|
||||
- Conversion runs in the background; the screen shows how many of the library
|
||||
are done and how much space was saved.
|
||||
- If an image changes size or position after switching on, pick the second
|
||||
delivery option on the same screen. A few themes style images with selectors
|
||||
that the extra `<picture>` element breaks.
|
||||
|
||||
## How to tell it is working
|
||||
|
||||
Do not read the settings page for this. Read these, in order.
|
||||
|
||||
### 1. The hit ratio, on **Nolimit → Cache**
|
||||
|
||||
The dashboard shows a hit ratio over the last 7 days and a count of stored
|
||||
pages. **Two numbers produced by different routes**: stored pages are counted
|
||||
by walking the disk, the ratio comes from a counter the engine writes before
|
||||
WordPress boots. If pages are stored and the ratio says nothing has been
|
||||
measured, those two disagree and the dashboard will say so explicitly rather
|
||||
than showing a bare zero.
|
||||
|
||||
That distinction exists because a zero can mean two things, a quiet site or a
|
||||
broken counter, and the dashboard should never leave you guessing which.
|
||||
|
||||
### 2. The response headers
|
||||
|
||||
```
|
||||
curl -sI https://example.test/ | grep -i x-nolimit
|
||||
```
|
||||
|
||||
A served-from-cache response carries the plugin's header. A logged-in request
|
||||
or one with a basket cookie should **not**.
|
||||
|
||||
### 3. The health report
|
||||
|
||||
**Nolimit → Cache → Health** runs each signal and says which failed and why —
|
||||
the drop-in missing, the cache directory not writable, `WP_CACHE` unset, the
|
||||
drop-in pointing at a different copy of the plugin. Each is measured by doing
|
||||
the thing (writing a file, reading the config the engine reads), not by
|
||||
calling `is_writable()` and believing it.
|
||||
|
||||
## Turning it off
|
||||
|
||||
Deactivating removes the drop-in. If a drop-in is ever left behind — a
|
||||
half-finished delete, a file permission problem — a site can end up serving
|
||||
from a cache no plugin is managing. `wp-content/advanced-cache.php` is safe to
|
||||
delete by hand; WordPress treats its absence as "no page cache".
|
||||
Reference in New Issue
Block a user