diff --git a/.github/workflows/static.yaml b/.github/workflows/static.yaml index a6ed634c32..976def3140 100644 --- a/.github/workflows/static.yaml +++ b/.github/workflows/static.yaml @@ -177,6 +177,7 @@ jobs: "${BINARY}" list-modules | grep http.handlers.mercure "${BINARY}" list-modules | grep http.handlers.mercure "${BINARY}" list-modules | grep http.handlers.vulcain + "${BINARY}" php-cli -r "echo 'Sanity check passed';" env: BINARY: ./frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }} @@ -277,6 +278,7 @@ jobs: "${BINARY}" list-modules | grep http.handlers.mercure "${BINARY}" list-modules | grep http.handlers.mercure "${BINARY}" list-modules | grep http.handlers.vulcain + "${BINARY}" php-cli -r "echo 'Sanity check passed';" env: BINARY: ./frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}-gnu @@ -393,5 +395,6 @@ jobs: "${BINARY}" list-modules | grep http.handlers.mercure "${BINARY}" list-modules | grep http.handlers.mercure "${BINARY}" list-modules | grep http.handlers.vulcain + "${BINARY}" php-cli -r "echo 'Sanity check passed';" env: BINARY: dist/frankenphp-mac-${{ matrix.platform }} diff --git a/caddy/php-cli.go b/caddy/php-cli.go index 3e75e20fdd..0a0172cd3e 100644 --- a/caddy/php-cli.go +++ b/caddy/php-cli.go @@ -19,13 +19,13 @@ func init() { Long: ` Executes a PHP script similarly to the CLI SAPI.`, CobraFunc: func(cmd *cobra.Command) { - cmd.DisableFlagParsing = true + cmd.Flags().StringP("code", "r", "", "Execute PHP code directly without tags") cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdPHPCLI) }, }) } -func cmdPHPCLI(caddycmd.Flags) (int, error) { +func cmdPHPCLI(fs caddycmd.Flags) (int, error) { args := os.Args[2:] if len(args) < 1 { return 1, errors.New("the path to the PHP script is required") @@ -37,7 +37,13 @@ func cmdPHPCLI(caddycmd.Flags) (int, error) { } } - status := frankenphp.ExecuteScriptCLI(args[0], args) + var status int + if evalCode := fs.String("code"); evalCode != "" { + status = frankenphp.ExecutePHPCode(evalCode) + } else { + status = frankenphp.ExecuteScriptCLI(args[0], args) + } + os.Exit(status) return status, nil diff --git a/frankenphp.c b/frankenphp.c index af116437d0..551bd53ed6 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -1108,6 +1108,7 @@ static void sapi_cli_register_variables(zval *track_vars_array) /* {{{ */ static void *execute_script_cli(void *arg) { void *exit_status; + bool eval = (bool)arg; /* * The SAPI name "cli" is hardcoded into too many programs... let's usurp it. @@ -1120,11 +1121,16 @@ static void *execute_script_cli(void *arg) { cli_register_file_handles(false); zend_first_try { - zend_file_handle file_handle; - zend_stream_init_filename(&file_handle, cli_script); + if (eval) { + /* evaluate the cli_script as literal PHP code (php-cli -r "...") */ + zend_eval_string_ex(cli_script, NULL, "Command line code", 1); + } else { + zend_file_handle file_handle; + zend_stream_init_filename(&file_handle, cli_script); - CG(skip_shebang) = 1; - php_execute_script(&file_handle); + CG(skip_shebang) = 1; + php_execute_script(&file_handle); + } } zend_end_try(); @@ -1135,7 +1141,8 @@ static void *execute_script_cli(void *arg) { return exit_status; } -int frankenphp_execute_script_cli(char *script, int argc, char **argv) { +int frankenphp_execute_script_cli(char *script, int argc, char **argv, + bool eval) { pthread_t thread; int err; void *exit_status; @@ -1148,7 +1155,7 @@ int frankenphp_execute_script_cli(char *script, int argc, char **argv) { * Start the script in a dedicated thread to prevent conflicts between Go and * PHP signal handlers */ - err = pthread_create(&thread, NULL, execute_script_cli, NULL); + err = pthread_create(&thread, NULL, execute_script_cli, (void *)eval); if (err != 0) { return err; } diff --git a/frankenphp.go b/frankenphp.go index 17caea1c9b..216071c5a5 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -635,7 +635,13 @@ func ExecuteScriptCLI(script string, args []string) int { argc, argv := convertArgs(args) defer freeArgs(argv) - return int(C.frankenphp_execute_script_cli(cScript, argc, (**C.char)(unsafe.Pointer(&argv[0])))) + return int(C.frankenphp_execute_script_cli(cScript, argc, (**C.char)(unsafe.Pointer(&argv[0])), false)) +} + +func ExecutePHPCode(phpCode string) int { + cCode := C.CString(phpCode) + defer C.free(unsafe.Pointer(cCode)) + return int(C.frankenphp_execute_script_cli(cCode, 0, nil, true)) } func convertArgs(args []string) (C.int, []*C.char) { diff --git a/frankenphp.h b/frankenphp.h index 7839bcf7e0..6636bfbf02 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -61,7 +61,8 @@ int frankenphp_update_server_context(bool is_worker_request, int frankenphp_request_startup(); int frankenphp_execute_script(char *file_name); -int frankenphp_execute_script_cli(char *script, int argc, char **argv); +int frankenphp_execute_script_cli(char *script, int argc, char **argv, + bool eval); void frankenphp_register_variables_from_request_info( zval *track_vars_array, zend_string *content_type, diff --git a/frankenphp_test.go b/frankenphp_test.go index 59312c9045..e6caa42711 100644 --- a/frankenphp_test.go +++ b/frankenphp_test.go @@ -790,6 +790,19 @@ func TestExecuteScriptCLI(t *testing.T) { assert.Contains(t, stdoutStderrStr, "From the CLI") } +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`") + } + + cmd := exec.Command("internal/testcli/testcli", "-r", "echo 'Hello World';") + stdoutStderr, err := cmd.CombinedOutput() + assert.NoError(t, err) + + stdoutStderrStr := string(stdoutStderr) + assert.Equal(t, stdoutStderrStr, `Hello World`) +} + func ExampleServeHTTP() { if err := frankenphp.Init(); err != nil { panic(err) diff --git a/internal/testcli/main.go b/internal/testcli/main.go index 47a4ca1862..c03c836c4d 100644 --- a/internal/testcli/main.go +++ b/internal/testcli/main.go @@ -13,5 +13,9 @@ func main() { os.Exit(1) } + if len(os.Args) == 3 && os.Args[1] == "-r" { + os.Exit(frankenphp.ExecutePHPCode(os.Args[2])) + } + os.Exit(frankenphp.ExecuteScriptCLI(os.Args[1], os.Args)) } diff --git a/static-builder-gnu.Dockerfile b/static-builder-gnu.Dockerfile index 120126bb0c..9288a62018 100644 --- a/static-builder-gnu.Dockerfile +++ b/static-builder-gnu.Dockerfile @@ -107,7 +107,7 @@ ENV CC='/opt/rh/devtoolset-10/root/usr/bin/gcc' ENV CXX='/opt/rh/devtoolset-10/root/usr/bin/g++' ENV AR='/opt/rh/devtoolset-10/root/usr/bin/ar' ENV LD='/opt/rh/devtoolset-10/root/usr/bin/ld' -ENV SPC_DEFAULT_C_FLAGS='-fPIE -fPIC -O3 -march=native' +ENV SPC_DEFAULT_C_FLAGS='-fPIE -fPIC -O3' ENV SPC_LIBC='glibc' ENV SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM='-Wl,-O3 -pie' ENV SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS='-ldl -lpthread -lm -lresolv -lutil -lrt' diff --git a/static-builder-musl.Dockerfile b/static-builder-musl.Dockerfile index e122862b27..3f7ab48327 100644 --- a/static-builder-musl.Dockerfile +++ b/static-builder-musl.Dockerfile @@ -90,7 +90,7 @@ RUN go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get WORKDIR /go/src/app COPY --link . ./ -ENV SPC_DEFAULT_C_FLAGS='-fPIE -fPIC -O3 -march=native' +ENV SPC_DEFAULT_C_FLAGS='-fPIE -fPIC -O3' ENV SPC_LIBC='musl' ENV SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM='-Wl,-O3 -pie'