Skip to content

Commit

Permalink
Enable CURL in Playground Web
Browse files Browse the repository at this point in the history
Enables the CURL PHP extension on playground.wordpress.net when
networking is enabled.

The heavy lifting was done in #1926. All this PR does is:

* Enables the curl extension
* Rebuilds PHP.wasm for the web
* Enables curl_exec and curl_multiexec functions in web browsers
* Unrelated – adds a JSPI vs Asyncify indication to the SAPI name so
  that we can easily learn which PHP.wasm build Playground is running.

Related to #85
Closes #1008

 ## Testing instrucions

Confirm the new E2E tests are sound and that they work in CI. You could
also try installing a CURL-reliant plugin such as Plausible and confirm
it installs without the fatal errors reported in #1008
  • Loading branch information
adamziel committed Oct 23, 2024
1 parent 7fa40be commit 9493096
Show file tree
Hide file tree
Showing 11 changed files with 103 additions and 25 deletions.
13 changes: 2 additions & 11 deletions packages/php-wasm/compile/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,7 @@ const platformDefaults = {
WITH_LIBZIP: 'yes',
WITH_SQLITE: 'yes',
WITH_JSPI: 'no',
},
web: {
WITH_CURL: 'yes',
WITH_FILEINFO: 'yes',
WITH_ICONV: 'yes',
WITH_LIBXML: 'yes',
Expand All @@ -137,19 +136,11 @@ const platformDefaults = {
WITH_OPENSSL: 'yes',
WITH_WS_NETWORKING_PROXY: 'yes',
},
web: {},
node: {
WITH_CURL: 'yes',
WITH_FILEINFO: 'yes',
WITH_ICONV: 'yes',
WITH_LIBXML: 'yes',
WITH_GD: 'yes',
WITH_MBSTRING: 'yes',
WITH_MBREGEX: 'yes',
WITH_CLI_SAPI: 'yes',
WITH_OPENSSL: 'yes',
WITH_NODEFS: 'yes',
WITH_MYSQL: 'yes',
WITH_WS_NETWORKING_PROXY: 'yes',
},
};
const platform = args.PLATFORM;
Expand Down
6 changes: 5 additions & 1 deletion packages/php-wasm/compile/php/php_wasm.c
Original file line number Diff line number Diff line change
Expand Up @@ -958,7 +958,11 @@ static char *wasm_sapi_getenv(char *name, unsigned long name_len)

SAPI_API sapi_module_struct php_wasm_sapi_module = {
"wasm", /* name */
"PHP WASM SAPI", /* pretty name */
#ifdef PLAYGROUND_JSPI
"PHP WASM SAPI (JSPI)", /* pretty name */
#else
"PHP WASM SAPI (Asyncify)", /* pretty name */
#endif

wasm_sapi_module_startup, /* startup */
wasm_sapi_shutdown_wrapper, /* shutdown */
Expand Down
3 changes: 1 addition & 2 deletions packages/php-wasm/universal/src/lib/php.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,7 @@ export class PHP implements Disposable {
'always_populate_raw_post_data = -1',
'upload_max_filesize = 2000M',
'post_max_size = 2000M',
'disable_functions = curl_exec,curl_multi_exec',
'allow_url_fopen = Off',
'allow_url_fopen = On',
'allow_url_include = Off',
'session.save_path = /home/web_user',
'implicit_flush = 1',
Expand Down
Binary file modified packages/php-wasm/web/public/php/jspi/8_1_23/php_8_1.wasm
Binary file not shown.
Binary file modified packages/php-wasm/web/public/php/jspi/8_2_10/php_8_2.wasm
Binary file not shown.
Binary file modified packages/php-wasm/web/public/php/jspi/8_3_0/php_8_3.wasm
Binary file not shown.
2 changes: 1 addition & 1 deletion packages/php-wasm/web/public/php/jspi/php_8_1.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/php-wasm/web/public/php/jspi/php_8_2.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/php-wasm/web/public/php/jspi/php_8_3.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 11 additions & 4 deletions packages/playground/remote/src/lib/worker-thread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,9 @@ export class PlaygroundWorkerEndpoint extends PHPWorker {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const endpoint = this;
const knownRemoteAssetPaths = new Set<string>();
const phpIniEntries: Record<string, string> = {
'openssl.cafile': '/internal/ca-bundle.crt',
};
let CAroot: false | GeneratedCertificate = false;
let tcpOverFetch: TCPOverFetchOptions | undefined = undefined;
if (withNetworking) {
Expand All @@ -260,6 +263,13 @@ export class PlaygroundWorkerEndpoint extends PHPWorker {
tcpOverFetch = {
CAroot,
};
} else {
phpIniEntries['allow_url_fopen'] = '0';
// Calling curl_exec() with networking disabled causes PHP to
// enter an infinite loop. Let's disable it completely to
// throw a fatal error instead.
phpIniEntries['disable_functions'] =
'curl_exec,curl_multi_exec';
}
const requestHandler = await bootWordPress({
siteUrl: setURLScope(wordPressSiteUrl, scope).toString(),
Expand Down Expand Up @@ -330,10 +340,7 @@ export class PlaygroundWorkerEndpoint extends PHPWorker {
}
},
},
phpIniEntries: {
allow_url_fopen: 'On',
'openssl.cafile': '/internal/ca-bundle.crt',
},
phpIniEntries,
createFiles: {
'/internal/ca-bundle.crt': CAroot
? certificateToPEM(CAroot.certificate)
Expand Down
85 changes: 81 additions & 4 deletions packages/playground/website/playwright/e2e/blueprints.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,87 @@ test('wp-cli step should create a post', async ({ website, wordpress }) => {
).toBeVisible();
});

test('HTTPS requests via curl_exec() should work', async ({
website,
wordpress,
}) => {
const blueprint: Blueprint = {
landingPage: '/curl-test.php',
features: { networking: true },
steps: [
{
step: 'writeFile',
path: '/wordpress/curl-test.php',
/**
* Dump the length of a known README.md file from the WordPress Playground repository.
*
* The URL:
*
* * Is served over HTTPS.
* * References a specific commit to avoid the file changing underfoot.
* * The server provides the CORS headers required for fetch() to work.
*/
data: `<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://raw.githubusercontent.com/WordPress/wordpress-playground/5e5ba3e0f5b984ceadd5cbe6e661828c14621d25/README.md");
curl_setopt($ch, CURLOPT_TCP_NODELAY, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($ch);
curl_close($ch);
var_dump(
strlen(
$result
)
);
`,
},
],
};
await website.goto(`/#${JSON.stringify(blueprint)}`);
await expect(wordpress.locator('body')).toContainText('int(13061)');
});

test('HTTPS requests via curl_exec() should fail when networking is disabled', async ({
website,
wordpress,
}) => {
const blueprint: Blueprint = {
landingPage: '/curl-test.php',
steps: [
{
step: 'writeFile',
path: '/wordpress/curl-test.php',
/**
* Dump the length of a known README.md file from the WordPress Playground repository.
*
* The URL:
*
* * Is served over HTTPS.
* * References a specific commit to avoid the file changing underfoot.
* * The server provides the CORS headers required for fetch() to work.
*/
data: `<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://raw.githubusercontent.com/WordPress/wordpress-playground/5e5ba3e0f5b984ceadd5cbe6e661828c14621d25/README.md");
curl_setopt($ch, CURLOPT_TCP_NODELAY, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($ch);
curl_close($ch);
var_dump(
strlen(
$result
)
);
`,
},
],
};
await website.goto(`/#${JSON.stringify(blueprint)}`);
await expect(wordpress.locator('body')).toContainText(
'Call to undefined function curl_exec()'
);
});

test('HTTPS requests via file_get_contents() should work', async ({
website,
wordpress,
Expand All @@ -141,7 +222,6 @@ test('HTTPS requests via file_get_contents() should work', async ({
landingPage: '/https-test.php',
features: { networking: true },
steps: [
{ step: 'login' },
{
step: 'writeFile',
path: '/wordpress/https-test.php',
Expand Down Expand Up @@ -177,7 +257,6 @@ test('HTTPS requests via file_get_contents() should fail when networking is disa
const blueprint: Blueprint = {
landingPage: '/https-test.php',
steps: [
{ step: 'login' },
{
step: 'writeFile',
path: '/wordpress/https-test.php',
Expand Down Expand Up @@ -216,7 +295,6 @@ test('HTTPS requests via file_get_contents() to invalid URLs should fail', async
landingPage: '/https-test.php',
features: { networking: true },
steps: [
{ step: 'login' },
{
step: 'writeFile',
path: '/wordpress/https-test.php',
Expand Down Expand Up @@ -249,7 +327,6 @@ test('HTTPS requests via file_get_contents() to CORS-disabled URLs should fail',
landingPage: '/https-test.php',
features: { networking: true },
steps: [
{ step: 'login' },
{
step: 'writeFile',
path: '/wordpress/https-test.php',
Expand Down

0 comments on commit 9493096

Please sign in to comment.