Skip to content

Make the pull-reprint command work with native PHP#3881

Merged
fredrikekelund merged 10 commits into
trunkfrom
stu-1814-make-pull-reprint-work-with-native-php
Jun 25, 2026
Merged

Make the pull-reprint command work with native PHP#3881
fredrikekelund merged 10 commits into
trunkfrom
stu-1814-make-pull-reprint-work-with-native-php

Conversation

@fredrikekelund

Copy link
Copy Markdown
Contributor

Related issues

How AI was used in this PR

I used Claude to help me with the initial implementation. Then I had to slow down to understand the pull-reprint architecture and the changes that Claude made. I went through a few rounds of iterations by hand and with Claude before reaching the final state.

Proposed Changes

The pull-reprint command comes with more assumptions about the PHP runtime than most other Studio features do. The prime example is how it uses PHP-WASM-based parsing to extract constants from runtime.php.

Studio now has two PHP runtimes, and native PHP is slated to become the new default shortly. This PR makes the pull-reprint command work with native PHP. This implementation does not cover all bases, but it works when pulling a typical WordPress.com site. We'll keep iterating to refine the experience.

Testing Instructions

  1. Run npm run cli:build
  2. Run STUDIO_ENABLE_PULL_REPRINT=1 STUDIO_RUNTIME=native-php node apps/cli/dist/cli/main.mjs pull-reprint
  3. Ensure that you can successfully complete a pull, that the site starts and works

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?

This commit includes a static reprint.phar build, because the latest HEAD in the reprint repo includes a fix we need (that hasn't been released yet)
@fredrikekelund fredrikekelund requested a review from epeicher June 18, 2026 08:22
@fredrikekelund fredrikekelund self-assigned this Jun 18, 2026
Comment thread wp-files/reprint/reprint.phar Outdated
Comment on lines -225 to -245
// For sites imported via `studio pull-reprint`, the pull command
// persists the computed start options to start-options.json so the
// daemon doesn't need to recompute them (which would spin up PHP
// WASM to extract runtime.php constants from the imported site).
if ( ! config.useExactMountLayout && config.blueprint?.uri ) {
try {
const optionsPath = path.join( path.dirname( config.blueprint.uri ), 'start-options.json' );
if ( fs.existsSync( optionsPath ) ) {
const saved = JSON.parse( fs.readFileSync( optionsPath, 'utf-8' ) );
if ( saved.useExactMountLayout ) {
config.mountsBeforeInstall = saved.mountsBeforeInstall;
config.mounts = saved.mounts;
config.wordpressInstallMode = saved.wordpressInstallMode ?? config.wordpressInstallMode;
config.useExactMountLayout = true;
logToConsole( `Loaded persisted start options from ${ optionsPath } before startup` );
}
}
} catch {
// Ignore missing or invalid start options and continue with the provided config.
}
}

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.

We get this data straight from the config object sent over IPC. No need to re-read start-options.json

} );

const wordpressInstallModeSchema = z.enum( [
const wordpressInstallModeSchema: z.ZodType< WordPressInstallMode > = z.enum( [

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.

We cannot derive a schema from the WordPressInstallMode type, but we can ensure that our manually defined schema matches the given type.

@fredrikekelund fredrikekelund marked this pull request as ready for review June 18, 2026 13:32

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 had to upgrade Reprint to the latest release to make my changes work. This new approach ensures we still ship a fixed Reprint version with every release (i.e. it's not updated on the user's machine), but we also don't have to explicitly change our code to get the latest version. I think that's a good approach

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 agree with this approach. It was originally asked by @wojtekn here if we should use a tagged version, but given the fact that Reprint is a known dependency, I think it is safe to get the latest version, and we don't have to change our code to upgrade the version.

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

Thanks for solving this @fredrikekelund! This is now required after merging #3786 because the pull-reprint is now failing. With this solution, we would not need #3922.

I have added an early review, and the changes look good to me. I have just left some questions/comments. Please let me know when you resolve the merging conflicts and I can give some testing.

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 agree with this approach. It was originally asked by @wojtekn here if we should use a tagged version, but given the fact that Reprint is a known dependency, I think it is safe to get the latest version, and we don't have to change our code to upgrade the version.

Comment thread apps/cli/lib/native-php/php-process.ts Outdated
}

reject( new Error( `PHP command failed (code: ${ code })` ) );
reject( new Error( `PHP command (${ args.join( ' ' ) }) failed (code: ${ code })` ) );

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.

In case of an error, is the stderr logged somewhere after these changes?

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.

This change was actually a leftover that I reverted.

What happens to the output of the child process depends on the mode option passed to the runPhpCommand() function

@wpmobilebot

wpmobilebot commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing 1100980 vs trunk

app-size

Metric trunk 1100980 Diff Change
App Size (Mac) 1311.91 MB 1311.89 MB 0.02 MB ⚪ 0.0%

site-editor

Metric trunk 1100980 Diff Change
load 1108 ms 1094 ms 14 ms ⚪ 0.0%

site-startup

Metric trunk 1100980 Diff Change
siteCreation 6494 ms 6513 ms +19 ms ⚪ 0.0%
siteStartup 6553 ms 6558 ms +5 ms ⚪ 0.0%

Results are median values from multiple test runs.

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

Comment thread apps/cli/php/router.php

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.

The changes in this file are necessary to prevent auto_prepend_file from firing twice for requests that target PHP files directly (like /wp-admin/admin-ajax.php).

If auto_prepend_file fires twice, it could cause classes to be redeclared, for example, which triggers an error.

@fredrikekelund fredrikekelund requested a review from epeicher June 24, 2026 11:56

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

Thanks for working on this @fredrikekelund! I have tested, and the pull-reprint works, but when the site is going to start, I'm getting the following error in the logs:

Error: Error establishing a database connection.

I am testing this by running:

node apps/cli/dist/cli/main.mjs pull-reprint --url <site-url>

This happens consistently when pulling a new site and creating a new site in Studio:

CleanShot 2026-06-24 at 16 18 21@2x

);
preflight = await runPreflight( studioMetadata, apiUrl, secret, verbose );
preflight = await runPreflight(
SITE_RUNTIME_NATIVE_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.

Should we use a dynamic value for this SITE_RUNTIME_NATIVE_PHP? I'm wondering what happens if a user selects the playground runtime in Studio. Not a blocker, just a question for consideration.

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.

We need to iterate on this in another PR. I'm trying to think of ways that we could tie the pull-reprint state management closer to the regular Studio state management (which is site-based rather than pull-based). That would help address this issue

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.

Great

Comment thread scripts/download-wp-server-files.ts Outdated
const asset = release.assets.find( ( a ) => a.name === 'reprint.phar' );
if ( ! asset ) {
throw new Error(
`No asset found in latest wp-cli-sqlite-command release ${ release.tag_name }`

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.

Wrong message from another Error probably

@epeicher

Copy link
Copy Markdown
Contributor

After some investigation, I have created this draft PR in Reprint that seems to fix the issue described here:

To test it, what I do is:

  1. On that branch, run composer build:phar
  2. Copy reprint.phar to the Studio repo wp-files/reprint/
  3. Run npm run cli:build on Studio
  4. Test by running node apps/cli/dist/cli/main.mjs pull-reprint --url mypurewebsite5.wpcomstaging.com

Hope that helps

@fredrikekelund

Copy link
Copy Markdown
Contributor Author

Error: Error establishing a database connection.

I could not reproduce this exact error, but the root cause of this was a late change on my part from passing --runtime=nginx-fpm to --runtime=php-builtin. I tested this, but clearly not thoroughly enough.

Your PR seems worth considering, but I changed the option back to --runtime=nginx-fpm for now so that we can land this PR and proceed with the other items on our list.

@fredrikekelund fredrikekelund requested a review from epeicher June 25, 2026 07:39

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

Thanks @fredrikekelund for addressing the comments. This is working perfectly now, I have tested a new pull and an incremental pull and everything works as expected. LGTM!

Image

);
preflight = await runPreflight( studioMetadata, apiUrl, secret, verbose );
preflight = await runPreflight(
SITE_RUNTIME_NATIVE_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.

Great

@epeicher

epeicher commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Your PR seems worth considering, but I changed the option back to --runtime=nginx-fpm for now so that we can land this PR and proceed with the other items on our list.

I think that PR is not required eventually, your changes fix the original issue, I have closed it and linked back here for the reasoning.

@fredrikekelund fredrikekelund merged commit de57937 into trunk Jun 25, 2026
13 checks passed
@fredrikekelund fredrikekelund deleted the stu-1814-make-pull-reprint-work-with-native-php branch June 25, 2026 08:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants