Skip to content
Merged
40 changes: 40 additions & 0 deletions apps/cli/lib/native-php/site-setup.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { DEFAULT_LOCALE } from '@studio/common/lib/locale';
import { escapePhpSingleQuotedString } from '@studio/common/lib/mu-plugins';
import { decodePassword } from '@studio/common/lib/passwords';
import { getWpCliPharPath } from 'cli/lib/dependency-management/paths';
import { runPhpCommand } from './php-process';
Expand Down Expand Up @@ -65,6 +67,44 @@ $transformer->to_file( $wp_config_path );
}
}

export function getSiteUrlPrependContent(
siteUrl: string,
originalAutoPrependFile?: string
): string {
const escapedSiteUrl = escapePhpSingleQuotedString( siteUrl );
const chained = originalAutoPrependFile
? `require '${ escapePhpSingleQuotedString( originalAutoPrependFile ) }';`
: '';

// Define WP_HOME/WP_SITEURL before WordPress boots so the site serves from
// the local URL even when the DB still holds a remote one. Running pre-boot
// means derived URLs (WP_CONTENT_URL, etc.) resolve locally too.
return `<?php
if ( ! defined( 'WP_HOME' ) ) {
define( 'WP_HOME', '${ escapedSiteUrl }' );
}
if ( ! defined( 'WP_SITEURL' ) ) {
define( 'WP_SITEURL', '${ escapedSiteUrl }' );
}
${ chained }
`;
}

/**
* Writes the auto_prepend_file that forces WordPress to serve from the local
* URL. Returns the path to use as auto_prepend_file. For imported sites it
* chains to reprint's own runtime.php so that prepend still runs.
*/
export function writeSiteUrlPrependFile(
siteUrl: string,
originalAutoPrependFile?: string
): string {
const dir = fs.mkdtempSync( path.join( os.tmpdir(), 'studio-siteurl-prepend-' ) );
const prependPath = path.join( dir, 'prepend.php' );
fs.writeFileSync( prependPath, getSiteUrlPrependContent( siteUrl, originalAutoPrependFile ) );
return prependPath;
}

export async function isWordPressInstalled(
siteFolder: string,
phpVersion: NativePhpSupportedVersion,
Expand Down
13 changes: 11 additions & 2 deletions apps/cli/php-server-child.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ import {
getPhpMyAdminSessionPath,
writeNativePhpMyAdminWpEnv,
} from './lib/native-php/phpmyadmin';
import { ensureWpConfig, installWordPress } from './lib/native-php/site-setup';
import {
ensureWpConfig,
installWordPress,
writeSiteUrlPrependFile,
} from './lib/native-php/site-setup';
import { SymlinkWatcher, collectSymlinkAllowlistEntries } from './lib/symlinks';
import type { ChildProcess } from 'node:child_process';

Expand Down Expand Up @@ -509,6 +513,9 @@ async function startServer( config: ServerConfig, signal: AbortSignal ): Promise
currentOpenBasedirAllowlist.add( getPhpMyAdminSessionPath( config ) );
currentOpenBasedirAllowlist.add( muPluginsPath );
currentOpenBasedirAllowlist.add( os.tmpdir() );
if ( config.autoPrependFile ) {
currentOpenBasedirAllowlist.add( path.dirname( config.autoPrependFile ) );
}
symlinkAllowlistEntries.forEach( ( entry ) => currentOpenBasedirAllowlist.add( entry ) );
config.openBasedirAllowList?.forEach( ( entry ) => currentOpenBasedirAllowlist.add( entry ) );
}
Expand Down Expand Up @@ -553,6 +560,8 @@ async function doStartServer(

try {
const phpMyAdminWpEnvPath = await writeNativePhpMyAdminWpEnv( config );
const siteUrl = config.absoluteUrl || `http://localhost:${ config.port }`;
const autoPrependFile = writeSiteUrlPrependFile( siteUrl, config.autoPrependFile );
const workerPorts: number[] = [];
for ( let index = 0; index < NATIVE_PHP_WORKER_POOL_SIZE; index++ ) {
workerPorts.push( await getAvailablePort() );
Expand All @@ -579,7 +588,7 @@ async function doStartServer(
onlyPathsThatPhpCanAccess: Array.from( openBasedirAllowlist ),
disallowRiskyFunctions: isFileAccessRestricted( config ),
enableXdebug: config.enableXdebug,
autoPrependFile: config.autoPrependFile,
autoPrependFile,
} );
spawnedChildren.push( serverChild );

Expand Down
7 changes: 7 additions & 0 deletions apps/cli/php/router.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@
* WP::parse_request() can resolve it against the rewrite rules.
*/

// PHP <= 8.3's built-in server doesn't apply auto_prepend_file to the router
// script (PHP bug #64566; fixed in 8.4), so apply it ourselves.
$studio_auto_prepend = ini_get( 'auto_prepend_file' );
if ( $studio_auto_prepend && is_file( $studio_auto_prepend ) ) {
require_once $studio_auto_prepend;
}

$root = realpath( $_SERVER['DOCUMENT_ROOT'] ?? '' ) ?: getcwd();
$path = urldecode( parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH ) );
$file = $root . $path;
Expand Down
2 changes: 1 addition & 1 deletion tools/common/lib/mu-plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const NATIVE_PHP_EXCLUDED_MU_PLUGINS = new Set( [
'0-http-request-timeout.php',
] );

function escapePhpSingleQuotedString( value: string ): string {
export function escapePhpSingleQuotedString( value: string ): string {
return value.replace( /\\/g, '\\\\' ).replace( /'/g, "\\'" );
}

Expand Down