From abedde3f59a12e6599036035216b5c39219c100d Mon Sep 17 00:00:00 2001 From: Utsav-Ladani <201901076@daiict.ac.in> Date: Tue, 16 Dec 2025 13:40:17 +0530 Subject: [PATCH 1/2] add missing Asyncify imports for enhanced functionality --- packages/php-wasm/compile/php/Dockerfile | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/php-wasm/compile/php/Dockerfile b/packages/php-wasm/compile/php/Dockerfile index 4e99394a3f..2fc6216895 100644 --- a/packages/php-wasm/compile/php/Dockerfile +++ b/packages/php-wasm/compile/php/Dockerfile @@ -776,6 +776,7 @@ RUN export ASYNCIFY_IMPORTS=$'[\n\ "__vfprintf_internal",\ "vfprintf",\ "vsnprintf",\ +"var_destroy",\ "__xmlRaiseError",\ "__xmlLoaderErr",\ "zim_SoapClient___construct",\ @@ -824,6 +825,7 @@ RUN export ASYNCIFY_IMPORTS=$'[\n\ "_erealloc",\ "_erealloc2",\ "_estrdup",\ +"_efree_large",\ "_get_zval_cv_lookup",\ "_is_numeric_string_ex",\ "_mysqlnd_init_ps_subsystem",\ @@ -872,6 +874,7 @@ RUN export ASYNCIFY_IMPORTS=$'[\n\ "_zval_dtor_func_for_ptr",\ "_zval_dtor_func",\ "_zval_ptr_dtor",\ +"_zval_undefined_op1",\ "add_property_resource_ex",\ "autoVacuumCommit",\ "bsearch",\ @@ -882,6 +885,7 @@ RUN export ASYNCIFY_IMPORTS=$'[\n\ "call",\ "callCollNeeded",\ "callFinaliser",\ +"concat_function",\ "checkTreePage",\ "cleanup_unfinished_calls",\ "clearDatabasePage",\ @@ -937,6 +941,11 @@ RUN export ASYNCIFY_IMPORTS=$'[\n\ "dynCall_i",\ "dynCall_ii",\ "dynCall_iidiiii",\ +"i",\ +"ii",\ +"iii",\ +"iiii",\ +"iiiii",\ "dynCall_iii",\ "dynCall_iiid",\ "dynCall_iiii",\ @@ -1016,6 +1025,7 @@ RUN export ASYNCIFY_IMPORTS=$'[\n\ "ZEND_FE_FETCH_R_SPEC_VAR_HANDLER",\ "zend_generator_resume",\ "zend_generator_iterator_rewind",\ +"zend_get_properties_for",\ "zend_fe_reset_iterator",\ "ZEND_FE_RESET_R_SPEC_VAR_HANDLER",\ "zend_user_it_get_new_iterator",\ @@ -1086,6 +1096,8 @@ RUN export ASYNCIFY_IMPORTS=$'[\n\ "zend_std_write_property",\ "ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DATA_CONST_HANDLER",\ "zend_objects_store_del",\ +"ZEND_CLONE_SPEC_CV_HANDLER",\ +"ZEND_CONCAT_SPEC_CONST_CV_HANDLER",\ "ZEND_UNSET_CV_SPEC_CV_UNUSED_HANDLER",\ "ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER",\ "ZEND_DO_FCALL_BY_NAME_SPEC_OBSERVER_HANDLER",\ @@ -1434,6 +1446,7 @@ RUN export ASYNCIFY_IMPORTS=$'[\n\ "php_var_dump",\ "php_var_serialize_intern",\ "php_var_unserialize_internal",\ +"php_var_unserialize_destroy",\ "php_verror",\ "php_wasm_init",\ "php_zend_stream_closer",\ @@ -1859,6 +1872,7 @@ RUN export ASYNCIFY_IMPORTS=$'[\n\ "zend_hash_del_key_or_index",\ "zend_hash_graceful_reverse_destroy",\ "zend_hash_reverse_apply",\ +"zend_hash_destroy",\ "ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLER",\ "ZEND_INCLUDE_OR_EVAL_SPEC_OBSERVER_HANDLER",\ "zend_include_or_eval",\ @@ -1946,7 +1960,9 @@ RUN export ASYNCIFY_IMPORTS=$'[\n\ "zend_startup",\ "zend_std_call_getter",\ "zend_std_call_user_call",\ +"zend_std_cast_object_tostring",\ "zend_std_compare_objects",\ +"zend_std_get_debug_info",\ "zend_std_has_dimension",\ "zend_std_has_property",\ "zend_std_read_dimension",\ @@ -2119,8 +2135,11 @@ RUN export ASYNCIFY_IMPORTS=$'[\n\ "zif_stream_socket_client",\ "zif_stream_socket_enable_crypto",\ "zif_system",\ +"zif_serialize",\ +"zif_unserialize",\ "zif_touch",\ "zif_unlink",\ +"zif_var_dump",\ "zif_wasm_popen",\ "zim_AppendIterator_append",\ "zim_AppendIterator_current",\ From 10a705baed84cc5c6ffa9d85cd7500246c163062 Mon Sep 17 00:00:00 2001 From: Utsav-Ladani <201901076@daiict.ac.in> Date: Thu, 18 Dec 2025 00:04:07 +0530 Subject: [PATCH 2/2] Add tests for PHP Magic Methods --- packages/php-wasm/compile/php/Dockerfile | 9 +- .../src/test/php-file-get-contents.spec.ts | 245 +++++++++++++----- 2 files changed, 185 insertions(+), 69 deletions(-) diff --git a/packages/php-wasm/compile/php/Dockerfile b/packages/php-wasm/compile/php/Dockerfile index 2fc6216895..9a02471edb 100644 --- a/packages/php-wasm/compile/php/Dockerfile +++ b/packages/php-wasm/compile/php/Dockerfile @@ -941,11 +941,6 @@ RUN export ASYNCIFY_IMPORTS=$'[\n\ "dynCall_i",\ "dynCall_ii",\ "dynCall_iidiiii",\ -"i",\ -"ii",\ -"iii",\ -"iiii",\ -"iiiii",\ "dynCall_iii",\ "dynCall_iiid",\ "dynCall_iiii",\ @@ -1052,6 +1047,7 @@ RUN export ASYNCIFY_IMPORTS=$'[\n\ "zif_array_filter",\ "zend_unclean_zval_ptr_dtor",\ "zend_call_known_instance_method_with_2_params",\ +"ZEND_CALL_TRAMPOLINE_SPEC_HANDLER",\ "ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_HANDLER",\ "zend_fetch_dimension_address_read_R",\ "_zval_dtor_func_for_ptr",\ @@ -1405,6 +1401,8 @@ RUN export ASYNCIFY_IMPORTS=$'[\n\ "php_openssl_sockop_read",\ "php_openssl_sockop_set_option",\ "php_openssl_sockop_write",\ +"php_output_op",\ +"php_output_write",\ "php_pcre_replace_func_impl",\ "php_pdo_free_statement",\ "php_pollfd_for",\ @@ -1838,6 +1836,7 @@ RUN export ASYNCIFY_IMPORTS=$'[\n\ "zend_execute",\ "zend_extension_statement_handler",\ "ZEND_EXT_STMT_SPEC_HANDLER",\ +"ZEND_ECHO_SPEC_CV_HANDLER",\ "ZEND_FE_FETCH_R_SPEC_VAR_HANDLER",\ "zend_fe_reset_iterator",\ "ZEND_FE_RESET_R_SPEC_VAR_HANDLER",\ diff --git a/packages/php-wasm/node/src/test/php-file-get-contents.spec.ts b/packages/php-wasm/node/src/test/php-file-get-contents.spec.ts index f51711dbae..e2bcfbcc5b 100644 --- a/packages/php-wasm/node/src/test/php-file-get-contents.spec.ts +++ b/packages/php-wasm/node/src/test/php-file-get-contents.spec.ts @@ -46,24 +46,29 @@ const httpsServer = https.createServer( requestHandler ); -[ - { - protocol: 'http', - port: new Promise((resolve) => { - httpServer.listen(0, function () { - resolve((httpServer.address() as any).port); +const startServerAndGetConfig = async ( + protocol: string, + server: http.Server | https.Server +): Promise<{ + protocol: string; + port: number; +}> => { + return new Promise((resolve) => { + server.listen(0, () => { + resolve({ + protocol, + port: (server.address() as any).port, }); - }), - }, - { - protocol: 'https', - port: new Promise((resolve) => { - httpsServer.listen(0, function () { - resolve((httpsServer.address() as any).port); - }); - }), - }, -].forEach(({ protocol, port }) => { + }); + }); +}; + +const serverConfigs = await Promise.all([ + startServerAndGetConfig('http', httpServer), + startServerAndGetConfig('https', httpsServer), +]); + +serverConfigs.forEach(({ protocol, port }) => { const host = '127.0.0.1'; const httpUrl = `${protocol}://${host}:${port}`; @@ -177,53 +182,6 @@ const httpsServer = https.createServer( function my_method() { ${networkCall} } } call_user_func_array([new Top(), 'my_method'], []); - `)); - test('Constructor', () => - assertNoCrash(` - class Top { - function __construct() { ${networkCall} } - } - new Top(); - `)); - test('Destructor', () => - assertNoCrash(` - class Top { - function __destruct() { ${networkCall} } - } - $x = new Top(); - unset($x); - `)); - test('__call', () => - assertNoCrash(` - class Top { - function __call($method, $args) { ${networkCall} } - } - $x = new Top(); - $x->test(); - `)); - test('__get', () => - assertNoCrash(` - class Top { - function __get($prop) { ${networkCall} } - } - $x = new Top(); - $x->test; - `)); - test('__set', () => - assertNoCrash(` - class Top { - function __set($prop, $value) { ${networkCall} } - } - $x = new Top(); - $x->test = 1; - `)); - test('__isset', () => - assertNoCrash(` - class Top { - function __isset($prop) { ${networkCall} } - } - $x = new Top(); - isset($x->test); `)); test('ArrayAccess', () => { assertNoCrash(` @@ -278,6 +236,165 @@ const httpsServer = https.createServer( `)); }); + /** + * Verifies that PHP magic methods can invoke asynchronous functions such as file_get_contents, + * correctly handle their results, and ensure proper stack management and context switching. + * + * @see https://www.php.net/manual/en/language.oop5.magic.php + */ + describe('PHP Magic Methods', () => { + it('__construct', () => { + return assertNoCrash(` + class Top { + function __construct() { ${networkCall} } + } + new Top();`); + }); + + it('__destruct', () => { + return assertNoCrash(` + class Top { + function __destruct() { ${networkCall} } + } + $x = new Top();`); + }); + + it('__call', () => { + return assertNoCrash(` + class Top { + function __call($method, $args) { ${networkCall} } + } + $x = new Top(); + $x->test();`); + }); + + it('__callStatic', () => { + return assertNoCrash(` + class Top { + static function __callStatic($method, $args) { ${networkCall} } + } + Top::test();`); + }); + + it('__get', () => { + return assertNoCrash(` + class Top { + function __get($prop) { ${networkCall} } + } + $x = new Top(); + $a = $x->test;`); + }); + + it('__set', () => { + return assertNoCrash(` + class Top { + function __set($prop, $value) { ${networkCall} } + } + $x = new Top(); + $x->test = 1;`); + }); + + it('__isset', () => { + return assertNoCrash(` + class Top { + function __isset($prop) { ${networkCall} } + } + $x = new Top(); + isset($x->test); + empty($x->test);`); + }); + + it('__unset', () => { + return assertNoCrash(` + class Top { + function __unset($prop) { ${networkCall} } + } + $x = new Top(); + unset($x->test);`); + }); + + it('__sleep', () => { + return assertNoCrash(` + class Top { + function __sleep() { ${networkCall} return []; } + } + $x = new Top(); + serialize($x);`); + }); + + it('__wakeup', () => { + return assertNoCrash(` + class Top { + function __wakeup() { ${networkCall} } + } + $serialized = serialize(new Top()); + unserialize($serialized);`); + }); + + it('__serialize', () => { + return assertNoCrash(` + class Top { + function __serialize() { ${networkCall} return []; } + } + $x = new Top(); + serialize($x);`); + }); + + it('__unserialize', () => { + return assertNoCrash(` + class Top { + function __unserialize($data) { ${networkCall} } + } + $serialized = serialize(new Top()); + unserialize($serialized);`); + }); + + it('__toString', () => { + return assertNoCrash(` + class Top { + function __toString() { ${networkCall} return ""; } + } + $x = new Top(); + echo $x;`); + }); + + it('__invoke', () => { + return assertNoCrash(` + class Top { + function __invoke() { ${networkCall} } + } + $x = new Top(); + $x();`); + }); + + it('__set_state', () => { + return assertNoCrash(` + class Top { + static function __set_state($an_array) { ${networkCall} } + } + $exported = var_export(new Top(), true); + eval('$x = ' . $exported . ';');`); + }); + + it('__clone', () => { + return assertNoCrash(` + class Top { + function __clone() { ${networkCall} } + } + $x = new Top(); + $y = clone $x;`); + }); + + it('__debugInfo', () => { + return assertNoCrash(` + class Top { + function __debugInfo() { ${networkCall} return []; } + } + $x = new Top(); + var_dump($x);`); + }); + }); + describe('exif extension support', () => { it('exif_read_data', async () => { assertNoCrash(