From 23661fcc30783b20805dcbe1047e0ab6a6dbb15f Mon Sep 17 00:00:00 2001 From: henderkes Date: Sun, 4 Oct 2026 23:00:27 +0200 Subject: [PATCH] fix #2690 --- cli_test.go | 24 ++++++++++++++++++++++++ emulate_php_cli.c | 34 +++++++++++++++++++++++++++++++++- testdata/argv.php | 3 +++ 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 testdata/argv.php diff --git a/cli_test.go b/cli_test.go index fb4dc62ffa..75acc22efe 100644 --- a/cli_test.go +++ b/cli_test.go @@ -37,6 +37,30 @@ func TestExecuteScriptCLI(t *testing.T) { assert.Contains(t, stdoutStderrStr, "From the CLI") } +// https://github.com/php/frankenphp/issues/2690 +func TestExecuteScriptCLIArgv(t *testing.T) { + if _, err := os.Stat("internal/testcli/testcli"); err != nil { + t.Skip("internal/testcli/testcli has not been compiled, run `cd internal/testcli/ && go build`") + } + + code := `printf("argv0=%s argc=%d args=%s\n", $argv[0], $argc, json_encode(array_slice($argv, 1)));` + for _, tt := range []struct { + name string + args []string + want string + }{ + {"script", []string{"testdata/argv.php", "foo", "bar"}, "argv0=testdata/argv.php argc=3 args=[\"foo\",\"bar\"]\n"}, + {"eval", []string{"-r", code, "a", "b"}, "argv0=Standard input code argc=3 args=[\"a\",\"b\"]\n"}, + } { + t.Run(tt.name, func(t *testing.T) { + cmd := exec.Command("internal/testcli/testcli", tt.args...) + stdoutStderr, err := cmd.CombinedOutput() + require.NoError(t, err, "output: %s", stdoutStderr) + assert.Equal(t, tt.want, string(stdoutStderr)) + }) + } +} + func TestExecuteCLICode(t *testing.T) { if _, err := os.Stat("internal/testcli/testcli"); err != nil { t.Skip("internal/testcli/testcli has not been compiled, run `cd internal/testcli/ && go build`") diff --git a/emulate_php_cli.c b/emulate_php_cli.c index 59f702f6f5..0c30313afb 100644 --- a/emulate_php_cli.c +++ b/emulate_php_cli.c @@ -39,6 +39,14 @@ cli_exec_args_t *cli_args; +static int (*previous_embed_startup)(sapi_module_struct *sapi_module) = NULL; + +static int cli_embed_startup(sapi_module_struct *sapi_module) { + sapi_module->executable_location = cli_args->argv[0]; + + return previous_embed_startup(sapi_module); +} + static void register_server_variable_filtered(const char *key, char **val, size_t *val_len, zval *track_vars_array) { @@ -148,13 +156,16 @@ void *emulate_script_cli(void *arg) { /* Parse argv to detect -r (eval mode) and find the script path */ bool eval = false; char *script = NULL; + int script_index = 0; for (int i = 1; i < args->argc; i++) { if (strcmp(args->argv[i], "-r") == 0 && i + 1 < args->argc) { eval = true; script = args->argv[i + 1]; + script_index = i + 1; break; } else if (args->argv[i][0] != '-') { script = args->argv[i]; + script_index = i; break; } } @@ -169,6 +180,18 @@ void *emulate_script_cli(void *arg) { */ cli_args->script = script; + /* + * Like the CLI SAPI's do_cli(): script path must become $argv[0]. + * In -r mode the CLI SAPI reports "Standard input code" as $argv[0]. + */ + int argc = args->argc - script_index; + char **argv = args->argv + script_index; + char *code = NULL; + if (eval) { + code = argv[0]; + argv[0] = "Standard input code"; + } + /* * The SAPI name "cli" is hardcoded into too many programs... let's usurp it. */ @@ -178,7 +201,12 @@ void *emulate_script_cli(void *arg) { /* the CLI SAPI prints phpinfo() as plain text, not as HTML */ php_embed_module.phpinfo_as_text = 1; - php_embed_init(cli_args->argc, cli_args->argv); + if (php_embed_module.startup != cli_embed_startup) { + previous_embed_startup = php_embed_module.startup; + php_embed_module.startup = cli_embed_startup; + } + + php_embed_init(argc, argv); cli_register_file_handles(); zend_first_try { @@ -199,5 +227,9 @@ void *emulate_script_cli(void *arg) { php_embed_shutdown(); + if (eval) { + argv[0] = code; + } + return exit_status; } diff --git a/testdata/argv.php b/testdata/argv.php new file mode 100644 index 0000000000..61d06fe62d --- /dev/null +++ b/testdata/argv.php @@ -0,0 +1,3 @@ +