Prefix bundled dependencies to prevent conflicts with site code - #1110
Draft
swissspidy wants to merge 6 commits into
Draft
Prefix bundled dependencies to prevent conflicts with site code#1110swissspidy wants to merge 6 commits into
swissspidy wants to merge 6 commits into
Conversation
WP-CLI registers its autoloader before WordPress boots, so for any class shipped both by the Phar and by the site, the Phar's copy wins and is imposed on the site. A site using monolog/monolog against psr/log v3 gets the Phar's psr/log 1.1.4 instead and fatals on the incompatible LoggerInterface signature. Moving wp-cli/package-command to require-dev fixed this for Composer-based installations, but the Phar is still built with dev dependencies, so it continues to ship composer/composer and its tree unprefixed: symfony/console v5.4.47, psr/log 1.1.4, react/promise, seld/*. Prefix that tree with php-scoper, with two constraints: * The `Composer\` namespace itself is left alone. Third-party Composer plugins are compiled against the real `Composer\Plugin\PluginInterface`, so prefixing it would break `wp package install` for any package shipping one. References from inside `Composer\` to the prefixed vendors are still rewritten, so Composer keeps using its own psr/log. * Nothing reachable from WP-CLI's public API is touched: php-cli-tools (`Utils\make_progress_bar()`), Requests (`Utils\http_request()`, plus RequestsLibrary deliberately sharing the library with Core), and every wp-cli/* package. php-scoper needs PHP 8.2 while WP-CLI still targets 7.2.24, so the toolchain lives in utils/scoper with its own composer.json. Two things this turned up that are easy to get wrong: * php-scoper rewrites source files but not Composer's generated autoload maps. A scoped tree with a stale autoloader still advertises `Psr\Log\` and the conflict survives with nothing to show for it, so the autoloader is regenerated and then asserted on. * Excluding a namespace does not stop php-scoper prefixing string literals naming classes inside it. Composer compares `$class` against 'Composer\Package\CompletePackage', which the prefix silently breaks; a patcher restores those. Refs wp-cli/wp-cli#5920 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RHtjyXkZh8X16sBgmidSQi
`utils/scope-dependencies.php` prefixes symfony/finder along with the rest of the Composer tree, but `utils/make-phar.php` builds the Phar with that same Finder. Once prefixing has run, `Symfony\Component\Finder\Finder` no longer exists and the build dies before writing anything. Resolve the class name at runtime instead, so the build works whether or not the dependencies have been prefixed yet. Also drop `@require-mysql` from the isolation scenarios: they only need a WordPress installation, which the Behat suite can provide on SQLite too. Refs wp-cli/wp-cli#5920 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RHtjyXkZh8X16sBgmidSQi
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the 鈿欙笍 Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The static analysis config lints everything under `utils`, so the new build scripts need the same treatment the existing ones already get. * Exempt them from the two WordPress-context sniffs `make-phar.php` is already exempt from; they are procedural stand-alone scripts that never run inside WordPress. * Exclude `utils/scoper/scoper.inc.php` from PHPStan. It is an isolated toolchain with its own composer.json, so the classes it references are not installed in this project's vendor directory. * Swap `str_contains()` for `strpos()`. The script refuses to run below PHP 8.2, but phpcs checks this repository against a 7.2 baseline and flags the newer function. Refs wp-cli/wp-cli#5920 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RHtjyXkZh8X16sBgmidSQi
The mu-plugins used a `\Stringable|string` union type, which is a parse error on the PHP 7.2 to 7.4 jobs in the matrix, so the scenarios failed with "syntax error, unexpected '|'" rather than exercising anything. Narrowing an untyped parameter to `string` is the same contravariance violation and parses on every version the suite runs, so the scenarios still distinguish a prefixed tree from an unprefixed one: declaring the class against an unprefixed `psr/log` v1 remains a fatal error, and succeeds once the bundled copy is prefixed. Also drop `--format=count` from `wp package list`, which does not offer that format and made the step fail on argument parsing. Refs wp-cli/wp-cli#5920 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RHtjyXkZh8X16sBgmidSQi
Three separate code quality failures on the new build scripts: * PHPStan runs at level 9, where every offset read off `json_decode()` output is `mixed`. Narrow the decoded `installed.json` explicitly and build the packages list in a local variable instead of writing back through nested offsets. * Align the scoper config's array arrows on the longest key. * `marc-mabe` is a vendor name, not a misspelling of "maybe"; mark those two lines with the `spellchecker:disable-line` annotation the repo's `.typos.toml` already recognises. Re-ran the prefixing end to end against a scratch tree after the PHPStan refactor: 30 packages rewritten, autoloader regenerated, verification still passes. Refs wp-cli/wp-cli#5920 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RHtjyXkZh8X16sBgmidSQi
Refs wp-cli/wp-cli#5920 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RHtjyXkZh8X16sBgmidSQi
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 free
to 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
This change implements namespace prefixing for WP-CLI's bundled third-party dependencies to prevent conflicts when the Phar runs against WordPress sites that ship their own versions of those libraries. Since WP-CLI's autoloader is registered before WordPress boots, unprefixed classes from the Phar take precedence over the site's own dependencies, causing fatal errors when interface signatures differ (e.g., psr/log v1 vs v3).
Key Changes
New
utils/scope-dependencies.phpscript: Orchestrates the prefixing workflow using php-scoper. The script:WP_CLI\Vendornamespacecomposer installandutils/make-phar.phpNew
utils/scoper/scoper.inc.phpconfiguration: Defines php-scoper behavior:Composer\namespace unprefixed so third-party Composer plugins continue workingComposer\classes to avoid broken dynamic class resolutionNew
utils/scoper/composer.json: Isolated toolchain configuration requiring php-scoper ^0.18, kept separate because php-scoper needs PHP 8.2+ while WP-CLI targets PHP 7.2.24Updated
utils/make-phar.php: Adapts to work with both prefixed and unprefixed Symfony Finder class depending on whetherscope-dependencies.phphas already runNew
features/dependency-isolation.feature: Behat scenarios validating that:Updated
.github/workflows/deployment.yml: Adds prefixing step to the Phar build pipelineNotable Implementation Details
--classmap-authoritativeflag ensures the ClassLoader consults only the classmap, preventing any leftover PSR-4 rules from resurrecting unprefixed nameshttps://claude.ai/code/session_01RHtjyXkZh8X16sBgmidSQi