Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explore: Setup SQLite database integration without creating wp-content/db.php #1382

Merged
merged 5 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 8 additions & 32 deletions packages/playground/cli/src/setup-wp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ import {
readAsFile,
} from './download';
import { withPHPIniValues } from './setup-php';
import { playgroundMuPlugin } from '@wp-playground/wordpress';
import {
playgroundMuPlugin,
preloadSqliteIntegration,
} from '@wp-playground/wordpress';

/**
* Ensures a functional WordPress installation in php document root.
Expand Down Expand Up @@ -49,7 +52,9 @@ export async function setupWordPress(
monitor
),
]);
await prepareWordPress(php, wpZip, sqliteZip);

await prepareWordPress(php, wpZip);
await preloadSqliteIntegration(php, sqliteZip);

const preinstalledWpContentPath = path.join(
CACHE_FOLDER,
Expand Down Expand Up @@ -105,7 +110,7 @@ export async function setupWordPress(
* accept the limitation, and switch to the PHP implementation as soon
* as that's viable.
*/
async function prepareWordPress(php: NodePHP, wpZip: File, sqliteZip: File) {
async function prepareWordPress(php: NodePHP, wpZip: File) {
php.mkdir('/internal/shared/mu-plugins');
php.writeFile(
'/internal/shared/mu-plugins/0-playground.php',
Expand All @@ -128,33 +133,4 @@ async function prepareWordPress(php: NodePHP, wpZip: File, sqliteZip: File) {
'/wordpress/wp-config.php',
php.readFileAsText('/wordpress/wp-config-sample.php')
);
// }}}

// Setup the SQLite integration {{{
php.mkdir('/tmp/sqlite-database-integration');
await unzip(php, {
zipFile: sqliteZip,
extractToPath: '/tmp/sqlite-database-integration',
});
php.mv(
'/tmp/sqlite-database-integration/sqlite-database-integration-main',
'/internal/shared/mu-plugins/sqlite-database-integration'
);

const dbPhp = php
.readFileAsText(
'/internal/shared/mu-plugins/sqlite-database-integration/db.copy'
)
.replace(
"'{SQLITE_IMPLEMENTATION_FOLDER_PATH}'",
"'/internal/shared/mu-plugins/sqlite-database-integration/'"
)
.replace(
"'{SQLITE_PLUGIN}'",
"'/internal/shared/mu-plugins/sqlite-database-integration/load.php'"
);
// @TODO do not create the db.php file. Either find a way to mount it, or
// load the custom $wpdb object in a different way.
php.writeFile('/wordpress/wp-content/db.php', dbPhp);
// }}}
}
3 changes: 0 additions & 3 deletions packages/playground/wordpress/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,5 @@
"publishConfig": {
"access": "public",
"directory": "../../../dist/packages/playground/wordpress"
},
"dependencies": {
"@php-wasm/universal": "^0.6.6"
}
}
5 changes: 1 addition & 4 deletions packages/playground/wordpress/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,7 @@
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": [
"packages/playground/wordpress/**/*.ts",
"packages/playground/wordpress/package.json"
]
"lintFilePatterns": ["packages/playground/wordpress/**/*.ts"]
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is technically unrelated, but this PR introduces a few dependencies that trigger an eslint warning – let's just get it in as a part of this PR.

}
}
},
Expand Down
112 changes: 112 additions & 0 deletions packages/playground/wordpress/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { UniversalPHP } from '@php-wasm/universal';
import { joinPaths, phpVar } from '@php-wasm/util';
import { unzip } from '@wp-playground/blueprints';

export * from './rewrite-rules';

export const RecommendedPHPVersion = '8.0';
Expand Down Expand Up @@ -105,3 +109,111 @@ export const specificMuPlugins = {
export const playgroundMuPlugin = Object.values(specificMuPlugins)
.map((p) => p.trim())
.join('\n');

export async function preloadSqliteIntegration(
php: UniversalPHP,
sqliteZip: File
) {
if (await php.isDir('/tmp/sqlite-database-integration')) {
await php.rmdir('/tmp/sqlite-database-integration', {
recursive: true,
});
}
await php.mkdir('/tmp/sqlite-database-integration');
await unzip(php, {
zipFile: sqliteZip,
extractToPath: '/tmp/sqlite-database-integration',
});
const SQLITE_PLUGIN_FOLDER = '/internal/shared/sqlite-database-integration';
await php.mv(
'/tmp/sqlite-database-integration/sqlite-database-integration-main',
SQLITE_PLUGIN_FOLDER
);
const dbCopy = await php.readFileAsText(
joinPaths(SQLITE_PLUGIN_FOLDER, 'db.copy')
);
const dbPhp = dbCopy
.replace(
"'{SQLITE_IMPLEMENTATION_FOLDER_PATH}'",
phpVar(SQLITE_PLUGIN_FOLDER)
)
.replace(
"'{SQLITE_PLUGIN}'",
phpVar(joinPaths(SQLITE_PLUGIN_FOLDER, 'load.php'))
);
const SQLITE_MUPLUGIN_PATH =
'/internal/shared/mu-plugins/sqlite-database-integration.php';
await php.writeFile(SQLITE_MUPLUGIN_PATH, dbPhp);
await php.writeFile(
`/internal/shared/preload/0-sqlite.php`,
`<?php
/**
* Loads the SQLite integration plugin before WordPress is loaded
* and without creating a drop-in "db.php" file.
*
* Technically, it creates a global $wpdb object whose only two
* purposes are to:
*
* * Exist – because the require_wp_db() WordPress function won't
* connect to MySQL if $wpdb is already set.
* * Load the SQLite integration plugin the first time it's used
* and replace the global $wpdb reference with the SQLite one.
*
* This lets Playground keep the WordPress installation clean and
* solves dillemas like:
*
* * Should we include db.php in Playground exports?
* * Should we remove db.php from Playground imports?
* * How should we treat stale db.php from long-lived OPFS sites?
*
* @see https://github.com/WordPress/wordpress-playground/discussions/1379 for
* more context.
*/
class Playground_SQLite_Integration_Loader {
public function __call($name, $arguments) {
$this->load_sqlite_integration();
if($GLOBALS['wpdb'] === $this) {
throw new Exception('Infinite loop detected in $wpdb – SQLite integration plugin could not be loaded');
}
return call_user_func_array(
array($GLOBALS['wpdb'], $name),
$arguments
);
}
public function __get($name) {
$this->load_sqlite_integration();
if($GLOBALS['wpdb'] === $this) {
throw new Exception('Infinite loop detected in $wpdb – SQLite integration plugin could not be loaded');
}
return $GLOBALS['wpdb']->$name;
}
public function __set($name, $value) {
$this->load_sqlite_integration();
if($GLOBALS['wpdb'] === $this) {
throw new Exception('Infinite loop detected in $wpdb – SQLite integration plugin could not be loaded');
}
$GLOBALS['wpdb']->$name = $value;
}
protected function load_sqlite_integration() {
require_once ${phpVar(SQLITE_MUPLUGIN_PATH)};
}
}
$wpdb = $GLOBALS['wpdb'] = new Playground_SQLite_Integration_Loader();
`
);
/**
* Ensure the SQLite integration is loaded and clearly communicate
* if it isn't. This is useful because WordPress database errors
* may be cryptic and won't mention the SQLite integration.
*/
await php.writeFile(
`/internal/shared/mu-plugins/sqlite-test.php`,
`<?php
global $wpdb;
if(!($wpdb instanceof WP_SQLite_DB)) {
var_dump(isset($wpdb));
die("SQLite integration not loaded " . get_class($wpdb));
}
`
);
}
Loading