diff --git a/packages/php-wasm/node-polyfills/src/index.ts b/packages/php-wasm/node-polyfills/src/index.ts index 2432ff8272..08fc906f47 100644 --- a/packages/php-wasm/node-polyfills/src/index.ts +++ b/packages/php-wasm/node-polyfills/src/index.ts @@ -1,2 +1,3 @@ import './lib/blob'; import './lib/custom-event'; +import './lib/url'; diff --git a/packages/php-wasm/node-polyfills/src/lib/url.ts b/packages/php-wasm/node-polyfills/src/lib/url.ts new file mode 100644 index 0000000000..1997c015aa --- /dev/null +++ b/packages/php-wasm/node-polyfills/src/lib/url.ts @@ -0,0 +1,23 @@ +import { currentJsRuntime } from './current-js-runtime'; + +if (currentJsRuntime === 'NODE') { + /** + * Polyfill for URL.canParse if it's missing. + * + * URL.canParse is available since Node 19.9.0, + * but Playground currently uses Node 18.18.0. + * + * This implementation is based on the one from `core-js` + * by Denis Pushkarev, https://github.com/zloirock + * https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/web.url.can-parse.js + */ + if (typeof URL.canParse !== 'function') { + globalThis.URL.canParse = function (url: string) { + try { + return !!new URL(url); + } catch (e) { + return false; + } + }; + } +} diff --git a/packages/php-wasm/universal/src/lib/php-request-handler.ts b/packages/php-wasm/universal/src/lib/php-request-handler.ts index 135f9e0f0b..7b984ac8b2 100644 --- a/packages/php-wasm/universal/src/lib/php-request-handler.ts +++ b/packages/php-wasm/universal/src/lib/php-request-handler.ts @@ -318,9 +318,7 @@ export class PHPRequestHandler { * @param request - PHP Request data. */ async request(request: PHPRequest): Promise { - const isAbsolute = - request.url.startsWith('http://') || - request.url.startsWith('https://'); + const isAbsolute = URL.canParse(request.url); const requestedUrl = new URL( // Remove the hash part of the URL as it's not meant for the server. request.url.split('#')[0], diff --git a/packages/php-wasm/web-service-worker/src/utils.ts b/packages/php-wasm/web-service-worker/src/utils.ts index c5da650b8b..8dc3256912 100644 --- a/packages/php-wasm/web-service-worker/src/utils.ts +++ b/packages/php-wasm/web-service-worker/src/utils.ts @@ -63,6 +63,31 @@ export async function convertFetchEventToPHPRequest(event: FetchEvent) { throw e; } + /** + * Safari has a bug that prevents Service Workers from redirecting relative URLs. + * When attempting to redirect to a relative URL, the network request will return an error. + * See the Webkit bug for details: https://bugs.webkit.org/show_bug.cgi?id=282427. + * + * Because PHP and WordPress can redirect to both relative and absolute URLs + * using the `location` header we need to ensure redirects are processed + * correctly by the Service Worker. + * + * As a workaround for Safari Service Workers, we use `Response.redirect()` + * for all redirect responses (300-399 status codes) coming from PHP. + * This solution was suggested in the Webkit bug comment: + * https://bugs.webkit.org/show_bug.cgi?id=282427#c2 + */ + if ( + phpResponse.httpStatusCode >= 300 && + phpResponse.httpStatusCode <= 399 && + phpResponse.headers['location'] + ) { + return Response.redirect( + phpResponse.headers['location'][0], + phpResponse.httpStatusCode + ); + } + return new Response(phpResponse.bytes, { headers: phpResponse.headers, status: phpResponse.httpStatusCode, diff --git a/packages/playground/wordpress/src/index.ts b/packages/playground/wordpress/src/index.ts index ac0dfb13b6..5e37689697 100644 --- a/packages/playground/wordpress/src/index.ts +++ b/packages/playground/wordpress/src/index.ts @@ -160,19 +160,9 @@ export async function setupPlatformLevelMuPlugins(php: UniversalPHP) { * Reload page to ensure the user is logged in correctly. * WordPress uses cookies to determine if the user is logged in, * so we need to reload the page to ensure the cookies are set. - * - * Both WordPress home url and REQUEST_URI may include the site scope - * subdirectory. - * To prevent the redirect from including two scopes, we need to - * remove the scope from the REQUEST_URI if it's present. */ - $redirect_url = $_SERVER['REQUEST_URI']; - if (strpos($redirect_url, '/scope:') === 0) { - $parts = explode('/', $redirect_url); - $redirect_url = '/' . implode('/', array_slice($parts, 2)); - } wp_redirect( - home_url($redirect_url), + $_SERVER['REQUEST_URI'], 302 ); exit;