Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/php-wasm/compile/php/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,7 @@ RUN export ASYNCIFY_IMPORTS=$'[\n\
"__vfprintf_internal",\
"vfprintf",\
"vsnprintf",\
"var_destroy",\
"__xmlRaiseError",\
"__xmlLoaderErr",\
"zim_SoapClient___construct",\
Expand Down Expand Up @@ -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",\
Expand Down Expand Up @@ -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",\
Expand All @@ -882,6 +885,7 @@ RUN export ASYNCIFY_IMPORTS=$'[\n\
"call",\
"callCollNeeded",\
"callFinaliser",\
"concat_function",\
"checkTreePage",\
"cleanup_unfinished_calls",\
"clearDatabasePage",\
Expand Down Expand Up @@ -1016,6 +1020,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",\
Expand All @@ -1042,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",\
Expand Down Expand Up @@ -1086,6 +1092,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",\
Expand Down Expand Up @@ -1393,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",\
Expand Down Expand Up @@ -1434,6 +1444,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",\
Expand Down Expand Up @@ -1825,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",\
Expand Down Expand Up @@ -1859,6 +1871,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",\
Expand Down Expand Up @@ -1946,7 +1959,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",\
Expand Down Expand Up @@ -2119,8 +2134,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",\
Expand Down
245 changes: 181 additions & 64 deletions packages/php-wasm/node/src/test/php-file-get-contents.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;
Expand Down Expand Up @@ -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(`
Expand Down Expand Up @@ -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(
Expand Down
Loading