Skip to content

Commit f076b83

Browse files
authored
fix: avoid inactive plugin dev autoloaders (#1984)
1 parent 78a6fa3 commit f076b83

4 files changed

Lines changed: 91 additions & 5 deletions

File tree

docs/recipe-contract.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,9 @@ the mounted plugin slug.
204204
Supported `loadAs` values:
205205

206206
- `plugin`: mount below `/wordpress/wp-content/plugins/<slug>` and activate when
207-
`activate` is not `false`.
207+
`activate` is not `false`. Composer autoloaders are preloaded only for these
208+
active plugin inputs; an inactive plugin is mounted without executing its
209+
`vendor/autoload.php`.
208210
- `mu-plugin`: mount below
209211
`/wordpress/wp-content/mu-plugins/wp-codebox-runtime/<slug>` and load through
210212
WP Codebox's generated MU-plugin loader. Use this for sandbox runtime

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@
234234
"test:composer-package-overlay-revision": "tsx scripts/composer-backed-source-hydration-smoke.ts",
235235
"test:composer-package-overlay-autoload-layout": "tsx scripts/composer-package-overlay-autoload-layout-smoke.ts",
236236
"test:composer-installed-versions-loader-order": "tsx scripts/composer-installed-versions-loader-order-smoke.ts",
237+
"test:recipe-extra-plugin-composer-autoloaders": "tsx tests/recipe-extra-plugin-composer-autoloaders.test.ts",
237238
"test:runtime-preset-registry": "tsx tests/runtime-preset-registry.test.ts",
238239
"test:generic-ability-runtime-run": "tsx tests/generic-ability-runtime-run.test.ts",
239240
"test:provider-runtime-contracts": "tsx tests/provider-runtime-contracts.test.ts",

packages/cli/src/recipe-sources.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,14 +1796,15 @@ echo wp_json_encode(array('command' => 'install-mu-plugins', 'plugins' => $plugi
17961796

17971797
export function installPluginComposerAutoloadersCode(extraPlugins: PreparedExtraPlugin[]): string | null {
17981798
const plugins = extraPlugins
1799-
.filter((plugin) => plugin.loadAs === "plugin")
1799+
.filter((plugin) => plugin.loadAs === "plugin" && plugin.activate)
18001800
.map((plugin) => plugin.pluginFile)
18011801

18021802
if (plugins.length === 0) {
18031803
return null
18041804
}
18051805

18061806
return `$plugins = ${JSON.stringify(plugins)};
1807+
$autoloaders = array();
18071808
if (!is_dir(WPMU_PLUGIN_DIR) && !mkdir(WPMU_PLUGIN_DIR, 0777, true) && !is_dir(WPMU_PLUGIN_DIR)) {
18081809
throw new RuntimeException('Could not create mu-plugins directory.');
18091810
}
@@ -1828,16 +1829,20 @@ foreach ($plugins as $plugin) {
18281829
}
18291830
$package_autoload = WP_PLUGIN_DIR . '/' . $plugin_dir . '/vendor/autoload_packages.php';
18301831
if (is_file($package_autoload)) {
1831-
$lines[] = "require_once WP_PLUGIN_DIR . '/" . str_replace("'", "\\'", $plugin_dir) . "/vendor/autoload_packages.php';";
1832+
$autoload_path = $plugin_dir . '/vendor/autoload_packages.php';
1833+
$autoloaders[] = $autoload_path;
1834+
$lines[] = "require_once WP_PLUGIN_DIR . '/" . str_replace("'", "\\'", $autoload_path) . "';";
18321835
continue;
18331836
}
18341837
$autoload = WP_PLUGIN_DIR . '/' . $plugin_dir . '/vendor/autoload.php';
18351838
if (is_file($autoload)) {
1836-
$lines[] = "require_once WP_PLUGIN_DIR . '/" . str_replace("'", "\\'", $plugin_dir) . "/vendor/autoload.php';";
1839+
$autoload_path = $plugin_dir . '/vendor/autoload.php';
1840+
$autoloaders[] = $autoload_path;
1841+
$lines[] = "require_once WP_PLUGIN_DIR . '/" . str_replace("'", "\\'", $autoload_path) . "';";
18371842
}
18381843
}
18391844
if (false === file_put_contents($loader, implode("\n", $lines) . "\n")) {
18401845
throw new RuntimeException('Could not write WP Codebox Composer autoloader loader.');
18411846
}
1842-
echo wp_json_encode(array('command' => 'install-composer-autoloaders', 'plugins' => $plugins, 'loader' => $loader), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);`
1847+
echo wp_json_encode(array('command' => 'install-composer-autoloaders', 'plugins' => $plugins, 'autoloaders' => $autoloaders, 'loader' => $loader), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);`
18431848
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import assert from "node:assert/strict"
2+
import { execFile as execFileCallback } from "node:child_process"
3+
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"
4+
import { tmpdir } from "node:os"
5+
import { join } from "node:path"
6+
import { promisify } from "node:util"
7+
8+
import { installPluginComposerAutoloadersCode, type PreparedExtraPlugin } from "../packages/cli/src/recipe-sources.js"
9+
10+
const execFile = promisify(execFileCallback)
11+
const root = await mkdtemp(join(tmpdir(), "wp-codebox-extra-plugin-autoloaders-"))
12+
const pluginsDir = join(root, "plugins")
13+
const muPluginsDir = join(root, "mu-plugins")
14+
15+
function plugin(slug: string, activate: boolean): PreparedExtraPlugin {
16+
return {
17+
source: join(pluginsDir, slug),
18+
slug,
19+
target: `/wordpress/wp-content/plugins/${slug}`,
20+
pluginFile: `${slug}/${slug}.php`,
21+
activate,
22+
loadAs: "plugin",
23+
cleanupPaths: [],
24+
provenance: { kind: "local", original: join(pluginsDir, slug) },
25+
}
26+
}
27+
28+
try {
29+
await mkdir(join(pluginsDir, "active-provider", "vendor"), { recursive: true })
30+
await mkdir(join(pluginsDir, "inactive-plugin", "vendor"), { recursive: true })
31+
await mkdir(join(pluginsDir, "inactive-plugin", "tests"), { recursive: true })
32+
await mkdir(muPluginsDir, { recursive: true })
33+
34+
await writeFile(join(pluginsDir, "active-provider", "vendor", "autoload.php"), "<?php function wp_codebox_active_provider_loaded() { return true; }\n")
35+
await writeFile(join(pluginsDir, "inactive-plugin", "composer.json"), JSON.stringify({
36+
autoload: { "psr-4": { "InactivePlugin\\\\": "src/" } },
37+
"autoload-dev": { files: ["tests/autoload-dev.php"] },
38+
}))
39+
await writeFile(join(pluginsDir, "inactive-plugin", "tests", "autoload-dev.php"), "<?php function wp_codebox_inactive_plugin_dev_sentinel() { return true; }\n")
40+
await writeFile(join(pluginsDir, "inactive-plugin", "vendor", "autoload.php"), "<?php require_once dirname(__DIR__) . '/tests/autoload-dev.php';\n")
41+
42+
const installCode = installPluginComposerAutoloadersCode([
43+
plugin("active-provider", true),
44+
plugin("inactive-plugin", false),
45+
])
46+
assert.ok(installCode)
47+
assert.doesNotMatch(installCode, /inactive-plugin/, "inactive plugin inputs are absent from the generated preload contract")
48+
49+
const php = `
50+
function wp_json_encode($value, $options = 0) { return json_encode($value, $options); }
51+
define('ABSPATH', ${JSON.stringify(`${root}/`)});
52+
define('WP_PLUGIN_DIR', ${JSON.stringify(pluginsDir)});
53+
define('WPMU_PLUGIN_DIR', ${JSON.stringify(muPluginsDir)});
54+
${installCode}
55+
echo "\n---RESULT---\n";
56+
require WPMU_PLUGIN_DIR . '/wp-codebox-composer-autoloaders.php';
57+
echo json_encode(array(
58+
'active_provider_loaded' => function_exists('wp_codebox_active_provider_loaded'),
59+
'inactive_dev_sentinel_loaded' => function_exists('wp_codebox_inactive_plugin_dev_sentinel'),
60+
));
61+
`
62+
const { stdout } = await execFile("php", ["-r", php])
63+
const [evidenceJson, resultJson] = stdout.split("\n---RESULT---\n")
64+
assert.deepEqual(JSON.parse(evidenceJson), {
65+
command: "install-composer-autoloaders",
66+
plugins: ["active-provider/active-provider.php"],
67+
autoloaders: ["active-provider/vendor/autoload.php"],
68+
loader: join(muPluginsDir, "wp-codebox-composer-autoloaders.php"),
69+
}, "setup evidence names each intentionally preloaded autoloader")
70+
assert.deepEqual(JSON.parse(resultJson), {
71+
active_provider_loaded: true,
72+
inactive_dev_sentinel_loaded: false,
73+
})
74+
75+
console.log("recipe extra plugin composer autoloaders ok")
76+
} finally {
77+
await rm(root, { recursive: true, force: true })
78+
}

0 commit comments

Comments
 (0)