=== 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.2.2
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. Four 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.

### Four 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 |
| `all` | `blocks` + 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.
* 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.

= 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.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.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.
