Skip to content
Merged
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
24 changes: 24 additions & 0 deletions cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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`")
Expand Down
34 changes: 33 additions & 1 deletion emulate_php_cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}
}
Expand All @@ -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.
*/
Expand All @@ -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 {
Expand All @@ -199,5 +227,9 @@ void *emulate_script_cli(void *arg) {

php_embed_shutdown();

if (eval) {
argv[0] = code;
}

return exit_status;
}
3 changes: 3 additions & 0 deletions testdata/argv.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php

printf("argv0=%s argc=%d args=%s\n", $argv[0], $argc, json_encode(array_slice($argv, 1)));
Loading