-
Notifications
You must be signed in to change notification settings - Fork 353
[ php-wasm ] Add intl dynamic extension to @php-wasm/web
#2591
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
ce5ac2f to
9915b90
Compare
Little summary :
- I first retried to build PHP and Intl with
MAIN_MODULEfor Web. It was a success.
-
I then remembered something happened when
MAIN_MODULEwas enabled [PHP: Do not pull WebGL in Playground web #2318 ]. I first tried to reproduce thedocument is not definederror and I easily did that by runningnpm run dev. -
I then tried to set
MAIN_MODULEandSIDE_MODULEequal 2 instead of the default 1. It failed. Each file compiled successfully but I always got the same error :
PHP Startup: Invalid library (maybe not a PHP library) '/internal/shared/extensions/intl.so'
I finally found out the resulting intl.so file when compiling with SIDE_MODULE=1 was 5.6 Mb while SIDE_MODULE=2 was 78 bytes. I understood the MAIN_MODULE was then responsible for keeping the resource. I supposed this was not the solution.
- I came back with
MAIN_MODULE=1and tried to fix thedocument is not definedissue. Which I did first by replacing the problematic code :
- var specialHTMLTargets = [0, document, window]; + var specialHTMLTargets = [0, typeof document != 'undefined' ? document : 0, typeof window != 'undefined' ? window : 0]; /** @suppress {duplicate } */ var findEventTarget = (target) => { target = maybeCStringToJsString(target); - var domElement = specialHTMLTargets[target] || document.querySelector(target); + var domElement = specialHTMLTargets[target] || (typeof document != 'undefined' ? document.querySelector(target) : null); return domElement; };
And running npm run dev didn't crash.
- I kept looking for
specialHTMLTargetsinemscriptenrepository and I found these lines inlibhtml5.js:
#if ENVIRONMENT_MAY_BE_WORKER || ENVIRONMENT_MAY_BE_NODE || ENVIRONMENT_MAY_BE_SHELL || PTHREADS
$specialHTMLTargets: "[0, typeof document != 'undefined' ? document : 0, typeof window != 'undefined' ? window : 0]",
#else
$specialHTMLTargets: "[0, document, window]",
#endif
settings.ENVIRONMENT_MAY_BE_WORKER = not settings.ENVIRONMENT or 'worker' in settings.ENVIRONMENT
So I added worker to the ENVIRONMENT variable and it successfully ran npm run dev again.
@adamziel WDYT? Is this the right approach or do you think I should investigate MAIN_MODULE=2 further?
I had in mind to also try to add php-wasm-web tests using vitest in jsdom environment and use a vitest.setup.ts file that will emulate fetch with fs.
So I added worker to the ENVIRONMENT variable and it successfully ran npm run dev again.
Good find! It should be fine as long as we're not breaking loading that script on a regular web page (not in a worker). And if we are breaking it, that may still be fine, but let's acknowledge that and discuss any consequences.
I had in mind to also try to add php-wasm-web tests using vitest in jsdom environment and use a vitest.setup.ts file that will emulate fetch with fs.
Let's just use a specific E2E testing setup. I've tried that in the past in this repo and jsdom was notoriously failing to simulate the browser environment or catch any real errors.
c511369 to
52fe96a
Compare
f244496 to
f54d3c5
Compare
Good find! It should be fine as long as we're not breaking loading that script on a regular web page (not in a worker). And if we are breaking it, that may still be fine, but let's acknowledge that and discuss any consequences.
the environment is now web,worker instead of only web so I suppose the previous behavior for web pages could be fused with the worker behavior?
Let's just use a specific E2E testing setup. I've tried that in the past in this repo and jsdom was notoriously failing to simulate the browser environment or catch any real errors.
I implemented a php-wasm-web:e2e nx command based on the playground-website:e2e one and it runs with success in CI! We could, in another pull request, maybe duplicate the files from Node to Web by replacing everything related to Vitest by Cypress?
I also managed to run the tests in JSPI and Asyncify separately by using chrome for JSPI and electron for Asyncify. I guess we can also run the tests in Asyncify mode with chrome via a different setup. On it.
I ended up only removing the close function in node because it made the last test crash only for node versions :
if [ "$PLATFORM" = "node" ]; then \
# Calling close() on a file descriptor acquired through WASI syscalls can trigger a JS call/await
# during a non-resumable C++ stack frame, leading to "RuntimeError: trying to suspend JS frames".
# Since ICU maps the file into memory and does not require the descriptor after mapping
# under our build context, skipping close(fd) avoids that suspension error.
# NOTE: This means the file descriptor will remain open until process teardown.
# This is acceptable here because ICU data files are loaded only once at startup.
/root/replace.sh 's/close\(fd\);//' /root/icu/source/common/umapfile.cpp; \
fi; \
I tried to add the close method in JSPI_IMPORTS and JSPI_EXPORTS but the close function comes from ICU code, not PHP's Intl extension. Like an extension in an extension.
@adamziel That's it I think! The first full dynamic extension with its associated tests in Node, Web and Playground.
I will clean up the old artifacts from static Intl and Playground CLI in the next pull request, to keep this one clean.
Should I leave withICU option in php-wasm-web load-runtime for backwards compatibility?
I had to upgrade playwright version from 1.47.1 to 1.53.2. The new version includes JSPI supported Chrome version 137. Unfortunately the test-e2e-playwright fail now. I guess I'll need to update thes tests now.
The error says:
4539 pixels (ratio 0.01 of all image pixels) are different.
This is the diff:
website-old-diffI think we can just increase the threshold. It doesn't seem like an actual failure. It's weird it would happen in this PR of all 🤷
🎉
I would expect these .wasm binaries to get smaller, but they're significantly larger. Do we know what happened there? Is there a way we can ship this PR without frontloading additional 8MB of data on playground.wordpress.net?
I guess this is because of MAIN_MODULE. But you're right, 8Mb is significantly higher than expected.
What does it add to the binary? Are those additions relevant? It worked without them earlier on and it was smaller despite shipping an additional php extension. Can we post process the wasm binary and remove the extra stuff? Or get emscripten to not include it in the first place?
I'm currently listing the different steps and possibilities. I already tried MAIN_MODULE=2 and one of the tests passed. This doesn't help us a lot but 2.6Mb is better than nothing.
packages/php-wasm/web/public/php/jspi/8_3_25/php_8_3.wasm | Bin 24607385 -> 22006934 bytes packages/php-wasm/web/public/php/jspi/php_8_3.js | Bin 577829 -> 161805 bytes
I'm still investigating.
|
Ok. I found something interesting. First of all, why does
Based on this, I found two ways to decrease the wasm file size while adding the
But to be honest, these two options were not satisfying enough. I decided to make some kind of sizes benchmark of the wasm binary and its composition : with and without I hope this will be readable. [ n.b. GD needs LIBZIP, CURL needs OPENSSL and LIBZIP and OPENSSL needs MBSTRING and MBREGEX to build individually ]
Now using
Now a comparison between the increase based on the individual static extension :
Now the overall additionnal Mb from all the extensions enabled equals Why is So, I don't know if this is quite possible right now but I would like to suggest to transform Summary : Current PHP.Wasm web without That sounds promising right ? |
It does sound promising, thank you for this great research!
So, I don't know if this is quite possible right now but I would like to suggest to transform FILEINFO into a dynamic extension. This will probably free up to 8Mb while adding 5 Mb due to MAIN_MODULE.
The way I understand it, is we only need the system libraries that are already shipped before this PR. Whatever makes the additional FILEINFO-related 8MB or MAIN_MODULE-related 5Mb can probably be slashed.
It sounds like these are additional libraries loaded just in case some dynamic library tries to load them later on – but we know there is no dynamic library that will need that later on because we're just splitting php.wasm into php.wasm + intl.so. The current system libraries are enough today so they should also be enough for the dynamic library
Can we inspect the built wasm file for the functions it ships, diff that with the wasm file we have today, and just blanket remove all the additional functions?
- During the meet-up, @adamziel and I discussed about this pull request and what was exactly adding these
+8Mbof additional data. The first element was : Because ofMAIN_MODULE, the data file needed byfileinfo, normally available outside of the wasm file had to be added inside the wasm file. To correct that, I emptied the array with these two lines :
# Remove fileinfo if needed RUN if [ "$WITH_FILEINFO" = "yes" ]; \ then \ echo -n ' --enable-fileinfo' >> /root/.php-configure-flags; \ + rm /root/php-src/ext/fileinfo/data_file.c; \ + echo -e 'const unsigned char php_magic_database[0] = {};' > /root/php-src/ext/fileinfo/data_file.c; \ else \ # light bundle should compile without fileinfo and libmagic echo -n ' --disable-fileinfo' >> /root/.php-configure-flags; \ fi;
But, as you may understand the issue, even if fileinfo compiles correctly, it won't work anymore since it needs this huge php_magic_database variable. So, I would like to suggest making fileinfo a dynamic extension instead, before continuing to implement this pull request.
- The second element was due to
MAIN_MODULE=1setting a ton of exported functions instead of the ones we only needed. I then achieved to build every version of PHP withMAIN_MODULE=2requiring each version to have its own set of specificEXPORTED_FUNCTIONSin order to make theintldynamic extension tests pass. Here is the list :
GLOBAL TO ALL PHP VERSIONS :
"_zend_string_init_interned", \n\ "___cxa_pure_virtual", \n\ "_executor_globals", \n\ "_std_object_handlers", \n\ "_zend_empty_string", \n\ "_timezone", \n\ "_tzname", \n\ "_zend_ce_aggregate", \n\ "__ZTVN10__cxxabiv120__si_class_type_infoE", \n\ "__ZTVN10__cxxabiv117__class_type_infoE", \n\ "__ZTVN10__cxxabiv121__vmi_class_type_infoE", \n\ "_zend_ce_exception", \n\ "_OnUpdateStringUnempty", \n\ "_OnUpdateLong", \n\ "_OnUpdateBool", \n\ "_zend_ini_boolean_displayer_cb", \n\ "_zend_ce_countable", \n\ "_zend_ce_iterator", \n\ "__ZNSt12length_errorD1Ev", \n\ "__ZTISt12length_error", \n\ "__ZTVSt12length_error", \n\ "__ZNSt20bad_array_new_lengthD1Ev", \n\ "__ZTISt20bad_array_new_length", \n\ "_zval_add_ref", \n\ "_free", \n\ "_object_init_ex", \n\ "__emalloc", \n\ "_object_properties_init", \n\ "_strlen", \n\ "_strstr", \n\ "_strchr", \n\ "__ZNSt3__211__call_onceERVmPvPFvS2_E", \n\ "__ZNSt3__25mutex4lockEv", \n\ "__ZNSt3__25mutex6unlockEv", \n\ "__ZNSt3__218condition_variable10notify_allEv", \n\ "_strcmp", \n\ "_strcpy", \n\ "_strncmp", \n\ "_strrchr", \n\ "_strncpy", \n\ "_getenv", \n\ "_setlocale", \n\ "_stat", \n\ "_open", \n\ "_mmap", \n\ "_close", \n\ "_memcmp", \n\ "_realloc", \n\ "_convert_to_double", \n\ "__safe_emalloc", \n\ "__efree", \n\ "_convert_to_long", \n\ "_zend_known_strings", \n\ "_zend_empty_array", \n\ "_compiler_globals", \n\ "_zend_add_attribute", \n\ "_zend_register_ini_entries", \n\ "_zend_register_ini_entries_ex", \n\ "_zend_declare_class_constant_long", \n\ "_zend_declare_class_constant_null", \n\ "_zend_declare_class_constant_string", \n\ "_zend_register_long_constant", \n\ "_zend_declare_class_constant_double", \n\ "_zend_register_internal_class_with_flags", \n\ "_zend_declare_typed_class_constant", \n\ "_zend_register_string_constant", \n\ "_zend_register_internal_class_ex", \n\ "_zend_unregister_ini_entries_ex", \n\ "_zend_throw_exception_ex", \n\ "_zend_strpprintf", \n\ "_zend_object_std_dtor", \n\ "_zend_object_std_init", \n\ "_zend_declare_class_constant_ex", \n\ "_zend_parse_method_parameters", \n\ "_zend_throw_error", \n\ "_zend_sort", \n\ "_zend_hash_sort_ex", \n\ "__zend_new_array_0", \n\ "_zend_hash_next_index_insert", \n\ "_zend_hash_update", \n\ "_zend_hash_index_update", \n\ "_zend_error", \n\ "_zend_parse_parameters", \n\ "_zend_replace_error_handling", \n\ "_zend_spprintf", \n\ "_zend_throw_exception", \n\ "_zend_restore_error_handling", \n\ "_zend_strtod", \n\ "_zend_wrong_parameters_none_error", \n\ "_zend_try_assign_typed_ref_long", \n\ "_zend_fcall_info_init", \n\ "_zend_array_destroy", \n\ "_zend_argument_value_error", \n\ "_zend_hash_str_find", \n\ "_zend_objects_clone_members", \n\ "_zend_call_function", \n\ "_zend_try_assign_typed_ref_str", \n\ "___zend_malloc", \n\ "_zend_alter_ini_entry", \n\ "_zend_argument_type_error", \n\ "_zend_hash_destroy", \n\ "_zend_memnstr_ex", \n\ "_zend_str_tolower", \n\ "_zend_create_internal_iterator_zval", \n\ "_zend_class_implements", \n\ "_zend_iterator_init", \n\ "_zend_update_property", \n\ "_zend_declare_typed_property", \n\ "_zend_wrong_parameters_count_error", \n\ "_zend_wrong_parameter_error", \n\ "_zend_parse_arg_str_slow", \n\ "_zend_parse_arg_long_slow", \n\ "_zend_parse_arg_str_or_long_slow", \n\ "_zend_release_fcall_info_cache", \n\ "_zend_try_assign_typed_ref_arr", \n\ "___zend_realloc", \n\ "_zend_objects_store_del", \n\ "_zend_get_gc_buffer_create", \n\ "_zend_get_gc_buffer_grow", \n\ "_zend_std_get_properties", \n\ "_zend_hash_index_find", \n\ "__zend_hash_init", \n\ "_zend_hash_str_update", \n\ "_zend_call_known_function", \n\ "_zend_std_compare_objects", \n\ "_zend_try_assign_typed_ref_bool", \n\ "_zend_hash_copy", \n\ "__zend_new_array", \n\ "_zend_argument_error", \n\ "_zend_argument_count_error", \n\ "_zend_call_method", \n\
ONLY PHP8.4 :
"_zend_register_internal_class_with_flags", \n\ "_zend_declare_typed_class_constant", \n\
ABOVE PHP8.0 :
"_zend_unregister_ini_entries_ex", \n\ "_zend_register_ini_entries_ex", \n\
ABOVE PHP7.4 :
"_zend_add_attribute", \n\ "_zend_argument_count_error", \n\ "_zend_argument_error", \n\ "_zend_argument_type_error", \n\ "_zend_argument_value_error", \n\ "_zend_call_known_function", \n\ "_zend_create_internal_iterator_zval", \n\ "_zend_get_gc_buffer_create", \n\ "_zend_get_gc_buffer_grow", \n\ "_zend_parse_arg_str_or_long_slow", \n\ "_zend_wrong_parameter_error", \n\
ABOVE PHP7.3 :
"_zend_declare_typed_property", \n\ "_zend_release_fcall_info_cache", \n\ "_zend_try_assign_typed_ref_arr", \n\ "_zend_try_assign_typed_ref_bool", \n\ "_zend_try_assign_typed_ref_long", \n\ "_zend_try_assign_typed_ref_str", \n\
ABOVE PHP7.2 :
"__zend_new_array", \n\ "__zend_new_array_0", \n\ "_object_init_ex", \n\ "_zend_empty_array", \n\ "_zend_hash_index_update", \n\ "_zend_hash_next_index_insert", \n\ "_zend_hash_str_update", \n\ "_zend_hash_update", \n\ "_zend_std_compare_objects", \n\ "_zend_string_init_interned", \n\ "_zend_wrong_parameters_none_error", \n\ "_zval_ptr_dtor", \n\
Setting MAIN_MODULE=2 makes the wasm file 2Mb lighter because of this exported functions cleanup. But it inconveniently breaks every time it needs an exported function that was not added into the list.
To be sure we have the correct amount of exported functions for intl, we should run intl tests with every PHP.wasm versions.
I would like to suggest doing this optimization in a next pull request. Since this will probably need a certain amount of time to implement all those necessary exported functions for each PHP version.
As explained above, removing fileinfo from the binary and adding MAIN_MODULE shouldn't add more than 1Mb. But I can run a new benchmark if you want.
So @adamziel, in summary, what do you think of these steps before being able to merge this pull request ? :
- Making
fileinfoa dynamic extension, for Node and Web. - Keeping
MAIN_MODULE=1temporarily until we find a way to run dynamic extension tests and find the correct amount of exported functions for each PHP version with precision.
But, as you may understand the issue, even if fileinfo compiles correctly, it won't work anymore since it needs this huge php_magic_database variable. So, I would like to suggest making fileinfo a dynamic extension instead, before continuing to implement this pull request.
But why does it work today with the smaller .wasm file? We're not shipping an additional fileinfo database. Is it optimized out of the build? Or compressed? Can we do the same thing with intl as a dynamic library?
achieved to build every version of PHP with MAIN_MODULE=2 requiring each version to have its own set of specific EXPORTED_FUNCTIONS in order to make the intl dynamic extension tests pass. Here is the list :
Aha, I see the issue – we need to expose any function that the built intl dynamic library may want to call. Manually listing those exports will only get us so far. To cover 100% of possible code paths, we'll need a script that extracts the imports from the built intl.wasm bundle and confirms they're exposed from the php.wasm bundle – let's run it as a part of the build process, even if only as an assertion after everything is built.
But why does it work today with the smaller .wasm file? We're not shipping an additional fileinfo database. Is it optimized out of the build? Or compressed? Can we do the same thing with intl as a dynamic library?
I spent some time digging into why the fileinfo extension behaves differently when building php.wasm with and without MAIN_MODULE, and here’s what I found :
1.When we build php.wasm without MAIN_MODULE, the resulting file is significantly smaller (roughly 15–16MB with fileinfo) compared to the build with MAIN_MODULE (26–27MB). Yet, the fileinfo extension still works, even without shipping an external magic database.
2.The php_magic_database is a 16MB constant defined in ext/fileinfo/data_file.c and included by apprentice.c and he is responsible for that significant increase in size. In the non-MAIN_MODULE, this file is compiled directly into the binary. However, the size increase is only about 1MB instead of 8MB.
3.Through investigation, it appears that the linker and Emscripten optimizations remove most of the unused data. Specifically : Non-MAIN_MODULE builds are "static," meaning the binary doesn’t need to export symbols globally.
-
The linker notices that only a small portion of
php_magic_databaseis ever actually referenced, and it discards the rest. The final wasm file only contains the data actually needed for MIME detection of the types we use. -
So it’s not compression in the usual way. The unused array entries are eliminated by linker level dead data elimination. Only the bytes actually referenced by
apprentice.csurvive in the binary. -
But With
MAIN_MODULE, PHP is built as a dynamic core. All global symbols must be exported to allow dynamic extensions to reference them. Andphp_magic_databaseis a global symbol. As a result, the linker cannot discard any part ofphp_magic_database, because it might be referenced by a future extension.
Therefore, most of the 16MB constant ends up fully included in the wasm file, increasing its size by 8MB.
This explains why the MAIN_MODULE build is much larger. The "shrinking trick" only works in static builds where the linker can be sure that nothing outside the main binary needs the data.
I am stuck here on a situation where it would be super useful to have that shrinking happen without having the possibility to use it. I tried a lot of things and by modifying the apprentice.c file it mostly made the trick, the wasm file was reduced but fileinfo returned errors on some mimetypes. I should probably dig into this deeper.
Aha, I see the issue – we need to expose any function that the built intl dynamic library may want to call. Manually listing those exports will only get us so far. To cover 100% of possible code paths, we'll need a script that extracts the imports from the built intl.wasm bundle and confirms they're exposed from the php.wasm bundle – let's run it as a part of the build process, even if only as an assertion after everything is built.
This is mostly right! But some missing functions make the test crash too. I tested that with the PHP8.3 build and almost every function I listed above are present except these ones :
zend_empty_array
zend_add_attribute
zend_new_interned_string
zend_parse_parameters_ex
zend_ce_traversable
zend_declare_property_null
zend_objects_destroy_object
zend_declare_class_constant_double
zend_declare_class_constant_string
zend_declare_class_constant_null
zend_declare_class_constant_long
zend_register_ini_entries
compiler_globals
zend_known_strings
Everytime I ran the Intl tests, it crashed because of one of the above missing function, I had to compile again after adding the missing function and so on until the test passed. So in theory you're correct and the files can be analyzed to list the needed EXPORTED_FUNCTIONS but in practice some errors still occur even with that new process, probably based on missing exported functions needed by the MAIN_MODULE.
FYI, there are 769 imports needed from the intl.so file.
let's run it as a part of the build process, even if only as an assertion after everything is built.
I am not sure to understand what you're meaning with "only as an assertion". Did you mean, at the end of the intl build I print the list of import functions needed, for information?
I tried multiple options :
-
Rewrite the
apprentice.cfile to enable shrinking [ by setting thephp_magic_databaseconstant exclusive to that file
-> It resulted to an emptyphp_magic_databasevariable. -
I tried to rebuild a
data_file.cfile based on the hex dumps in the php.wasm withoutMAIN_MODULEbuild
-> Manually replacing hex failed because Emscripten relies on pointer offsets and relocations. Obviously. -
I tried to find or build a light weight
magic.mgcfile [ base file to create thedata_file.c]
-> I couldn't find one and creating one with arbitrary extensions is probably not a good idea. -
I tried to find a way to build
fileinfoseparately and inject it into PHP.wasm withMAIN_MODULE.
-> This is basically like creating aSIDE_MODULEso I should wait for your opinion on this. -
I looked for another way to find and use a
magic.mgcfile, but I didn't find a way to access one from the browser.
I came to the frustrating conclusion that it we had two last options :
- Building PHP.wasm with
MAIN_MODULEis forbidden in PHP.wasm Web - Setting up
fileinfoas aSIDE_MODULE[ And I am not sure the shrinking will occur in a side module as well ]
I hope you'll have better ideas and options than mine...
Thank you @mho22! Let me think about that for a moment and follow up here
Could wasm-split help us here? It seems to enable doing a static build and splitting it into the main module and the dynamic library after the fact. We don't need to support arbitrary future PHP extensions but only the set of extensions we're already building so perhaps we could use it to eliminate dead code first and split afterwards. Alternatively, perhaps there's a way we can do a full static build, list all the symbols left inside by the linker, and then do the MAIN_MODULE build and post-process it to remove the symbols that weren't there in the static build?
Everytime I ran the Intl tests, it crashed because of one of the above missing function, I had to compile again after adding the missing function and so on until the test passed. So in theory you're correct and the files can be analyzed to list the needed EXPORTED_FUNCTIONS but in practice some errors still occur even with that new process, probably based on missing exported functions needed by the MAIN_MODULE.
The built dynamic library should have some imports and exports listed at the top (viewable via wasm2wat or wasm-objdump). Couldn't all the required imports be extracted from there? It would be weird if the dynamic library needed a PHP function and yet it didn't list that as an import. The list of required core PHP exports must be somewhere in the built artifact, either in the binary wasm file or the js file, because it, ultimately, tries to call those functions by name.
Basically we want the same linking/dead code elimination outcome as when only the main module it built. It seems like EMscripten won't give us that by default so we need to help it with other tools and, potentially, custom static analysis and rewriting of the generated build. If the linker can prune a large part of the php_magic_database when building without the MAIN_MODULE option, we can likely also prune it from the built binary blob (or, at least, patch emscripten to strategically eliminate dead code anyway when using MAIN_MODULE=2)
I am thoroughly studying your suggestions. I am learning a lot of things related to emscripten and webassembly. I haven't found a solution yet. I created an issue on emscripten and will go back to Xdebug waiting for an answer.
I got some news about emscripten contributor sbc100 and it looks like his pull request made the trick. I added that code below the emsdk git clone and I got the same results for MAIN_MODULE=0 and MAIN_MODULE=2 :
RUN git clone https://github.com/emscripten-core/emsdk.git && \ ./emsdk/emsdk install 4.0.5 && \ /root/emsdk/emsdk activate 4.0.5 + RUN rm -rf /root/emsdk/upstream/emscripten && \ + git clone https://github.com/sbc100/emscripten.git /root/emsdk/upstream/emscripten && \ + cd /root/emsdk/upstream/emscripten && \ + git checkout main_module_static_v2 + RUN python3 /root/emsdk/upstream/emscripten/bootstrap.py
I tried that in a test, not yet php recompilation :
FROM playground-php-wasm:base
COPY ./php/test.c /example/test.c
COPY ./php/data_file.c /example/data_file.c
RUN set -euxo pipefail; \
source /root/emsdk/emsdk_env.sh; \
emcc -o /example/test.wasm -Wl,--trace-symbol=php_magic_database -O3 -sMAIN_MODULE=2 -sTOTAL_MEMORY=200MB /example/test.c
Results for MAIN_MODULE=0
#10 [6/6] RUN ls -lha /example #10 0.115 total 22M #10 0.115 drwxr-xr-x 1 root root 4.0K Oct 25 17:55 . #10 0.115 drwxr-xr-x 1 root root 4.0K Oct 25 17:55 .. #10 0.115 -rw-r--r-- 1 root root 20M Oct 8 10:32 data_file.c #10 0.115 -rw-r--r-- 1 root root 221 Oct 24 17:03 test.c #10 0.115 -rwxr-xr-x 1 root root 1.5M Oct 25 17:55 test.wasm
Results for MAIN_MODULE=2
#10 [6/6] RUN ls -lha /example
#10 0.180 total 22M
#10 0.180 drwxr-xr-x 1 root root 4.0K Oct 25 17:57 .
#10 0.180 drwxr-xr-x 1 root root 4.0K Oct 25 17:57 ..
#10 0.180 -rw-r--r-- 1 root root 20M Oct 8 10:32 data_file.c
#10 0.180 -rw-r--r-- 1 root root 221 Oct 24 17:03 test.c
+#10 0.180 -rwxr-xr-x 1 root root 1.5M Oct 25 17:57 test.wasmFor the record, this is the result for MAIN_MODULE=2 without the PR :
#10 [6/6] RUN ls -lha /example
#10 0.092 total 30M
#10 0.092 drwxr-xr-x 1 root root 4.0K Oct 25 18:20 .
#10 0.092 drwxr-xr-x 1 root root 4.0K Oct 25 18:20 ..
#10 0.092 -rw-r--r-- 1 root root 20M Oct 8 10:32 data_file.c
#10 0.092 -rw-r--r-- 1 root root 221 Oct 24 17:03 test.c
-#10 0.092 -rwxr-xr-x 1 root root 9.9M Oct 25 18:20 test.wasmIt is now just a matter of time and stats to have that pull request merged. I will try tomorrow to compile php with that pull request and see if the results are promising.
Oh, lovely! If the build passes all our tests, we could switch to that emscripten branch until it lands in core, or even maintain our fork with that change applied. Thank you so much for opening that issue and collaborating with the Emscripten team! ❤️
Uh oh!
There was an error while loading. Please reload this page.
Motivation for the change, related issues
This is a pull request to dynamically load Intl in PHP.wasm Web.
Related issues and pull requests
Issues
Pull requests
intldynamic extension to @php-wasm/node ASYNCIFY #2501 #2557intldynamic extension to @php-wasm/node JSPI #2501intlextension #2187Implementation details
MAIN_MODULEin node and webworkerto the [web] environmentignore-lib-importsVite pluginwasm-feature-detectto simulate JSPI mode enabled based on Cypress ENVTesting Instructions (or ideally a Blueprint)
CI
🧪 test-e2e-php-wasm-web-jspi
🧪 test-e2e-php-wasm-web-asyncify
Next steps