SOLID/DI architecture rebuild, selectable lint target, caching + custom DB table modules, expanded test tooling (v2.0.0) - #8
Merged
Conversation
…m DB table modules, expanded test tooling BREAKING CHANGE: generated plugin architecture is rebuilt around a Container + Service_Provider composition root, replacing the Plugin singleton and its hardcoded build_services(). Existing generated plugins are unaffected, but newly-generated output is structurally different (Registrable -> Service_Provider, Woo_Hooks split, Settings_Page split, etc.) — bump to 2.0.0 accordingly. Architecture (audit AUDIT_BACKLOG.md Section A): - Plugin is a plain composition root (Plugin::create() + boot()), no longer a singleton with a private constructor/get_instance(). - New Core\Container (bind/singleton/instance/get) and contracts: Contracts\Service_Provider (register()+boot()), Contracts\Conditional (is_needed() self-exclusion), Contracts\Activatable/Deactivatable. - Activator/Deactivator are now instance classes resolved from the container instead of static methods that bypassed it — this also fixes a real bug: the old Activator referenced "PostTypes\Post_Types" unqualified from inside the Core namespace, which resolved to a nonexistent class and would have fataled on activation whenever cpt_taxonomy was selected. - uninstall.php delegates to a new OOP Core\Uninstaller. - Woo_Hooks (113-line god class) split into 5 focused providers under Woo\Providers\ (Gateway/Shipping/Email/Product_Type/Blocks), each Conditional on WooCommerce being active. The shared woocommerce_blocks_loaded hook needed no coordination once split — WordPress fires every registered callback, so the original nested-closure design was solving a non-problem. - Admin\Settings_Page split into Settings_Repository (data access), Settings_Registrar (hooks), and src/Admin/views/ (markup). - Elementor's widget-discovery methods, previously injected into Plugin.php via a ~100-line JS template literal in index.js, now live in a real Elementor\Widget_Registrar.php template file. - Rest_Controller now extends WP_REST_Controller. - declare(strict_types=1) across every template file. New modules: - caching: Cache_Service (object cache + transient fallback, remember()). - custom_table: Database\Schema (dbDelta schema + automatic migration checked on every request) + Database\Item_Repository (CRUD example). Selectable lint target (--lint-target wp-org|vip|both, new interactive prompt): phpcs.xml and composer.json now generate WordPress-Extra/-Docs and/or WordPress-VIP-Go (+ automattic/vipwpcs) based on this instead of always bundling both rulesets unconditionally. Test tooling: - WP integration test suite via wp-phpunit/wp-phpunit + yoast/phpunit-polyfills (tests/Integration/, phpunit-integration.xml.dist, composer test:integration) always scaffolded, separate from the existing Brain Monkey unit suite. - Jest (jest.config.js + tests/js/App.test.js, npm run test:js) alongside the React admin app — assets/src/index.js now exports App for testability. - Playwright E2E (playwright.config.js + tests/e2e/, npm run test:e2e) alongside any JS pipeline; an admin_settings-aware spec is added when that module is selected. Other fixes found while rebuilding this: - Generated plugin version now defaults to 1.0.0 instead of 0.1.0. - Trailing whitespace in the plugin header when --author/--author-uri are left blank (fixed-width label lines) now gets trimmed post-substitution. - composer install retry logic (transient network blips, or the Windows antivirus/Search Indexer file-lock flake) in both scripts/verify.sh and the generated CI workflow's Install Composer Dependencies steps. - scripts/verify.sh itself had a latent bug (3-char --prefix values that fail the CLI's own 4-char minimum) that meant it silently never ran to completion; also restructured to verify each fixture independently so one flaky/failing variant doesn't hide results for the other four. 35 regression tests in tests/generator.test.js (up from 20).
- Cache_Service::get()'s $default parameter renamed to $fallback — Universal.NamingConventions.NoReservedKeywordParameterNames flags default as a reserved-keyword-ish parameter name. - Schema::drop_table()'s phpcs:ignore comment cited the wrong sniff code (WordPress.DB.PreparedSQL.NotPrepared instead of the actual WordPress.DB.PreparedSQL.InterpolatedNotPrepared that fires for an interpolated variable in a query string), so it never suppressed anything. Fixed the sniff name; the actual DROP TABLE + table name reasoning is unchanged. Both confirmed via the PR's own CI logs (self-consumption job, both ubuntu-latest and macos-latest hit the identical two findings).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Major (breaking) rebuild of the generated plugin's architecture around a
Container+Service_Providercomposition root, plus several new modules and a much larger generated test suite. Addresses Section A ofAUDIT_BACKLOG.mdalmost in full.Architecture
Pluginis a plain composition root (create()+boot()) — no more singleton/get_instance().Core\Container,Contracts\Service_Provider,Contracts\Conditional,Contracts\Activatable/Deactivatable.Activator/Deactivatorare instance classes resolved via the container (fixes a real bug: the oldActivatorreferencedPostTypes\Post_Typesunqualified from theCorenamespace — would have fataled on activation withcpt_taxonomyselected).Woo_Hooksgod class → 5 focused,Conditionalproviders underWoo\Providers\.Settings_Page→Settings_Repository+Settings_Registrar+src/Admin/views/.Plugin.phpvia a JS template literal inindex.js) → realElementor\Widget_Registrar.php.Rest_Controllernow extendsWP_REST_Controller.strict_typeseverywhere.New modules
caching— object-cache + transient-fallback wrapper.custom_table—dbDelta()schema + auto-migration + repository (audit item E3, marked P0).Selectable lint target
--lint-target wp-org|vip|both(new interactive prompt too) —phpcs.xml/composer.jsonnow generate the right ruleset(s) instead of always bundlingWordPress-VIP-Go.Test tooling
wp-phpunit/wp-phpunit(composer test:integration), separate from the existing Brain Monkey unit suite.npm run test:js).npm run test:e2e), with anadmin_settings-aware spec when that module is selected.Other
1.0.0(was0.1.0).--author/--author-uriare left blank.composer installretry logic (transient network blips / Windows AV file-lock flake) inscripts/verify.shand the generated CI workflow.scripts/verify.shhad a latent bug (3-char--prefixvalues failing the CLI's own 4-char minimum) that meant it silently never completed — fixed, and restructured to verify each fixture independently.Testing
npm test, up from 20).php -lclean on every generated file across all module combinations.composer install/lint/testverified successfully multiple times during development (this machine's antivirus intermittently locks files mid-composer install, unrelated to the code — see commit message / session notes).🤖 Generated with Claude Code