diff --git a/packages/playground/blueprints/src/lib/steps/import-wordpress-files.ts b/packages/playground/blueprints/src/lib/steps/import-wordpress-files.ts index 8575e284a28..d54f81504eb 100644 --- a/packages/playground/blueprints/src/lib/steps/import-wordpress-files.ts +++ b/packages/playground/blueprints/src/lib/steps/import-wordpress-files.ts @@ -128,7 +128,7 @@ export const importWordPressFiles: StepHandler< // Remove the directory where we unzipped the imported zip file. await playground.rmdir(importPath); - // Ensure required constants are defined in wp-config.php. + // Ensure required constants are defined if wp-config.php doesn't define them. await ensureWpConfig(playground, documentRoot); const newSiteUrl = await playground.absoluteUrl; diff --git a/packages/playground/wordpress/src/boot.ts b/packages/playground/wordpress/src/boot.ts index 158135d3052..c4df439db2c 100644 --- a/packages/playground/wordpress/src/boot.ts +++ b/packages/playground/wordpress/src/boot.ts @@ -233,9 +233,9 @@ export async function bootWordPress( php.defineConstant('WP_SITEURL', options.siteUrl); /* - * Add required constants to "wp-config.php" if they are not already defined. - * This is needed, because some WordPress backups and exports may not include - * definitions for some of the necessary constants. + * Ensure required constants are defined if "wp-config.php" doesn't define + * them. This is needed because some WordPress backups and exports may not + * include definitions for some of the necessary constants. */ await ensureWpConfig(php, requestHandler.documentRoot); // Run "before database" hooks to mount/copy more files in diff --git a/packages/playground/wordpress/src/test/wp-config.spec.ts b/packages/playground/wordpress/src/test/wp-config.spec.ts index fba41a48dde..239e0980896 100644 --- a/packages/playground/wordpress/src/test/wp-config.spec.ts +++ b/packages/playground/wordpress/src/test/wp-config.spec.ts @@ -7,99 +7,96 @@ import { joinPaths } from '@php-wasm/util'; const documentRoot = '/tmp'; const wpConfigPath = joinPaths(documentRoot, 'wp-config.php'); +const constsJsonPath = '/internal/shared/consts.json'; + +function getDefinedConstants(php: PHP): Record { + if (!php.fileExists(constsJsonPath)) { + return {}; + } + return JSON.parse(php.readFileAsText(constsJsonPath)); +} -/* - * Tests below execute the rewritten wp-config.php and assert on - * the JSON output, not just on define() substrings. This proves - * the file still parses and runs, constants have the expected - * runtime values, and no warnings or errors were introduced. - */ describe('ensureWpConfig', () => { let php: PHP; beforeEach(async () => { php = new PHP(await loadNodeRuntime(RecommendedPHPVersion)); }); - it('should define required constants when they are missing', async () => { - php.writeFile( - wpConfigPath, - ` DB_NAME, - ]);` - ); + it('should define DB_NAME via defineConstant when wp-config.php does not define it', async () => { + php.writeFile(wpConfigPath, ` DB_NAME]);`, }); + expect(response.errors).toHaveLength(0); + expect(response.json).toEqual({ DB_NAME: 'wordpress' }); }); - it('should only define missing constants', async () => { + it('should not define DB_NAME when wp-config.php already defines it', async () => { php.writeFile( wpConfigPath, ` DB_NAME, - 'DB_USER' => DB_USER, - 'AUTH_KEY' => AUTH_KEY, - 'WP_DEBUG' => WP_DEBUG, - ]);` + define( 'DB_NAME', 'custom-db' );` ); await ensureWpConfig(php, documentRoot); - const rewritten = php.readFileAsText(wpConfigPath); - expect(rewritten).toContain(`define( 'DB_NAME', 'wordpress' );`); - expect(rewritten).toContain(`define( 'DB_USER', 'unchanged' );`); - expect(rewritten).not.toContain( - `define( 'DB_USER', 'username_here' );` - ); - expect(rewritten).toContain(`define( 'AUTH_KEY', 'unchanged' );`); - expect(rewritten).not.toContain( - `define( 'AUTH_KEY', 'put your unique phrase here' );` + // wp-config.php should not be modified. + expect(php.readFileAsText(wpConfigPath)).toContain( + `define( 'DB_NAME', 'custom-db' );` ); - expect(rewritten).toContain(`define( 'WP_DEBUG', true );`); - expect(rewritten).not.toContain(`define( 'WP_DEBUG', false );`); - const response = await php.run({ code: rewritten }); - expect(response.json).toEqual({ - DB_NAME: 'wordpress', - DB_USER: 'unchanged', - AUTH_KEY: 'unchanged', - WP_DEBUG: true, - }); + // DB_NAME should not be in consts.json. + expect(getDefinedConstants(php)).not.toHaveProperty('DB_NAME'); }); - it('should not define required constants when they are already defined conditionally', async () => { + it('should not define DB_NAME when wp-config.php defines it conditionally', async () => { php.writeFile( wpConfigPath, ` DB_NAME, - ]);` + }` ); await ensureWpConfig(php, documentRoot); - const rewritten = php.readFileAsText(wpConfigPath); - expect(rewritten).not.toContain(`define( 'DB_NAME', 'wordpress' );`); + // wp-config.php should not be modified. + expect(php.readFileAsText(wpConfigPath)).toContain( + `define('DB_NAME','defined-conditionally');` + ); - const response = await php.run({ code: rewritten }); - expect(response.json).toEqual({ - DB_NAME: 'defined-conditionally', - }); + // DB_NAME should not be in consts.json. + expect(getDefinedConstants(php)).not.toHaveProperty('DB_NAME'); + }); + + it('should only define missing constants and preserve pre-existing ones', async () => { + php.writeFile( + wpConfigPath, + ` { let php: PHP; beforeEach(async () => { diff --git a/packages/playground/wordpress/src/wp-config.ts b/packages/playground/wordpress/src/wp-config.ts index 08fa9f8f96d..259991aef12 100644 --- a/packages/playground/wordpress/src/wp-config.ts +++ b/packages/playground/wordpress/src/wp-config.ts @@ -5,9 +5,10 @@ import type { UniversalPHP } from '@php-wasm/universal'; import wpConfigTransformer from './wp-config-transformer.php?raw'; /** - * Ensures that the "wp-config.php" file exists and required constants are defined. + * Ensures that "wp-config.php" exists and required constants are defined. * - * When a required constant is missing, it will be defined with a default value. + * - Copies "wp-config-sample.php" to "wp-config.php" if it doesn't exist. + * - Defines fallback values for missing constants without modifying "wp-config.php". * * @param php The PHP instance. * @param documentRoot The path to the document root. @@ -17,9 +18,6 @@ export async function ensureWpConfig( documentRoot: string ): Promise { const wpConfigPath = joinPaths(documentRoot, 'wp-config.php'); - const defaults = { - DB_NAME: 'wordpress', - }; /** * WordPress requires a wp-config.php file to be present during @@ -51,23 +49,10 @@ export async function ensureWpConfig( return; } - // Ensure required constants are defined. - const js = phpVars({ wpConfigPath, constants: defaults }); - const result = await php.run({ - code: `${wpConfigTransformer} - $wp_config_path = ${js.wpConfigPath}; - $transformer = WP_Config_Transformer::from_file($wp_config_path); - foreach ( ${js.constants} as $name => $value ) { - if ( ! $transformer->constant_exists( $name ) ) { - $transformer->define_constant($name, $value); - } - } - $transformer->to_file($wp_config_path); - `, + // Ensure missing constants are defined without modifying "wp-config.php". + await defineWpConfigConstantFallbacks(php, wpConfigPath, { + DB_NAME: 'wordpress', }); - if (result.errors.length > 0) { - throw new Error('Failed to auto-configure wp-config.php.'); - } } /** @@ -101,3 +86,52 @@ export async function defineWpConfigConstants( throw new Error('Failed to rewrite constants in wp-config.php.'); } } + +/** + * Defines fallback values for constants missing from "wp-config.php". + * + * This function does NOT modify "wp-config.php": + * + * 1. It checks "wp-config.php" to determine which constants are missing. + * 2. It defines the missing constants via the PHP auto-prepend script. + * + * @param php The PHP instance. + * @param wpConfigPath The path to the "wp-config.php" file. + * @param fallbacks The constants to define if missing. + */ +async function defineWpConfigConstantFallbacks( + php: UniversalPHP, + wpConfigPath: string, + fallbacks: Record +): Promise { + const constantNames = Object.keys(fallbacks); + const js = phpVars({ wpConfigPath, constantNames }); + const result = await php.run({ + code: `${wpConfigTransformer} + $transformer = WP_Config_Transformer::from_file(${js.wpConfigPath}); + $missing = []; + foreach (${js.constantNames} as $name) { + if (!$transformer->constant_exists($name)) { + $missing[] = $name; + } + } + echo json_encode($missing); + `, + }); + if (result.errors.length > 0) { + throw new Error('Failed to check wp-config.php for constants.'); + } + + // Define the missing constants via the PHP auto-prepend script. + let missing: string[]; + try { + missing = JSON.parse(result.text); + } catch { + throw new Error( + `Failed to parse wp-config.php constant check output: ${result.text}` + ); + } + for (const name of missing) { + await php.defineConstant(name, fallbacks[name]); + } +}