Skip to content

Add opt-in Composer dependency scoping (#309)#537

Open
srtfisher wants to merge 6 commits into
developfrom
feature/composer-scoping
Open

Add opt-in Composer dependency scoping (#309)#537
srtfisher wants to merge 6 commits into
developfrom
feature/composer-scoping

Conversation

@srtfisher

Copy link
Copy Markdown
Member

Closes #309.

What

Scaffolded plugins can opt in (default off) to prefixing their runtime Composer dependencies into vendor-prefixed/ via php-scoper, so they no longer conflict with other plugins or the host project when loaded as a Composer dependency. The dominant collision source is Alley's own shared packages (wp-type-extensions, mantle-framework/support) shipping at different versions across plugins.

How

  • .scoper/scoper.inc.php — namespace-agnostic config. Derives the prefix (<RootNamespace>\Dependencies) from composer.json, scopes runtime packages only (from composer.lock's packages, so dev tools are never scoped), and excludes WordPress core symbols (sniccowp/php-scoper-wordpress-excludes) + the plugin's own namespace (AC Bump nick-invision/retry from 1 to 2.6.0 #2).
  • .scoper/scope.php — orchestrator. Runs php-scoper, then regenerates a classmap-authoritative autoloader over the scoped files + the plugin's own src/, rebuilding each runtime package's autoload.files so scoped helper functions still load. No-ops when php-scoper is absent (safe in post-install/post-update hooks and under --no-dev).
  • configure.php — opt-in prompt that wires the tooling + scope script + hooks, repoints the plugin loader to vendor-prefixed/vendor/autoload.php, prefixes the finite set of vendor imports in src/, and enables the CI workflow. Removes all of it when declined.
  • .github/workflows/test-scoped.yml — PR test that scopes then runs phpunit against the scoped dependencies (AC Bump styfle/cancel-workflow-action from 0.5.0 to 0.9.1 #4); enabled by configure.php on opt-in.

Why classmap regeneration (not php-scoper's autoloader)

php-scoper scopes files reliably, but its autoloader output is unusable here: the Alley packages use the WordPress autoloader (no PSR-4), so some registerFromRules() namespace strings are left unprefixed, and its scoped Composer bootstrap fails to load ClassLoader. Regenerating a classmap autoloader parses actual class declarations, so PSR-4 and WordPress-autoloader packages are handled uniformly — which is why Strauss wasn't needed.

Verification

  • Scaffolded a plugin, opted into scoping + SQLite, ran composer require symfony/string (auto re-scoped to 1606 classes).
  • All scoped classes + the scoped register_meta_from_file resolve; the unscoped Alley\WP\Features\Group is correctly absent.
  • composer phpunitOK (2 tests, 3 assertions) — the Feature test boots the whole plugin through the scoped autoloader.
  • Re-scaffolded with scoping declined → machinery removed, loader/imports untouched, tests still pass (no regression to the default path).

Follow-ups (not in this PR)

  1. alleyinteractive/action-release needs a backward-compatible scope input that runs composer scope and enables the require-pruning step currently commented out citing this issue. (Separate PR.)
  2. Pre-existing quirk: configure.php's 'alleyinteractive' => $vendor_slug replacement rewrites Alley dependency package names for non-Alley vendors, breaking composer update. Unrelated to Allow Composer Dependencies to be scoped #309.

Design spec and implementation plan included under docs/superpowers/.

🤖 Generated with Claude Code

Scaffolded plugins can opt into prefixing their runtime Composer
dependencies into vendor-prefixed/ via php-scoper, isolating them from
other plugins or the host project when loaded as a Composer dependency.

- .scoper/scoper.inc.php: namespace-agnostic php-scoper config that
  derives the prefix from composer.json and scopes runtime packages only.
- .scoper/scope.php: orchestrator that runs php-scoper then regenerates a
  classmap autoloader over the scoped files + the plugin's own src,
  carrying autoload.files (helper functions) forward. No-ops without
  php-scoper so it is safe in post-install/post-update hooks.
- configure.php: opt-in prompt (default off) that wires the tooling,
  rewrites the plugin loader + the finite set of vendor imports, and
  enables the scoped-build CI workflow; removes the machinery otherwise.
- .github/workflows/test-scoped.yml: PR test that scopes then runs phpunit
  against the scoped dependencies (enabled by configure.php on opt-in).

Verified by scaffolding a plugin, requiring symfony/string, and running
phpunit (2 passed) against the scoped autoloader.
When scoping is enabled, configure.php now wires built-release.yml to
call action-release with scope: true, so the built branch ships the
prefixed vendor-prefixed/ directory.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow Composer Dependencies to be scoped

1 participant