From 47a27e71d9562ab746cfe3e2e341b219412f9fad Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Thu, 3 Sep 2026 17:03:07 +0200 Subject: [PATCH] Add `--enable-cli-fpm` to link the FPM SAPI into the CLI binary (`php --fpm`) --- NEWS | 2 ++ UPGRADING | 3 +++ UPGRADING.INTERNALS | 7 +++++++ sapi/cli/config.m4 | 18 ++++++++++++++++++ sapi/cli/php.1.in | 6 ++++++ sapi/cli/php_cli.c | 3 +++ sapi/cli/php_cli_main.c | 9 +++++++++ sapi/cli/tests/cli_fpm.phpt | 24 ++++++++++++++++++++++++ sapi/fpm/config.m4 | 19 +++++++++++-------- sapi/fpm/config0.m4 | 6 ++++++ sapi/fpm/fpm/fpm.h | 2 ++ sapi/fpm/fpm/fpm_main.c | 10 +++++++--- sapi/fpm/fpm/fpm_main.stub.php | 3 ++- sapi/fpm/fpm/fpm_main_arginfo.h | 8 ++++---- sapi/fpm/php_fpm_main.c | 19 +++++++++++++++++++ 15 files changed, 123 insertions(+), 16 deletions(-) create mode 100644 sapi/cli/tests/cli_fpm.phpt create mode 100644 sapi/fpm/config0.m4 create mode 100644 sapi/fpm/php_fpm_main.c diff --git a/NEWS b/NEWS index 623a29b0d6c8..ff51e326a1e1 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,8 @@ PHP NEWS ?? ??? ????, PHP 8.6.0beta3 - CLI: + . Added the --enable-cli-fpm configure option, which links the FPM SAPI into + the php binary so that "php --fpm" runs php-fpm. (Matthieu Napoli) . Fixed bug GH-23242 (PHP development server does not support Expect 100-continue flow control). (Sjoerd Langkemper) diff --git a/UPGRADING b/UPGRADING index 42ee8f4228d4..4b734f3dea2d 100644 --- a/UPGRADING +++ b/UPGRADING @@ -506,6 +506,9 @@ PHP 8.6 UPGRADE NOTES - CLI: . The built-in development server now accepts requests using the HTTP QUERY method instead of returning 501 Not Implemented. + . When PHP is configured with --enable-cli-fpm, the php binary also contains + the FPM SAPI: "php --fpm [php-fpm options]" behaves like php-fpm. The flag + is only recognized as the first argument. The default build is unchanged. ======================================== 4. Deprecated Functionality diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index bd671018e038..8db42963622c 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -227,6 +227,7 @@ PHP 8.6 INTERNALS UPGRADE NOTES . Added zend_string_ends_with() and related variants. . Added trait support for internal classes. . Added do_php_cli(). + . Added do_php_fpm(), the former main() of php-fpm. ======================== 2. Build system changes @@ -270,6 +271,12 @@ PHP 8.6 INTERNALS UPGRADE NOTES . The CLI SAPI can not be disabled when building the embed SAPI (--enable-embed is incompatible with --disable-cli). +- FPM: + . The --enable-fpm option moved to sapi/fpm/config0.m4 so that other SAPIs + can test $PHP_FPM. The FPM sources other than the main() entry point are + now built into PHP_FPM_SHARED_OBJS. The new --enable-cli-fpm option links + them into the CLI binary. + ======================== 3. Module changes ======================== diff --git a/sapi/cli/config.m4 b/sapi/cli/config.m4 index 6abaa88df419..26b5b81a250f 100644 --- a/sapi/cli/config.m4 +++ b/sapi/cli/config.m4 @@ -5,6 +5,22 @@ PHP_ARG_ENABLE([cli], [yes], [no]) +PHP_ARG_ENABLE([cli-fpm], + [whether to link FPM into the CLI binary], + [AS_HELP_STRING([--enable-cli-fpm], + [Link the FPM SAPI into the CLI binary, so that "php --fpm" runs php-fpm + (requires --enable-fpm)])], + [no], + [no]) + +if test "$PHP_CLI_FPM" != "no"; then + if test "$PHP_CLI" = "no" || test "$PHP_FPM" = "no"; then + AC_MSG_ERROR([--enable-cli-fpm requires both the CLI and the FPM SAPI]) + fi + AC_DEFINE([PHP_CLI_WITH_FPM], [1], + [Define to 1 if the FPM SAPI is linked into the CLI binary.]) +fi + dnl The embed SAPI requires the CLI sources for do_php_cli(). if test "$PHP_EMBED" != "no" -a "$PHP_CLI" = "no"; then AC_MSG_ERROR([--enable-embed requires the CLI SAPI, do not use --disable-cli with --enable-embed]) @@ -47,6 +63,8 @@ if test "$PHP_CLI" != "no"; then [-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1], [PHP_CLI_SHARED_OBJS]) PHP_CLI_OBJS="$PHP_CLI_OBJS $PHP_CLI_SHARED_OBJS" + AS_VAR_IF([PHP_CLI_FPM], [no],, + [PHP_CLI_OBJS="$PHP_CLI_OBJS \$(PHP_FASTCGI_OBJS) \$(PHP_FPM_SHARED_OBJS)"]) AS_CASE([$host_alias], [*aix*], [ diff --git a/sapi/cli/php.1.in b/sapi/cli/php.1.in index 30626015ce77..a9f2fc79af87 100644 --- a/sapi/cli/php.1.in +++ b/sapi/cli/php.1.in @@ -365,6 +365,12 @@ Shows configuration for extension .TP .B \-\-ini Show configuration file names +.TP +.B \-\-fpm +Run as +.BR php\-fpm (8), +passing the remaining arguments to it. Only available when PHP was configured +with \-\-enable\-cli\-fpm, and only recognized as the first argument. .SH FILES .TP 15 .B php\-cli.ini diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index 296b7cd521ff..3613cb946fbe 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -482,6 +482,9 @@ static void php_cli_usage(char *argv0) "\n" " --ini Show configuration file names\n" " --ini=diff Show INI entries that differ from the built-in default\n" +#ifdef PHP_CLI_WITH_FPM + " --fpm Run as php-fpm, passing the remaining arguments to it\n" +#endif "\n" " --rf Show information about function .\n" " --rc Show information about class .\n" diff --git a/sapi/cli/php_cli_main.c b/sapi/cli/php_cli_main.c index 1030b5feb923..f91b882ca296 100644 --- a/sapi/cli/php_cli_main.c +++ b/sapi/cli/php_cli_main.c @@ -12,8 +12,17 @@ #include "php.h" #include "cli.h" +#ifdef PHP_CLI_WITH_FPM +#include "sapi/fpm/fpm/fpm.h" +#endif int main(int argc, char *argv[]) { +#ifdef PHP_CLI_WITH_FPM + /* "php --fpm [args]" runs the FPM SAPI, which ignores the "--fpm" option. */ + if (argc > 1 && strcmp(argv[1], "--fpm") == 0) { + return do_php_fpm(argc, argv); + } +#endif return do_php_cli(argc, argv); } diff --git a/sapi/cli/tests/cli_fpm.phpt b/sapi/cli/tests/cli_fpm.phpt new file mode 100644 index 000000000000..750129338353 --- /dev/null +++ b/sapi/cli/tests/cli_fpm.phpt @@ -0,0 +1,24 @@ +--TEST-- +php --fpm runs the FPM SAPI +--SKIPIF-- +&1"), 'fpm-fcgi')) { + die("skip PHP was not built with --enable-cli-fpm"); +} +?> +--FILE-- + .*$/m', shell_exec("$php --fpm -n -i"), $m); +var_dump($m[0]); + +// Anywhere else it is an ordinary argument. +var_dump(shell_exec("$php -n -r 'echo \$argv[1];' -- --fpm")); +?> +--EXPECT-- +string(25) "Server API => FPM/FastCGI" +string(5) "--fpm" diff --git a/sapi/fpm/config.m4 b/sapi/fpm/config.m4 index 89c53a0c4d28..ad5ec8f26c02 100644 --- a/sapi/fpm/config.m4 +++ b/sapi/fpm/config.m4 @@ -1,10 +1,3 @@ -PHP_ARG_ENABLE([fpm], - [for FPM build], - [AS_HELP_STRING([--enable-fpm], - [Enable building of the fpm SAPI executable])], - [no], - [no]) - dnl Configure checks. AC_DEFUN([PHP_FPM_CLOCK], [ AC_CHECK_FUNCS([clock_gettime],, [ @@ -473,9 +466,18 @@ if test "$PHP_FPM" != "no"; then PHP_SELECT_SAPI([fpm], [program], - [$PHP_FPM_FILES $PHP_FPM_TRACE_FILES $PHP_FPM_SD_FILES], + [php_fpm_main.c], [-I$abs_srcdir/sapi/fpm -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) + dnl Everything except the main() entry point, so that the CLI binary can link + dnl the same objects for do_php_fpm() (--enable-cli-fpm). + PHP_ADD_SOURCES_X([sapi/fpm], + [$PHP_FPM_FILES $PHP_FPM_TRACE_FILES $PHP_FPM_SD_FILES], + [-I$abs_srcdir/sapi/fpm -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1], + [PHP_FPM_SHARED_OBJS]) + PHP_FPM_OBJS="$PHP_FPM_OBJS $PHP_FPM_SHARED_OBJS" + PHP_SUBST([PHP_FPM_SHARED_OBJS]) + AS_CASE([$host_alias], [*aix*], [ BUILD_FPM="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FPM_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --tag=CC --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FASTCGI_OBJS) \$(PHP_FPM_OBJS) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)" @@ -489,4 +491,5 @@ if test "$PHP_FPM" != "no"; then PHP_SUBST([SAPI_FPM_PATH]) PHP_SUBST([BUILD_FPM]) PHP_SUBST([FPM_EXTRA_LIBS]) + AS_VAR_IF([PHP_CLI_FPM], [no],, [EXTRA_LIBS="$EXTRA_LIBS $FPM_EXTRA_LIBS"]) fi diff --git a/sapi/fpm/config0.m4 b/sapi/fpm/config0.m4 new file mode 100644 index 000000000000..d00da52b30e8 --- /dev/null +++ b/sapi/fpm/config0.m4 @@ -0,0 +1,6 @@ +PHP_ARG_ENABLE([fpm], + [for FPM build], + [AS_HELP_STRING([--enable-fpm], + [Enable building of the fpm SAPI executable])], + [no], + [no]) diff --git a/sapi/fpm/fpm/fpm.h b/sapi/fpm/fpm/fpm.h index ec036a2d6819..90b06962a1ff 100644 --- a/sapi/fpm/fpm/fpm.h +++ b/sapi/fpm/fpm/fpm.h @@ -41,6 +41,8 @@ enum fpm_init_return_status { }; int fpm_run(int *max_requests); +/* this performs full fpm-SAPI boot: the former main() of php-fpm. */ +int do_php_fpm(int argc, char *argv[]); enum fpm_init_return_status fpm_init(int argc, char **argv, char *config, char *prefix, char *pid, int test_conf, int run_as_root, int force_daemon, int force_stderr); struct fpm_globals_s { diff --git a/sapi/fpm/fpm/fpm_main.c b/sapi/fpm/fpm/fpm_main.c index 655d66ce5f06..755d6c1f3ee4 100644 --- a/sapi/fpm/fpm/fpm_main.c +++ b/sapi/fpm/fpm/fpm_main.c @@ -131,6 +131,7 @@ static const opt_struct OPTIONS[] = { {'D', 0, "daemonize"}, {'F', 0, "nodaemonize"}, {'O', 0, "force-stderr"}, + {20, 0, "fpm"}, /* "php --fpm", see sapi/cli/php_cli_main.c */ {'-', 0, NULL} /* end of args */ }; @@ -1495,7 +1496,7 @@ PHP_FUNCTION(fastcgi_finish_request) /* {{{ */ } /* }}} */ -PHP_FUNCTION(apache_request_headers) /* {{{ */ +PHP_FUNCTION(fpm_apache_request_headers) /* {{{ */ { fcgi_request *request; @@ -1531,8 +1532,8 @@ static zend_module_entry cgi_module_entry = { STANDARD_MODULE_PROPERTIES }; -/* {{{ main */ -int main(int argc, char *argv[]) +/* {{{ do_php_fpm */ +int do_php_fpm(int argc, char *argv[]) { int exit_status = FPM_EXIT_OK; int c, use_extended_info = 0; @@ -1668,6 +1669,9 @@ int main(int argc, char *argv[]) force_stderr = 1; break; + case 20: /* php --fpm */ + break; + default: case 'h': case '?': diff --git a/sapi/fpm/fpm/fpm_main.stub.php b/sapi/fpm/fpm/fpm_main.stub.php index 70cd27522391..4cafc6522ae3 100644 --- a/sapi/fpm/fpm/fpm_main.stub.php +++ b/sapi/fpm/fpm/fpm_main.stub.php @@ -4,9 +4,10 @@ function fastcgi_finish_request(): bool {} +/** @implementation-alias fpm_apache_request_headers */ function apache_request_headers(): array {} -/** @alias apache_request_headers */ +/** @implementation-alias fpm_apache_request_headers */ function getallheaders(): array {} function fpm_get_status(): array|false {} diff --git a/sapi/fpm/fpm/fpm_main_arginfo.h b/sapi/fpm/fpm/fpm_main_arginfo.h index e612f160e760..db1192e94e75 100644 --- a/sapi/fpm/fpm/fpm_main_arginfo.h +++ b/sapi/fpm/fpm/fpm_main_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit fpm_main.stub.php instead. - * Stub hash: b4ac4c0f1d91c354293e21185a2e6d9f99cc9fcc */ + * Stub hash: 464cd4b4281d68f6368597bd3f890437459e77b7 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fastcgi_finish_request, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() @@ -13,13 +13,13 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_fpm_get_status, 0, 0, MAY_BE_ARR ZEND_END_ARG_INFO() ZEND_FUNCTION(fastcgi_finish_request); -ZEND_FUNCTION(apache_request_headers); +ZEND_FUNCTION(fpm_apache_request_headers); ZEND_FUNCTION(fpm_get_status); static const zend_function_entry ext_functions[] = { ZEND_FE(fastcgi_finish_request, arginfo_fastcgi_finish_request) - ZEND_FE(apache_request_headers, arginfo_apache_request_headers) - ZEND_RAW_FENTRY("getallheaders", zif_apache_request_headers, arginfo_getallheaders, 0, NULL, NULL) + ZEND_RAW_FENTRY("apache_request_headers", zif_fpm_apache_request_headers, arginfo_apache_request_headers, 0, NULL, NULL) + ZEND_RAW_FENTRY("getallheaders", zif_fpm_apache_request_headers, arginfo_getallheaders, 0, NULL, NULL) ZEND_FE(fpm_get_status, arginfo_fpm_get_status) ZEND_FE_END }; diff --git a/sapi/fpm/php_fpm_main.c b/sapi/fpm/php_fpm_main.c new file mode 100644 index 000000000000..d7a3f0bca369 --- /dev/null +++ b/sapi/fpm/php_fpm_main.c @@ -0,0 +1,19 @@ +/* + +----------------------------------------------------------------------+ + | Copyright © The PHP Group and Contributors. | + +----------------------------------------------------------------------+ + | This source file is subject to the Modified BSD License that is | + | bundled with this package in the file LICENSE, and is available | + | through the World Wide Web at . | + | | + | SPDX-License-Identifier: BSD-3-Clause | + +----------------------------------------------------------------------+ +*/ + +#include "php.h" +#include "fpm/fpm.h" + +int main(int argc, char *argv[]) +{ + return do_php_fpm(argc, argv); +}