Skip to content

Fix native PHP sites loading assets from remote siteurl/home (STU-1925) - #3988

Merged
bcotrim merged 4 commits into
trunkfrom
fetch-stu-1925
Jun 29, 2026
Merged

Fix native PHP sites loading assets from remote siteurl/home (STU-1925)#3988
bcotrim merged 4 commits into
trunkfrom
fetch-stu-1925

Conversation

@bcotrim

@bcotrim bcotrim commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Related issues

How AI was used in this PR

Claude Code investigated the root cause, implemented the fix, and verified it end-to-end against a pulled site (cotrim.dev). I reviewed the diff and tested manually via the CLI.

Proposed Changes

When a site pulled from a remote (e.g. a WordPress.com staging site) runs on the native PHP runtime, it kept loading assets from the remote URL because the database's siteurl/home were served as-is. Native PHP now defines WP_HOME/WP_SITEURL before WordPress boots — derived from the request, so it survives dynamic ports and custom domains and never mutates the database (a push still sends the original remote URL). This also fixes a latent issue: PHP's built-in server ignores auto_prepend_file when a router script is used, so reprint's runtime.php never ran for imported native sites — the router now applies it explicitly.

Testing Instructions

  1. On the native PHP runtime, use a site whose database holds a remote siteurl (pull a WP.com staging site, or set siteurl/home to a remote URL in the DB).
  2. Start the site, open it, and check DevTools → Network.
  3. Before: theme/plugin assets (e.g. style.css) load from the remote domain. After: all assets load from http://localhost:<port>, and wp-json reports the local url/home.
  4. wp-cli still reports the stored URL (intentionally untouched).

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?
@bcotrim bcotrim self-assigned this Jun 29, 2026
@bcotrim
bcotrim requested review from a team and fredrikekelund June 29, 2026 11:39
Comment thread apps/cli/php/router.php Outdated
Comment on lines +13 to +16
// PHP's built-in server ignores the auto_prepend_file ini directive when a
// router script is in use, so apply it ourselves before dispatching. This is
// how Studio's pre-boot prepend (local WP_HOME/WP_SITEURL) and reprint's
// runtime.php for imported sites get a chance to run.

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.

This is not true.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I tested this further and it seems to be related with PHP version.
Same Studio site, without the fix in router.php

PHP 8.3 PHP 8.4
image image

Can you see that same behavior?

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.

Yep, I see the same thing. Some PHP 8.4 release seems to have changed this behavior.

require_once seems to work really well in this context. With this change, auto_prepend_file as a config directive just keeps working, regardless of PHP version. Nice 👍

@wpmobilebot

wpmobilebot commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing 7f11e26 vs trunk

app-size

Metric trunk 7f11e26 Diff Change
App Size (Mac) 1315.58 MB 1315.58 MB 0.00 MB ⚪ 0.0%

site-editor

Metric trunk 7f11e26 Diff Change
load 1068 ms 1077 ms +9 ms ⚪ 0.0%

site-startup

Metric trunk 7f11e26 Diff Change
siteCreation 6541 ms 6482 ms 59 ms 🟢 -0.9%
siteStartup 6579 ms 6968 ms +389 ms 🔴 5.9%

Results are median values from multiple test runs.

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

@fredrikekelund fredrikekelund 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.

Good catch @shaunandrews and nice work, @bcotrim 👍 This is a nice and targeted fix, and I'm glad we found the auto_prepend_file discrepancy between PHP versions.

Let's generate the siteurl / home URL deterministically (since we already know which value we want). Other than that – this LGTM

Comment thread apps/cli/lib/native-php/site-setup.ts Outdated
Comment on lines +86 to +87
$studio_is_https = ( ! empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' )
|| ( ! empty( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && stripos( $_SERVER['HTTP_X_FORWARDED_PROTO'], 'https' ) !== false );

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.

The PHP child processes never use HTTPS. Only the HTTPS proxy does that in in Studio. Might as well remove this part

Comment thread apps/cli/lib/native-php/site-setup.ts Outdated
if ( PHP_SAPI !== 'cli' && ! empty( $_SERVER['HTTP_HOST'] ) ) {
$studio_is_https = ( ! empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' )
|| ( ! empty( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && stripos( $_SERVER['HTTP_X_FORWARDED_PROTO'], 'https' ) !== false );
$studio_local_url = ( $studio_is_https ? 'https' : 'http' ) . '://' . $_SERVER['HTTP_HOST'];

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.

I mentioned this on Slack, too, but I think we should generate this URL deterministically instead of looking at the Host header. There's just no reason to use a dynamic value here, since we already know the complete URL we want for siteurl and home

Comment thread apps/cli/lib/native-php/site-setup.ts Outdated
Comment on lines +107 to +108
const hash = crypto.createHash( 'sha1' ).update( sitePath ).digest( 'hex' ).slice( 0, 16 );
const prependPath = path.join( os.tmpdir(), `studio-siteurl-prepend-${ hash }.php` );

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.

This is fine, but we could use fs.mktempd for a similar effect, instead of generating a hash ourselves https://nodejs.org/docs/latest/api/fs.html#fspromisesmkdtempprefix-options

Comment thread apps/cli/lib/native-php/site-setup.ts Outdated
Comment on lines +80 to +85
// Define WP_HOME/WP_SITEURL before WordPress boots so the site serves from
// the local URL regardless of the siteurl/home in the DB (e.g. after pulling
// a remote site — STU-1925). Pre-boot so derived URLs (WP_CONTENT_URL, etc.)
// resolve locally too. The URL is the site's configured local URL (custom
// domain or http://localhost:PORT), matching the Playground runtime's
// --site-url, rather than the request Host header.

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.

I think we could slim this down, or indeed even remove it. Some of this is encoding the AI agent session history

@fredrikekelund fredrikekelund 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.

Confirming that the latest changes look good and test well 👍

@gcsecsey gcsecsey left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I can also confirm that the changes work as described with a freshly cloned site, I get no requests targeting the remote.

Image
@bcotrim
bcotrim enabled auto-merge (squash) June 29, 2026 14:17
@bcotrim
bcotrim merged commit 9f218c5 into trunk Jun 29, 2026
11 checks passed
@bcotrim
bcotrim deleted the fetch-stu-1925 branch June 29, 2026 14:32
gcsecsey pushed a commit that referenced this pull request Jun 29, 2026
… (#3988)

## Related issues

- Fixes STU-1925

## How AI was used in this PR

Claude Code investigated the root cause, implemented the fix, and
verified it end-to-end against a pulled site (cotrim.dev). I reviewed
the diff and tested manually via the CLI.

## Proposed Changes

When a site pulled from a remote (e.g. a WordPress.com staging site)
runs on the native PHP runtime, it kept loading assets from the remote
URL because the database's `siteurl`/`home` were served as-is. Native
PHP now defines `WP_HOME`/`WP_SITEURL` before WordPress boots — derived
from the request, so it survives dynamic ports and custom domains and
never mutates the database (a push still sends the original remote URL).
This also fixes a latent issue: PHP's built-in server ignores
`auto_prepend_file` when a router script is used, so reprint's
`runtime.php` never ran for imported native sites — the router now
applies it explicitly.

## Testing Instructions

1. On the **native PHP** runtime, use a site whose database holds a
remote `siteurl` (pull a WP.com staging site, or set `siteurl`/`home` to
a remote URL in the DB).
2. Start the site, open it, and check DevTools → Network.
3. **Before:** theme/plugin assets (e.g. `style.css`) load from the
remote domain. **After:** all assets load from
`http://localhost:<port>`, and `wp-json` reports the local `url`/`home`.
4. `wp-cli` still reports the stored URL (intentionally untouched).

## Pre-merge Checklist

- [x] Have you checked for TypeScript, React or other console errors?
@gcsecsey gcsecsey mentioned this pull request Jun 29, 2026
1 task
gcsecsey added a commit that referenced this pull request Jun 30, 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.
-->

- N/A

## How AI was used in this PR

Claude compared previous release-note sections and release-note PR
descriptions, and drafted the curated 1.12.0 wording. The wording was
reviewed in-thread before updating the PR.

## Proposed Changes

- Replace the generated `1.12.0` release-note block with a shorter,
user-facing summary.
- Added the two PRs cherry-picked onto the release branch after code
freeze: #3974 and #3988
- Folded all dependency/dependabot bumps into a single "Updated multiple
dependencies" line.
- Omitted internal-only PRs (test/CI infra, `AGENTS.md`/`STUDIO.md`
docs, pure refactors, and the experimental Studio Web / "hosted"
groundwork).


## Testing Instructions

- Read the `1.12.0` section of `RELEASE-NOTES.txt`
- Confirm the groupings and wording read well and are accurate
- Cross-check against the merged-PR list for anything important that
should be surfaced or reworded

## Pre-merge Checklist

- [ ] Have you checked for TypeScript, React or other console errors?
gavande1 added a commit that referenced this pull request Jul 6, 2026
…per-command CLI shutdown (#4082)

## Related issues

- AINFRA-2588 (Investigate Studio Windows E2E hangs in Buildkite)
- Supersedes the split verification work in #4075; overlaps with #4070
(included as-is) and #4061 (its tilde fix is included; its daemon
isolation is not)

## Proposed Changes

Windows E2E has been broken since June 29 by two independent regressions
that merged the same day. This PR carries the minimal combination that
fixes both, plus CI reporting fixes so failures can't hide:

**1. Per-command CLI shutdown handling (from #4070).** #3954's shared
`killAll()` on `will-quit` runs one listener after the quit handler that
spawns `site stop --all`, killing the stop command before it stops any
site. Sites leaked into the machine-global process-manager daemon until
its capacity cap was exhausted — the 3-hour hangs. Restoring per-child
quit handlers lets the quit-time stop survive; verified on CI: quit-time
stops complete in under a second (previously a 20-second timeout on
every session) and zero capacity errors.

**2. PHP INI tilde fixes.** #3988 passed the site-url prepend file
(reprint's runtime: constants + SQLite loader) to PHP as an unquoted `-d
auto_prepend_file=` value. On machines where the temp path contains a
Windows 8.3 short name (e.g. `C:\Users\BUILDK~1\...` — any username over
8 characters), PHP's INI parser fails on the `~` (`syntax error,
unexpected '~'`), keeps only the prefix, and every request dies with
`Fatal error: Failed opening required 'C:\Users\BUILDK'` before
WordPress boots. This broke every page load of every native-PHP site on
affected machines and caused the ~25 Windows E2E failures. Fixed at both
ends:
- `auto_prepend_file` is now quoted and backslash-normalized via the
existing `toPhpIniPath()`, like every other path directive.
- `getPhpSafeTmpDir()` resolves the Windows short name to its long form
for every temp path handed to PHP (opcache dir, phpMyAdmin
config/sessions, site-url prepend dir).

**3. Honest CI reporting.** The mac/Windows/Linux E2E jobs all posted to
the same "E2E Tests" GitHub status and the last writer won, so a fast
green mac job masked a failing or still-running Windows job. The notify
now lives on the E2E group: one status, pending until every platform
finishes. Also: `run-e2e-tests.sh` traps termination so a
canceled/timed-out job can't record exit status 0 and turn the build
green (observed in build 18744).

**4. Windows E2E re-enabled** with a 100-minute job cap. Prior
verification of these fixes together (#4075, build 18789): 47 passed / 0
failed / 26 minutes — the first green Windows E2E since June 29. This
PR's own CI re-verifies the combination as extracted here.

Deliberately not included, pending their own review: #4041 (bounded
daemon socket requests, daemon force-settle, leaked-daemon reaping) and
#4061's per-home daemon isolation. This PR's CI run doubles as the
experiment showing whether they are required for green E2E or are
hardening.

## Testing Instructions

- **CI**: all three E2E platforms should pass; the "E2E Tests" GitHub
status stays pending until mac, Windows, and Linux all finish, then
reports one combined result. The Windows job should show no `syntax
error, unexpected '~'` in daemon logs, no `site stop --all command timed
out` lines, and no `CAPACITY_LIMIT_REACHED` errors.
- **Unit**: `npm test -- apps/cli/tests/ apps/studio/src/tests/` passes.
- **Manual (Windows)**: on a machine whose user profile path gets
8.3-mangled (username over 8 characters), create and open a native-PHP
site — pages render instead of a PHP fatal.

## Pre-merge Checklist

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

---------

Co-authored-by: Gergely Csecsey <gergely.csecsey@automattic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

4 participants