diff --git a/packages/php-wasm/node/src/test/php.spec.ts b/packages/php-wasm/node/src/test/php.spec.ts index 9f53153ba2..902ebd438a 100644 --- a/packages/php-wasm/node/src/test/php.spec.ts +++ b/packages/php-wasm/node/src/test/php.spec.ts @@ -1,5 +1,9 @@ import { getPHPLoaderModule, NodePHP } from '..'; -import { loadPHPRuntime, SupportedPHPVersions } from '@php-wasm/universal'; +import { + loadPHPRuntime, + SupportedPHPVersions, + __private__dont__use, +} from '@php-wasm/universal'; import { existsSync, rmSync, readFileSync } from 'fs'; const testDirPath = '/__test987654321'; @@ -554,3 +558,85 @@ bar1 }); }); }); + +// @TODO Prevent crash on PHP versions 5.6, 7.2, 8.2 +describe.each(['7.0', '7.1', '7.3', '7.4', '8.0', '8.1'])( + 'PHP %s – process crash', + (phpVersion) => { + let php: NodePHP; + beforeEach(async () => { + php = await NodePHP.load(phpVersion as any); + php.setPhpIniEntry('allow_url_fopen', '1'); + vi.restoreAllMocks(); + }); + + it('Does not crash due to an unhandled Asyncify error ', async () => { + let caughtError; + try { + /** + * PHP is intentionally built without network support for __clone() + * because it's an extremely unlikely place for any network activity + * and not supporting it allows us to test the error handling here. + * + * `clone $x` will throw an asynchronous error out when attempting + * to do a network call ("unreachable" WASM instruction executed). + * This test should gracefully catch and handle that error. + * + * A failure to do so will crash the entire process + */ + await php.run({ + code: ` { + let caughtError; + try { + const spy = vi.spyOn(php[__private__dont__use], 'ccall'); + expect(spy.getMockName()).toEqual('ccall'); + spy.mockImplementation((c_func) => { + if (c_func === 'wasm_sapi_handle_request') { + throw new Error('test'); + } + }); + + await php.run({ + code: `(async (resolve, reject) => { + exitCode = await new Promise((resolve, reject) => { errorListener = (e: ErrorEvent) => { const rethrown = new Error('Rethrown'); rethrown.cause = e.error; @@ -396,25 +396,16 @@ export abstract class BasePHP implements IsomorphicLocalPHP { 'error', errorListener ); - - try { - resolve( - /** - * This is awkward, but Asyncify makes wasm_sapi_handle_request return - * Promise>. - * - * @TODO: Determine whether this is a bug in emscripten or in our code. - */ - await await this[__private__dont__use].ccall( - 'wasm_sapi_handle_request', - NUMBER, - [], - [] - ) - ); - } catch (e) { - reject(e); + const response = this[__private__dont__use].ccall( + 'wasm_sapi_handle_request', + NUMBER, + [], + [] + ); + if (response instanceof Promise) { + return response.then(resolve, reject); } + return resolve(response); }); } catch (e) { /**