Skip to content

Avoid writing new mu-plugins if old ones are still accessible - #3403

Merged
fredrikekelund merged 7 commits into
trunkfrom
f26d/native-php-improved-mu-plugins-writing
May 13, 2026
Merged

Avoid writing new mu-plugins if old ones are still accessible#3403
fredrikekelund merged 7 commits into
trunkfrom
f26d/native-php-improved-mu-plugins-writing

Conversation

@fredrikekelund

Copy link
Copy Markdown
Contributor

Related issues

  • Related to RSM-1691

How AI was used in this PR

Codex implemented the first version, which I then tweaked – specifically to use JSON for stable string de-/serialization between JS and PHP.

Proposed Changes

With the native-php runtime, mu-plugins are loaded like so:

  1. Create a temporary directory.
  2. Write individual mu-plugin files to that directory.
  3. Write a wp-content/mu-plugins/99-studio-loader.php file to the site directory, with a path to the temporary directory from before.
  4. The loader file searches the temporary directory and includes all the files in it.

Previously, this process was executed not only when a server started, but also on every WP-CLI invocation. This is fine, but it's not very efficient.

This PR prepends the following steps to the previous process:

  1. Assert that wp-content/mu-plugins/99-studio-loader.php exists.
  2. Parse the temporary directory path from it.
  3. Assert that the template for the loader file hasn't changed.
  4. Assert that the temporary directory path contains the file names we would write.

If all assertions hold, we don't write anything to disk. If they don't, we run the setup process again.

Testing Instructions

  1. Run STUDIO_RUNTIME=native-php npm start
  2. If you don't already have a site with runtime: native-php, create a new one
  3. Start that site
  4. Open wp-content/mu-plugins/99-studio-loader.php and take note of $studio_mu_plugins_dir
  5. Run node apps/cli/dist/cli/main.mjs wp core version
  6. Open wp-content/mu-plugins/99-studio-loader.php and ensure that $studio_mu_plugins_dir is the same as before

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?
@fredrikekelund
fredrikekelund requested a review from bcotrim May 8, 2026 12:25
@fredrikekelund fredrikekelund self-assigned this May 8, 2026
@wpmobilebot

wpmobilebot commented May 8, 2026

Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing 180e41b vs trunk

app-size

Metric trunk 180e41b Diff Change
App Size (Mac) 1408.82 MB 1408.82 MB +0.00 MB ⚪ 0.0%

site-editor

Metric trunk 180e41b Diff Change
load 1515 ms 1503 ms 12 ms ⚪ 0.0%

site-startup

Metric trunk 180e41b Diff Change
siteCreation 8100 ms 8083 ms 17 ms ⚪ 0.0%
siteStartup 4935 ms 4939 ms +4 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

Comment thread tools/common/lib/mu-plugins.ts Fixed
Comment thread tools/common/lib/mu-plugins.ts Dismissed
This reverts commit ffe41e7.
fredrikekelund and others added 2 commits May 11, 2026 11:40
…nescaping'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

@bcotrim bcotrim left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worked as described, nice improvement!
LGTM 👍

@fredrikekelund
fredrikekelund merged commit 8757756 into trunk May 13, 2026
10 checks passed
@fredrikekelund
fredrikekelund deleted the f26d/native-php-improved-mu-plugins-writing branch May 13, 2026 13:41
fredrikekelund added a commit that referenced this pull request May 13, 2026
## Related issues

<!--
Link a related issue to this PR. If the PR does not immediately resolve
the issue,
for example, it requires a separate deployment to production, avoid
using the "Fixes" keyword and use "Related to" instead.
-->

- Fixes RSM-1691
- Builds on #3403

## How AI was used in this PR

<!--
Help reviewers understand what to look for and verify that you've
reviewed the code yourself.
-->

Claude was used to verify the list of disabled PHP functions and to
review the implementation as a whole, through several iterations.

## Proposed Changes

Running code from third parties on your local machine is inherently
risky. There's no sandbox with our native PHP runtime, so machines could
be compromised in any number of ways (extracting secrets, running
malicious code, etc). This PR applies some sane security defaults that
reduce risk. We aim to strike a good balance between relatively secure
defaults and still allowing most typical WordPress sites to function
perfectly normally.

1. Always call `getDefaultPhpArgs` when invoking the PHP binaries.
2. Increase `memory_limit` to 512 MB. This has nothing to do with
security, but I noticed that the default is 128 MB, which isn't enough
for a site with a few plugins installed.
3. Optionally, apply an `open_basedir` restriction to the PHP binary.
See [docs](https://www.php.net/manual/en/ini.core.php#ini.open-basedir).
This is applied to the web server that runs the website.
- Generating the `open_basedir` list is the most complex piece of logic
in this PR, because we want symlinked plugins and themes to just work.
To do this, we resolve any symlinks contained in the site directory and
watch the `wp-content` directory for new symlinks added while the site
is running. The symlink watcher triggers a PHP process restart.
5. Optionally, apply a `disable_functions` restriction to the PHP binary
that prevents PHP from doing a bunch of things that malicious plugins
could use to compromise the current machine.

## Testing Instructions

<!--
Add as many details as possible to help others reproduce the issue and
test the fix.
"Before / After" screenshots can also be very helpful when the change is
visual.
-->

1. Run `STUDIO_RUNTIME=native-php npm start`
2. If you don't already have a site with `runtime: native-php`, create a
new one
3. Start that site
4. Run `node apps/cli/dist/cli/main.mjs wp core version` and ensure it
works (do this before testing wp-admin)
6. Ensure that uploading media works
7. Ensure that installing plugins work
8. Do some light smoke testing of the site and ensure that nothing is
broken

## Pre-merge Checklist

<!--
Complete applicable items on this checklist **before** merging into
trunk. Inapplicable items can be left unchecked.

Both the PR author and reviewer are responsible for ensuring the
checklist is completed.
-->

- [ ] Have you checked for TypeScript, React or other console errors?

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

4 participants