=== Zero Blocks Given ===
Contributors: MythThrazz
Donate link: https://marcindudek.dev/
Tags: woocommerce, performance, disable-blocks, gutenberg, optimization
Requires at least: 6.0
Tested up to: 7.0
Requires PHP: 8.0
Stable tag: 1.3.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Turn off WooCommerce / WP block frontend output at the code level. Five tiers, optional one-click settings, no paywall.

== Description ==

Most WooCommerce stores ship 80-150 KB of block CSS and JS that the store never actually renders. The WC `BlockPatterns` scanner is the one that bugs me most - it hits the filesystem on every request to scan a directory of pattern templates you don't use. Then add core WP `global-styles`, `wp-block-library` and font-faces on top, and you're loading a Gutenberg frontend you probably switched off a long time ago.

Zero Blocks Given turns it off at the source, through WooCommerce's own dependency injection container. No CSS dequeue band-aid, no UI checkboxes, no PRO upsell. One constant in `wp-config.php`, pick a tier, done.

### How it works

Here's the thing - WC registers its block hooks as closures wrapping instance methods on container-managed objects. So you can't `remove_action()` them by string. You have to fetch the same instance back from the DI container and pass it in as the callable. That's what this does:

`$bp = \Automattic\WooCommerce\Blocks\Package::container()->get( BlockPatterns::class );`
`remove_action( 'init', [ $bp, 'register_block_patterns' ] );`

The DI-container path is the one that survives WC upgrades cleanly. String-callback matching and dequeue tricks tend to drift every release, so I went with the container.

### Five modes. Three ways to set them.

| Mode | What it turns off | When to use it |
|------|---------------|----------|
| `patterns` | WC `BlockPatterns` directory scanner only | Block-based Cart/Checkout - keeps all blocks rendering, just skips the file-I/O scan |
| `blocks` | `patterns` + `BlockTypesController` + `Notices` styles + `wc-blocks-style` handle | Classic-shortcode WC stores |
| `keep-styles` | `blocks` + WC Product Filter pattern scan (`ProductFilterAttribute`) | Stores that don't use WC blocks but keep Gutenberg styling - kills the expensive scans, leaves `global-styles` and core block rendering alone. Pixel-perfect. |
| `all` | `keep-styles` + WP `global-styles` pipeline + `theme.json` + font-faces + head `<style>` strip | Sites with no Gutenberg frontend at all |
| `nuclear` | `all` + `unregister_block_type()` for every core block | Page-builder sites - strips the editor inserter clean |

Default is `all`. Set the mode you want with any of these:

1. The `<dialog>` settings - click the Settings link on the Plugins screen. Native HTML dialog, four radios, save. No menu items, nothing else added to your admin.
2. A constant in `wp-config.php` - `define( 'ZEROBLG_MODE', 'patterns' );`. This wins over the dialog. Good devops escape hatch.
3. A filter in a theme or mu-plugin - `add_filter( 'zeroblg/mode', fn() => 'blocks' );`. Used when neither the constant nor the dialog set a value.

Resolution order: constant, then settings, then filter, then `all`. An invalid value at any step just falls through to the next.

### A few real cases

* Service-form site on WC. You sell consultations, not products - no cart, no checkout. `mode=all` strips every block stylesheet across the site.
* Classic-shortcode WC store. You use `[woocommerce_cart]` and `[woocommerce_checkout]`, no block UI. `mode=blocks` turns off the block frontend without touching your classic flow.
* Block-checkout store with bloated patterns. You run the new Cart/Checkout blocks but never the WC pattern library. `mode=patterns` skips just the directory scanner - saves the disk hit, leaves your checkout alone.
* Themed store with a layered-nav filter, pixel-perfect requirement. Woodmart or similar, WC layered nav, no Product Filter block in any post. The `ProductFilterAttribute` pattern scan can cost ~100ms a request on its own. `mode=keep-styles` kills that scan and the rest of the block registration, but leaves every inline `global-styles` rule in place - so the shop renders pixel-for-pixel the same. `all` would shave the same milliseconds but strips `theme.json` styles too, which moves pixels.
* FrankenPHP / worker mode. All hooks are idempotent, no `$GLOBALS` writes, `\Throwable` catches around the DI lookups. Safe in a worker.
* Elementor / Divi WC stores. The page builder renders its own checkout, so `mode=all` clears the WC and WP block CSS your theme never uses anyway.

### Why I built it this way

* Activate it and it works - `mode=all` is the default, no setup needed.
* No settings page to learn. One constant or one filter is the whole API.
* Pure PHP. No database rows, no admin scripts, no frontend JS.
* No external requests, no tracking, nothing phoning home. GDPR is a non-issue.
* It uses the same `Package::container()` lookup as WC core, so it follows WC's own object lifecycle instead of guessing.
* Worker-safe - FrankenPHP, Roadrunner, Swoole. No `die`, no `exit`, no session writes.
* mu-plugin friendly. Drop the folder into `wp-content/mu-plugins/` and it loads itself.
* GPL, no upsells. No PRO tier, no Freemius, no admin notice nagging you for a review.

### From the maker of WP Multitool

This plugin handles the frontend block bloat. If you also want the backend cleaned up, that's what [WP Multitool](https://wpmultitool.com/) does - slow queries, autoload bloat, the database bottlenecks that caching plugins just hide instead of fixing. Most optimization plugins guess at the problem. WP Multitool runs `EXPLAIN` and shows you. 14 tools in there - slow-query analyzer, autoload optimizer, index suggestions, fatal-error recovery - and a free site scanner if you want to see what's slow before paying for anything. If Zero Blocks Given earned a spot in your stack, that one probably will too.

== Installation ==

1. Install from the WordPress plugin directory, or upload the folder to `/wp-content/plugins/zero-blocks-given/`.
2. Activate it. It runs on `mode=all` out of the box.
3. To change the tier, add this to `wp-config.php`:
   `define( 'ZEROBLG_MODE', 'patterns' );` // or 'blocks' or 'all'
4. Or drop the folder into `wp-content/mu-plugins/` instead - it works as a must-use plugin too.

== Frequently Asked Questions ==

= Which WooCommerce versions does it support? =

WC 7.x through 10.x - anywhere the DI container (`Automattic\WooCommerce\Blocks\Package::container()`) is stable. If a future WC release renames a method, the per-method `\Throwable` catches mean Zero Blocks Given degrades quietly instead of throwing a fatal.

= Will it break my Block-based Cart/Checkout? =

`mode=patterns` is safe - it only turns off the directory scanner, not the block UI. `mode=blocks` and `mode=all` will break Block-based Cart/Checkout, which is the whole point of those modes. Pick the one that matches your site.

= How do I switch tiers? =

Add `define( 'ZEROBLG_MODE', 'patterns' );` (or `'blocks'` or `'all'`) to `wp-config.php`. The constant wins over the filter. Invalid values fall back to `'all'`.

= Does it work as a must-use plugin? =

Yes. Drop the whole `zero-blocks-given/` folder into `wp-content/mu-plugins/`, then add a one-line loader like `mu-plugins/zero-blocks-given-load.php` with `require WPMU_PLUGIN_DIR . '/zero-blocks-given/zero-blocks-given.php';` in it. WordPress loads it on every request.

= Is it GDPR compliant? =

Yes. It doesn't collect, store, send or process any user data. No external requests, no cookies, no options written to your database.

= Is it safe for FrankenPHP / worker mode? =

Yes. No `die`/`exit`, no `$GLOBALS` writes, no `session_start`, every hook is idempotent, every DI lookup is wrapped in a `\Throwable` catch. Tested on FrankenPHP worker mode.

= How do I uninstall? =

Deactivate and delete from the WordPress admin. It writes nothing to the database, so uninstall is a real no-op.

= Does `keep-styles` need a specific version? =

Yes - `keep-styles` shipped in 1.3.0. On 1.2.2 or older it's an unknown value, so the resolver falls back to the default `all` - which DOES strip `global-styles` and can move pixels. Set `keep-styles` only on 1.3.0+. Watch the edge case where a forced reinstall or downgrade leaves you on an older build: the mode silently becomes `all` until you update, so your styles disappear without an error. Confirm the active version before relying on `keep-styles`.

= I removed a hook but `wp profile` still lists it - did it work? =

Probably yes. `wp profile hook` can still show `register_block_patterns` as if it ran, because it wraps the callbacks it captured before the priority-0 removal fired. Don't trust the profiler here - check the real request instead. Dump `$wp_filter['wp_loaded']` (or the relevant hook) on an actual front-end load; if the callback is gone from there, it's gone.

= My pixel-diff shows changes I didn't expect - is `keep-styles` moving pixels? =

Check your screenshot widths first. Full-page screenshots taken at different viewport widths (say 2248px vs 2488px) produce a false diff that has nothing to do with the plugin. Re-shoot both the baseline and the test at identical dimensions before you trust the number. With matched dimensions, `keep-styles` leaves `global-styles` intact and the diff against baseline stays in the noise.

= Why a constant first, settings UI second? =

Keeping the tier in a `wp-config.php` constant means it lives in version control with the rest of your devops config. The dialog is there for sites where editing `wp-config.php` isn't practical. Both end up in the same code path.

== Screenshots ==

1. The whole configuration API - one `define()` line in `wp-config.php`.
2. Query Monitor "Hooks & Actions" panel showing `BlockPatterns->register_block_patterns` gone from `init`.
3. The optional `<dialog>` settings on the Plugins screen - four radios, one save button, no menu item.

== Changelog ==

= 1.3.0 =
* New `keep-styles` tier, sitting between `blocks` and `all`. It kills the WC Product Filter pattern scanner (`ProductFilterAttribute::register_block_patterns` on `wp_loaded`) on top of the `blocks` removals, but leaves the WP `global-styles` pipeline and core block rendering untouched. For stores that want the block/pattern registration cost gone without moving a single pixel - the scan can cost ~100ms a request on a layered-nav store that never uses the Product Filter block. Front-only; the editor's pattern inserter is left alone.
* The Product Filter scan removal runs for `keep-styles`, `all` and `nuclear`.
* Fixed: `nuclear` now also removes `BlockTypesController::register_blocks` (it was skipping it - max aggression should include it).
* Dialog now lists five tiers.

= 1.2.2 =
* WP.org plugin review feedback - switched direct-file-access guards from `return` to `exit` (the WP coding-standards form). Applied to `zero-blocks-given.php`, `uninstall.php` and all `inc/*.php` tier files. No behavior change.

= 1.2.1 =
* WP.org plugin review feedback - extended the internal prefix from `zbg` to `zeroblg` (review wanted prefixes longer than 4 chars). Renamed constant `ZBG_MODE` to `ZEROBLG_MODE`, filter `zbg/mode` to `zeroblg/mode`, option key `zbg_mode` to `zeroblg_mode`, plus the matching function, CSS and DOM-id prefixes. No behavior change.

= 1.2.0 =
* First public release. Slug: `zero-blocks-given`. Constant: `ZEROBLG_MODE`. Filter: `zeroblg/mode`. Option key: `zeroblg_mode`.
* Settings dialog JavaScript loaded via `wp_enqueue_script()` on the Plugins screen (`inc/admin-settings.js`).

= 1.1.0 =
* New `nuclear` tier - calls `unregister_block_type()` for every core block. Editor inserter goes empty, frontend block markup stops rendering. For sites that build content with page builders, shortcodes or the classic editor only.
* New optional settings UI - a native `<dialog>` modal off the Settings link on the Plugins screen. Four radios, one save button. No menu item, no admin notices.
* The settings UI sits below the constant in the resolution chain - `ZEROBLG_MODE` in `wp-config.php` still wins. When the constant is defined, the dialog shows the radios disabled with a "Locked by constant" notice.
* Resolution order is now constant, option, filter, `all` (was constant, filter, `all`).
* `uninstall.php` now deletes the `zeroblg_mode` option on uninstall (was a no-op).

= 1.0.0 =
* Initial release. Three tiers: `patterns`, `blocks`, `all`. Default `all`.
* DI container unregister via `Package::container()->get( BlockPatterns::class )`.
* Per-method `\Throwable` catches for forward-compat with WC method renames.
* Worker-safe (FrankenPHP / Roadrunner / Swoole).
* mu-plugin friendly.

== Upgrade Notice ==

= 1.3.0 =
New `keep-styles` mode: kills the WC Product Filter pattern scan (and block registration) while keeping inline global-styles and core block rendering. The pixel-perfect choice when `all` strips too much. Existing modes unchanged; default stays `all`.

= 1.2.0 =
First public release. Configure with the `ZEROBLG_MODE` constant in `wp-config.php`, the `zeroblg/mode` filter, or the `<dialog>` settings on the Plugins screen.
