Two plugins, one library
Run viv isolate <plugin> when viv install prints <plugin> bundles
<library> … Run viv isolate <plugin> to keep both, or when a plugin
fatals with a class or function from a library another plugin or the
site also ships (Class … not found, Call to undefined method, an
__PHP_Incomplete_Class from the wrong version). Don't run it otherwise:
it rewrites the plugin's bundled code, and two copies of a library that
writes to the database would fight.
What the install tells you
viv install always checks for this, on every install: a plugin's
bundled vendor/ carrying a library, unprefixed, at a version that
differs from the site's own copy. It skips libraries built to coexist
(several plugins register a copy and the newest wins) and libraries
already prefixed by the plugin's own build. When it does find a clash it
prints one line naming the plugin, the library, both versions and the
fix:
<plugin> bundles <library> <version> unprefixed; the site has <version>. Run viv isolate <plugin> to keep both.
What isolate does
viv isolate <plugin> copies the plugin's bundled vendor/, then runs
php-scoper over the copy on the project's own pinned PHP, with a prefix
derived from the plugin's slug. It then runs php -l on every file the
scoper wrote and a load check — a generated bootstrap that loads the
scoped vendor/autoload.php against the real php-stubs/wordpress-stubs
package and requires the plugin's own main file. Either check failing
drops the build and leaves the plugin's plain archive linked. A pass is
cached in the store, keyed to the plugin's archive and the site lock, and
linked into the plugin's install path. The plugin's own code is never
touched, only its bundled vendor/.
What it costs
On an Altis site where a plugin bundles the AWS SDK and the Guzzle stack
(2,386 files): php-scoper itself takes 4.6 seconds, php -l across eight
workers takes 51 seconds, 87 seconds in all on a cold cache. A warm cache
— the same plugin archive and site lock already isolated once — links
the cached result instantly, no scoper or lint run again.
Undo and inspect
viv isolate --rm <plugin> removes it from extra.viv.isolate and
relinks the plain, unprefixed archive. viv isolate --list prints every
isolated plugin and its prefix. extra.viv.isolate lives in
composer.json and travels in git like any other setting; viv lock
export carries the same map into composer.lock's own
extra.viv.isolate, a key Composer ignores.
Why it is opt-in
Detection runs on every install, unconditionally, but prefixing only
happens for a plugin named in extra.viv.isolate. Two copies of a
library that migrates or writes to the same database tables would fight
if both ran at once, so no site should pay that risk unasked — isolating
a plugin is a decision for the person who knows what that plugin's
library touches.
What it does not do
It does not resolve a clash at solve time — two installed packages declaring incompatible requirements for the same library. That is a different problem, covered under candidate 3.2 of the research programme: Generation 3.