Open eliot-akira opened 5 months ago
It could be an Asyncify issue, but it could also be dispatching another PHP Request before the previous one finished running – I can see php_request_startup
is later in the call stack than zend_execute
.
It's nice how detailed the error stack trace is, with helpful clues to what happened.
I suppose it might help with investigating deeper if the example is not using a PHAR file but a folder of PHP files. Then parts of it could be commented out, or place an exit at various points, to narrow down the specific code that leads to the error.
So I'll try simplifying the example to get closer to the cause.
OK, I tested by running PHPUnit from a Composer-installed vendor folder, and there is no WASM error. So the issue seems specific to the PHAR file.
I've updated the example repo to demonstrate the difference: npm run start
for PHAR with WASM error, and npm run start:vendor
for vendored folder.
For the latter, it ends with "Program terminated with exit(1)". After digging in the PHPUnit code, I found that this is expected behavior: here, the CLI exits with code 1
if no test file is specified.
For reference, I'll include the stack trace below. What's interesting is that everything is working correctly, but it still shows the "cause" of the error as Asyncify.handleSleep
.
$ npm run start:vendor
> wp-now-phpunit-example@1.0.0 start:vendor
> NODE_ENV=test node index.js
Error in argument 1, char 1: no argument for option -
Usage: phpunit [options] [-f] <file> [--] [args...]
phpunit [options] -r <code> [--] [args...]
phpunit [options] [-B <begin_code>] -R <code> [-E <end_code>] [--] [args...]
phpunit [options] [-B <begin_code>] -F <file> [-E <end_code>] [--] [args...]
phpunit [options] -S <addr>:<port> [-t docroot] [router]
phpunit [options] -- [args...]
phpunit [options] -a
-a Run as interactive shell
-c <path>|<file> Look for php.ini file in this directory
-n No configuration (ini) files will be used
-d foo[=bar] Define INI entry foo with value 'bar'
-e Generate extended information for debugger/profiler
-f <file> Parse and execute <file>.
-h This help
-i PHP information
-l Syntax check only (lint)
-m Show compiled in modules
-r <code> Run PHP <code> without using script tags <?..?>
-B <begin_code> Run PHP <begin_code> before processing input lines
-R <code> Run PHP <code> for every input line
-F <file> Parse and execute <file> for every input line
-E <end_code> Run PHP <end_code> after processing all input lines
-H Hide any passed arguments from external tools.
-S <addr>:<port> Run with built-in web server.
-t <docroot> Specify document root <docroot> for built-in web server.
-s Output HTML syntax highlighted source.
-v Version number
-w Output source with stripped comments and whitespace.
-z <file> Load Zend extension <file>.
args... Arguments passed to script. Use -- args when first argument
starts with - or script is read from stdin
--ini Show configuration file names
--rf <name> Show information about function <name>.
--rc <name> Show information about class <name>.
--re <name> Show information about extension <name>.
--rz <name> Show information about Zend extension <name>.
--ri <name> Show configuration for extension <name>.
PHP CLI error ExitStatus: Program terminated with exit(1)
at _proc_exit (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:31403:17)
at exitJS (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:31410:5)
at dynCall_vi (wasm://wasm/033de762:wasm-function[12342]:0x62e6cf)
at ret.<computed> (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:33018:33)
at runtime.asm.<computed> (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:71706:18)
at invoke_vi (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:33745:7)
at run_cli (wasm://wasm/033de762:wasm-function[10072]:0x55f617)
at ret.<computed> (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:33018:33)
at runtime.asm.<computed> (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:71706:18)
at Module._run_cli (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:33570:66)
at Object.ccall (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:33211:20)
at _NodePHP.cli (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:73875:47)
at main (file://~/wp-now-phpunit-example/index.js:26:15)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
status: 1,
cause: Error
at Asyncify.handleSleep (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:34003:45)
at _wasm_poll_socket (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:32883:21)
at php_pollfd_for (wasm://wasm/033de762:wasm-function[970]:0x91c74)
at php_network_connect_socket (wasm://wasm/033de762:wasm-function[5416]:0x3926cc)
at php_tcp_sockop_set_option (wasm://wasm/033de762:wasm-function[10420]:0x587eb1)
at php_openssl_sockop_set_option (wasm://wasm/033de762:wasm-function[10549]:0x592aff)
at _php_stream_set_option (wasm://wasm/033de762:wasm-function[520]:0x37949)
at dynCall_iiiii (wasm://wasm/033de762:wasm-function[12347]:0x62ebd7)
at ret.<computed> (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:33018:33)
at runtime.asm.<computed> (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:71706:18)
at invoke_iiiii (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:33767:14)
at _php_stream_xport_create (wasm://wasm/033de762:wasm-function[1715]:0xfb54c)
at zif_stream_socket_client (wasm://wasm/033de762:wasm-function[7831]:0x49f2b8)
at ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_USED_HANDLER (wasm://wasm/033de762:wasm-function[13699]:0x68d8f6)
at execute_ex (wasm://wasm/033de762:wasm-function[12148]:0x624deb)
at zend_execute (wasm://wasm/033de762:wasm-function[3379]:0x21da10)
}
The PHAR example doesn't show the help screen, so the WASM error must be happening before it.
Well, I can work around this issue by using vendored files instead of PHAR, but I'm curious to try if the error still happens with a minimal/empty PHAR file.
I added a more minimal example running the PHPUnit PHAR file with PHP-WASM only, not wp-now
, and there is no WASM error.
It displays a different help screen with colors, and exit code 2
. But working correctly. So it seems the WASM error arises from running a PHAR file using the PHP instance from wp-now
, but not @php-wasm/node
.
PHPUnit 9.6.17 by Sebastian Bergmann and contributors.
Usage:
phpunit [options] UnitTest.php
phpunit [options] <directory>
Code Coverage Options:
--coverage-clover <file> Generate code coverage report in Clover XML
format
--coverage-cobertura <file> Generate code coverage report in Cobertura XML
format
--coverage-crap4j <file> Generate code coverage report in Crap4J XML
format
--coverage-html <dir> Generate code coverage report in HTML format
--coverage-php <file> Export PHP_CodeCoverage object to file
--coverage-text=<file> Generate code coverage report in text format
[default: standard output]
--coverage-xml <dir> Generate code coverage report in PHPUnit XML
format
--coverage-cache <dir> Cache static analysis results
--warm-coverage-cache Warm static analysis cache
--coverage-filter <dir> Include <dir> in code coverage analysis
--path-coverage Perform path coverage analysis
--disable-coverage-ignore Disable annotations for ignoring code coverage
--no-coverage Ignore code coverage configuration
Logging Options:
--log-junit <file> Log test execution in JUnit XML format to file
--log-teamcity <file> Log test execution in TeamCity format to file
--testdox-html <file> Write agile documentation in HTML format to file
--testdox-text <file> Write agile documentation in Text format to file
--testdox-xml <file> Write agile documentation in XML format to file
--reverse-list Print defects in reverse order
--no-logging Ignore logging configuration
Test Selection Options:
--list-suites List available test suites
--testsuite <name> Filter which testsuite to run
--list-groups List available test groups
--group <name> Only runs tests from the specified group(s)
--exclude-group <name> Exclude tests from the specified group(s)
--covers <name> Only runs tests annotated with "@covers <name>"
--uses <name> Only runs tests annotated with "@uses <name>"
--list-tests List available tests
--list-tests-xml <file> List available tests in XML format
--filter <pattern> Filter which tests to run
--test-suffix <suffixes> Only search for test in files with specified
suffix(es). Default: Test.php,.phpt
Test Execution Options:
--dont-report-useless-tests Do not report tests that do not test anything
--strict-coverage Be strict about @covers annotation usage
--strict-global-state Be strict about changes to global state
--disallow-test-output Be strict about output during tests
--disallow-resource-usage Be strict about resource usage during small tests
--enforce-time-limit Enforce time limit based on test size
--default-time-limit <sec> Timeout in seconds for tests without @small,
@medium or @large
--disallow-todo-tests Disallow @todo-annotated tests
--process-isolation Run each test in a separate PHP process
--globals-backup Backup and restore $GLOBALS for each test
--static-backup Backup and restore static attributes for each
test
--colors <flag> Use colors in output ("never", "auto" or
"always")
--columns <n> Number of columns to use for progress output
--columns max Use maximum number of columns for progress output
--stderr Write to STDERR instead of STDOUT
--stop-on-defect Stop execution upon first not-passed test
--stop-on-error Stop execution upon first error
--stop-on-failure Stop execution upon first error or failure
--stop-on-warning Stop execution upon first warning
--stop-on-risky Stop execution upon first risky test
--stop-on-skipped Stop execution upon first skipped test
--stop-on-incomplete Stop execution upon first incomplete test
--fail-on-incomplete Treat incomplete tests as failures
--fail-on-risky Treat risky tests as failures
--fail-on-skipped Treat skipped tests as failures
--fail-on-warning Treat tests with warnings as failures
-v|--verbose Output more verbose information
--debug Display debugging information
--repeat <times> Runs the test(s) repeatedly
--teamcity Report test execution progress in TeamCity format
--testdox Report test execution progress in TestDox format
--testdox-group Only include tests from the specified group(s)
--testdox-exclude-group Exclude tests from the specified group(s)
--no-interaction Disable TestDox progress animation
--printer <printer> TestListener implementation to use
--order-by <order> Run tests in order:
default|defects|duration|no-depends|random|reverse|size
--random-order-seed <N> Use a specific random seed <N> for random order
--cache-result Write test results to cache file
--do-not-cache-result Do not write test results to cache file
Configuration Options:
--prepend <file> A PHP script that is included as early as
possible
--bootstrap <file> A PHP script that is included before the tests
run
-c|--configuration <file> Read configuration from XML file
--no-configuration Ignore default configuration file (phpunit.xml)
--extensions <extensions> A comma separated list of PHPUnit extensions to
load
--no-extensions Do not load PHPUnit extensions
--include-path <path(s)> Prepend PHP's include_path with given path(s)
-d <key[=value]> Sets a php.ini value
--cache-result-file <file> Specify result cache path and filename
--generate-configuration Generate configuration file with suggested
settings
--migrate-configuration Migrate configuration file to current format
PHAR Options:
--manifest Print Software Bill of Materials (SBOM) in
plain-text format
--sbom Print Software Bill of Materials (SBOM) in
CycloneDX XML format
--composer-lock Print composer.lock file used to build the PHAR
Miscellaneous Options:
-h|--help Prints this usage information
--version Prints the version and exits
--atleast-version <min> Checks that version is greater than min and exits
--check-version Checks whether PHPUnit is the latest version and
exits
ExitStatus: Program terminated with exit(2)
at _proc_exit (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:8518:17)
at exitJS (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:8525:5)
at dynCall_vi (wasm://wasm/03636fe2:wasm-function[12812]:0x69952e)
at ret.<computed> (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:10161:33)
at runtime.asm.<computed> (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:71706:18)
at invoke_vi (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:10892:7)
at run_cli (wasm://wasm/03636fe2:wasm-function[10402]:0x5b2dfa)
at ret.<computed> (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:10161:33)
at runtime.asm.<computed> (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:71706:18)
at Object.doRewind (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:10244:14) {
status: 2,
cause: Error
at Asyncify.handleSleep (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:11150:45)
at _js_popen_to_file (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:9529:21)
at wasm_popen (wasm://wasm/03636fe2:wasm-function[2420]:0x192a5d)
at zif_shell_exec (wasm://wasm/03636fe2:wasm-function[8190]:0x4f4731)
at ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER (wasm://wasm/03636fe2:wasm-function[14156]:0x6f99dc)
at execute_ex (wasm://wasm/03636fe2:wasm-function[12618]:0x68fc43)
at zend_execute (wasm://wasm/03636fe2:wasm-function[3444]:0x2303a5)
at zend_execute_scripts (wasm://wasm/03636fe2:wasm-function[9274]:0x576a9d)
at dynCall_iiiii (wasm://wasm/03636fe2:wasm-function[12817]:0x699a36)
at ret.<computed> (~/wp-now-phpunit-example/node_modules/@php-wasm/node/index.cjs:10161:33)
}
OK, I narrowed down the cause of the issue further. Created an even more minimal example (repo) using wp-now
and a basically empty PHAR file. (Kind of fun side quest to figure out how to build a PHAR file using @php-wasm/node
.)
git clone https://github.com/eliot-akira/wp-now-phar-example
cd wp-now-phar-example
npm install
npm run start
WASM ERROR
null function or function signature mismatch
So the issue is not related to PHPUnit, but simply running PHAR with wp-now
.
The same PHAR file can be run using @php-wasm/node
with no error.
npm run start:php-wasm
Hello from example.
Fantastic reproduction case @eliot-akira, thank you! I'm marking as high priority crash. It seems like we're not getting all the stack trace functions somehow, similarly as in https://github.com/WordPress/wordpress-playground/pull/1273.
I wonder if, for Node.js, we could just switch to JSPI and solve all these problems in one go. They would still occur on the web, but hopefully to a lesser extent. I'll start a new issue.
I tried to run a composer.phar
file too and I had this runtime error :
RuntimeError: null function or function signature mismatch
at php.wasm._php_stream_flush (wasm://wasm/php.wasm-038f5de6:wasm-function[1513]:0xe2a8f)
at php.wasm._php_stream_cast (wasm://wasm/php.wasm-038f5de6:wasm-function[737]:0x5a905)
at php.wasm.stream_array_from_fd_set (wasm://wasm/php.wasm-038f5de6:wasm-function[3842]:0x278d61)
at php.wasm.zif_stream_select (wasm://wasm/php.wasm-038f5de6:wasm-function[8501]:0x53e12e)
at php.wasm.ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_USED_HANDLER (wasm://wasm/php.wasm-038f5de6:wasm-function[15036]:0x7a78c1)
... 4 lines matching cause stack trace ...
at ret.<computed> (file:///Users/mho/Work/Projects/Development/Web/Side/browser-php/package/node_modules/@php-wasm/node/index.js:14145:22) {
cause: Error
at Asyncify.handleSleep (file:///Users/mho/Work/Projects/Development/Web/Side/browser-php/package/node_modules/@php-wasm/node/index.js:14955:45)
at _emscripten_sleep (file:///Users/mho/Work/Projects/Development/Web/Side/browser-php/package/node_modules/@php-wasm/node/index.js:12642:44)
at php.wasm.__wrap_select (wasm://wasm/php.wasm-038f5de6:wasm-function[1392]:0xcf647)
at php.wasm.zif_stream_select (wasm://wasm/php.wasm-038f5de6:wasm-function[8501]:0x53e02a)
at php.wasm.ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_USED_HANDLER (wasm://wasm/php.wasm-038f5de6:wasm-function[15036]:0x7a78c1)
at php.wasm.execute_ex (wasm://wasm/php.wasm-038f5de6:wasm-function[13394]:0x71fd7c)
at php.wasm.zend_execute (wasm://wasm/php.wasm-038f5de6:wasm-function[3609]:0x24bb16)
at php.wasm.zend_execute_scripts (wasm://wasm/php.wasm-038f5de6:wasm-function[9692]:0x5ca697)
at php.wasm.dynCall_iiiii (wasm://wasm/php.wasm-038f5de6:wasm-function[13612]:0x73c00e)
at ret.<computed> (file:///Users/mho/Work/Projects/Development/Web/Side/browser-php/package/node_modules/@php-wasm/node/index.js:14145:22)
}
The two missing functions where :
"zif_stream_select",\
"stream_array_from_fd_set",\
I added them in packages/php-wasm/compile/php/Dockerfile
on 541
and 780
, I compiled, built and I finally got this return when running Node's php.cli( [ 'php', 'vendor/bin/composer' ] )
:
I am not sure if this will make every PHAR file run, but it can be a small step toward the solution.
@adamziel should I suggest a PR ?
@mho22 a PR would be lovely, thank you!
I'm trying to run PHPUnit (in PHAR format) using
wp-now
, and encountered an error from@php-wasm/node
. I prepared an example repo that reproduces the issue.The error message: