Skip to content

Commit

Permalink
Revert "Route requests for missing static files using remote asset me…
Browse files Browse the repository at this point in the history
…tadata (#1417)" (#1474)

## What is this PR doing?

This reverts commit 36b126e.

## What problem is it solving?

When using browser storage and reloading Playground after an initial
load, WordPress renders as if it cannot find remote CSS assets.

![Screenshot 2024-05-31 at 1 16
58 AM](https://github.com/WordPress/wordpress-playground/assets/530877/50517355-9e80-4379-8593-00ef48a218a9)

The issue should be something small, but I am reverting until we can fix
it.
  • Loading branch information
brandonpayton authored May 30, 2024
1 parent 36b126e commit b35b882
Show file tree
Hide file tree
Showing 47 changed files with 165 additions and 361 deletions.
118 changes: 11 additions & 107 deletions packages/php-wasm/node/src/test/php-request-handler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,27 +39,6 @@ describe.each(SupportedPHPVersions)(
});
});

it('should execute a non-default PHP file in a directory', async () => {
php.mkdirTree('/folder');
php.writeFile(
'/folder/some.php',
`<?php echo 'Some PHP file in a folder.';`
);
const response = await handler.request({
url: '/folder/some.php',
});
expect(response).toEqual({
httpStatusCode: 200,
headers: {
'content-type': ['text/html; charset=UTF-8'],
'x-powered-by': [expect.any(String)],
},
bytes: new TextEncoder().encode('Some PHP file in a folder.'),
errors: '',
exitCode: 0,
});
});

it('should serve a static file', async () => {
php.writeFile('/index.html', `Hello World`);
const response = await handler.request({
Expand Down Expand Up @@ -123,8 +102,7 @@ describe.each(SupportedPHPVersions)(
});
});

it('should yield x-file-type=static when a static file is not found and is listed as a remote asset', async () => {
handler.addRemoteAssetPaths(['index.html']);
it('should yield x-file-type=static when a static file is not found', async () => {
const response = await handler.request({
url: '/index.html',
});
Expand All @@ -139,19 +117,6 @@ describe.each(SupportedPHPVersions)(
});
});

it('should not yield x-file-type=static when a static file is not found and is not listed as a remote asset', async () => {
const response = await handler.request({
url: '/index.html',
});
expect(response).toEqual({
httpStatusCode: 404,
headers: {},
bytes: expect.any(Uint8Array),
errors: '',
exitCode: 0,
});
});

it('should not yield x-file-type=static when a PHP file is not found', async () => {
const response = await handler.request({
url: '/index.php',
Expand All @@ -165,77 +130,6 @@ describe.each(SupportedPHPVersions)(
});
});

it('should redirect to add trailing slash to existing dir', async () => {
php.mkdirTree('/folder');
const response = await handler.request({
url: '/folder',
});
expect(response).toEqual({
httpStatusCode: 301,
headers: {
Location: ['/folder/'],
},
bytes: expect.any(Uint8Array),
errors: '',
exitCode: 0,
});
});

it('should return 200 and pass query strings when a valid request is made to a folder', async () => {
php.mkdirTree('/folder');
php.writeFile('/folder/index.php', `<?php echo $_GET['key'];`);
const response = await handler.request({
url: '/folder/?key=value',
});
expect(response.httpStatusCode).toEqual(200);
expect(response.text).toEqual('value');
});

it('should default a folder request to index.html if it exists and index.php does not', async () => {
php.mkdirTree('/folder');
php.writeFile('/folder/index.html', `INDEX DOT HTML`);
const response = await handler.request({
url: '/folder/?key=value',
});
expect(response.httpStatusCode).toEqual(200);
expect(response.text).toEqual('INDEX DOT HTML');
});

it('should default a folder request to index.php when when both index.php and index.html exist', async () => {
php.mkdirTree('/folder');
php.writeFile('/folder/index.php', `INDEX DOT PHP`);
php.writeFile('/folder/index.html', `INDEX DOT HTML`);
const response = await handler.request({
url: '/folder/?key=value',
});
expect(response.httpStatusCode).toEqual(200);
expect(response.text).toEqual('INDEX DOT PHP');
});

it('should delegate request for non-existent PHP file to /index.php with query args', async () => {
php.writeFile(
'/index.php',
`<?php echo "DEFAULT with key={$_GET['key']}";`
);
const response = await handler.request({
url: '/non/existent/file.php?key=value',
});
expect(response.httpStatusCode).toEqual(200);
expect(response.text).toEqual('DEFAULT with key=value');
});

it('should delegate request for non-existent non-PHP file to /index.php with query args', async () => {
php.writeFile(
'/index.php',
`<?php echo "DEFAULT with key={$_GET['key']}";`
);
const response = await handler.request({
url: '/non/existent/file?key=value',
});
expect(response.httpStatusCode).toEqual(200);
expect(response.text).toEqual('DEFAULT with key=value');
});

it('should return httpStatus 500 if exit code is not 0', async () => {
php.writeFile(
'/index.php',
Expand Down Expand Up @@ -407,6 +301,16 @@ describe.each(SupportedPHPVersions)(
expect(response.httpStatusCode).toEqual(200);
expect(response.text).toEqual('value');
});

it('should return 200 and pass query strings when a valid request is made to a folder', async () => {
php.mkdirTree('/folder');
php.writeFile('/folder/index.php', `<?php echo $_GET['key'];`);
const response = await handler.request({
url: '/folder/?key=value',
});
expect(response.httpStatusCode).toEqual(200);
expect(response.text).toEqual('value');
});
}
);

Expand Down
10 changes: 0 additions & 10 deletions packages/php-wasm/node/src/test/php.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -934,16 +934,6 @@ describe.each(SupportedPHPVersions)('PHP %s', (phpVersion) => {
expect(php.isDir(testFilePath)).toEqual(false);
});

it('isFile() should correctly distinguish between a file and a directory', () => {
php.writeFile(testFilePath, 'Hello World!');
expect(php.fileExists(testFilePath)).toEqual(true);
expect(php.isFile(testFilePath)).toEqual(true);

php.mkdir(testDirPath);
expect(php.fileExists(testDirPath)).toEqual(true);
expect(php.isFile(testDirPath)).toEqual(false);
});

it('listFiles() should return a list of files in a directory', () => {
php.mkdir(testDirPath);
php.writeFile(testDirPath + '/test.txt', 'Hello World!');
Expand Down
144 changes: 47 additions & 97 deletions packages/php-wasm/universal/src/lib/php-request-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ export class PHPRequestHandler {
#PATHNAME: string;
#ABSOLUTE_URL: string;
#cookieStore: HttpCookieStore;
#remoteAssetPaths: Set<string>;
rewriteRules: RewriteRule[];
processManager: PHPProcessManager;

Expand Down Expand Up @@ -195,8 +194,6 @@ export class PHPRequestHandler {
this.#PATHNAME,
].join('');
this.rewriteRules = rewriteRules;

this.#remoteAssetPaths = new Set<string>();
}

async getPrimaryPhp() {
Expand Down Expand Up @@ -309,88 +306,14 @@ export class PHPRequestHandler {
),
this.rewriteRules
);

const primaryPhp = await this.getPrimaryPhp();

let fsPath = joinPaths(this.#DOCROOT, normalizedRequestedPath);

if (primaryPhp.isDir(fsPath)) {
// Ensure directory URIs have a trailing slash. Otherwise,
// relative URIs in index.php or index.html files are relative
// to the next directory up.
//
// Example:
// For a request to "/wp-admin", the relative link "edit.php"
// resolves to "/edit.php" rather than "/wp-admin/edit.php".
//
// This is correct behavior for the browser:
// https://www.rfc-editor.org/rfc/rfc3986#section-5.2.3
//
// But the intent for `/wp-admin/index.php` is that its relative
// URIs are relative to `/wp-admin/`.
//
// In fact, WordPress also redirects like this when given a chance.
// - https://github.com/WordPress/wordpress-develop/blob/b6a3b9c7d1ce33cbeca6f95871a26c48141e524b/src/wp-includes/canonical.php#L696
// - https://github.com/WordPress/wordpress-develop/blob/b6a3b9c7d1ce33cbeca6f95871a26c48141e524b/src/wp-includes/canonical.php#L1036-L1045
// - https://github.com/WordPress/wordpress-develop/blob/b6a3b9c7d1ce33cbeca6f95871a26c48141e524b/src/wp-includes/link-template.php#L3558
if (!fsPath.endsWith('/')) {
return new PHPResponse(
301,
{ Location: [`${requestedUrl.pathname}/`] },
new Uint8Array(0)
);
}

// We can only satisfy requests for directories with a default file
// so let's first resolve to a default path when available.
const defaultFilePath = ['index.php', 'index.html']
.map((defaultFilename) => joinPaths(fsPath, defaultFilename))
.find((possibleDefaultPath) =>
primaryPhp.isFile(possibleDefaultPath)
);

if (defaultFilePath) {
fsPath = defaultFilePath;
}
}

if (fsPath.endsWith('.php')) {
if (primaryPhp.isFile(fsPath)) {
const effectiveRequest: PHPRequest = {
...request,
url: joinPaths(this.#ABSOLUTE_URL, fsPath),
};
return this.#spawnPHPAndDispatchRequest(
effectiveRequest,
requestedUrl
);
}
} else {
if (primaryPhp.isFile(fsPath)) {
return this.#serveStaticFile(primaryPhp, fsPath);
} else if (
// Make sure fsPath doesn't describe any other entity on the filesystem
!primaryPhp.fileExists(fsPath) &&
this.#remoteAssetPaths.has(fsPath)
) {
// This path is listed as a remote asset. Mark it as a static file
// so the service worker knows it can issue a real fetch() to the server.
return new PHPResponse(
404,
{ 'x-file-type': ['static'] },
new TextEncoder().encode('404 File not found')
);
}
const fsPath = joinPaths(this.#DOCROOT, normalizedRequestedPath);
if (!seemsLikeAPHPRequestHandlerPath(fsPath)) {
return this.#serveStaticFile(
await this.processManager.getPrimaryPhp(),
fsPath
);
}

// Delegate unresolved requests to WordPress. This makes WP magic possible,
// like pretty permalinks and dynamically generated sitemaps.
const wpDefaultPath = joinPaths(this.#DOCROOT, 'index.php');
const effectiveRequest: PHPRequest = {
...request,
url: joinPaths(this.#ABSOLUTE_URL, wpDefaultPath),
};
return this.#spawnPHPAndDispatchRequest(effectiveRequest, requestedUrl);
return this.#spawnPHPAndDispatchRequest(request, requestedUrl);
}

/**
Expand All @@ -400,6 +323,17 @@ export class PHPRequestHandler {
* @returns The response.
*/
#serveStaticFile(php: PHP, fsPath: string): PHPResponse {
if (!php.fileExists(fsPath)) {
return new PHPResponse(
404,
// Let the service worker know that no static file was found
// and that it's okay to issue a real fetch() to the server.
{
'x-file-type': ['static'],
},
new TextEncoder().encode('404 File not found')
);
}
const arrayBuffer = php.readFileAsBuffer(fsPath);
return new PHPResponse(
200,
Expand Down Expand Up @@ -552,19 +486,6 @@ export class PHPRequestHandler {
}
throw new Error(`File not found: ${resolvedFsPath}`);
}

/**
* Add paths to static files we can assume exist remotely.
*
* @param relativePaths A list of paths to remote assets, relative to the document root.
*/
addRemoteAssetPaths(relativePaths: string[]) {
const separator = this.#DOCROOT.endsWith('/') ? '' : '/';
relativePaths.forEach((relativePath) => {
const fsPath = `${this.#DOCROOT}${separator}${relativePath}`;
this.#remoteAssetPaths.add(fsPath);
});
}
}

/**
Expand All @@ -582,6 +503,35 @@ function inferMimeType(path: string): string {
return mimeTypes[extension] || mimeTypes['_default'];
}

/**
* Guesses whether the given path looks like a PHP file.
*
* @example
* ```js
* seemsLikeAPHPRequestHandlerPath('/index.php') // true
* seemsLikeAPHPRequestHandlerPath('/index.php') // true
* seemsLikeAPHPRequestHandlerPath('/index.php/foo/bar') // true
* seemsLikeAPHPRequestHandlerPath('/index.html') // false
* seemsLikeAPHPRequestHandlerPath('/index.html/foo/bar') // false
* seemsLikeAPHPRequestHandlerPath('/') // true
* ```
*
* @param path The path to check.
* @returns Whether the path seems like a PHP server path.
*/
export function seemsLikeAPHPRequestHandlerPath(path: string): boolean {
return seemsLikeAPHPFile(path) || seemsLikeADirectoryRoot(path);
}

function seemsLikeAPHPFile(path: string) {
return path.endsWith('.php') || path.includes('.php/');
}

function seemsLikeADirectoryRoot(path: string) {
const lastSegment = path.split('/').pop();
return !lastSegment!.includes('.');
}

/**
* Applies the given rewrite rules to the given path.
*
Expand Down
5 changes: 0 additions & 5 deletions packages/php-wasm/universal/src/lib/php-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,6 @@ export class PHPWorker implements LimitedPHPApi {
return _private.get(this)!.php!.isDir(path);
}

/** @inheritDoc @php-wasm/universal!/PHP.isFile */
isFile(path: string): boolean {
return _private.get(this)!.php!.isFile(path);
}

/** @inheritDoc @php-wasm/universal!/PHP.fileExists */
fileExists(path: string): boolean {
return _private.get(this)!.php!.fileExists(path);
Expand Down
Loading

0 comments on commit b35b882

Please sign in to comment.