From ba1897952c6d4f089752f6b915e3406272fcc71f Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Thu, 3 Sep 2026 17:03:07 +0200 Subject: [PATCH 1/4] Add `--enable-cli-fpm` to link the FPM SAPI into the CLI binary (`php --fpm`) --- NEWS | 4 ++++ UPGRADING | 5 +++++ UPGRADING.INTERNALS | 5 +++++ 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 | 8 ++++++-- sapi/fpm/php_fpm_main.c | 19 +++++++++++++++++++ 13 files changed, 118 insertions(+), 10 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 44fb719c6982..a987d073f18f 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.7.0alpha1 +- 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) + - Curl: . curl_multi_exec() now throws a TypeError when still_running is not an int. (arshidkv12) diff --git a/UPGRADING b/UPGRADING index 46cf4cafab12..1e0aa1ce0eee 100644 --- a/UPGRADING +++ b/UPGRADING @@ -52,6 +52,11 @@ PHP 8.7 UPGRADE NOTES 3. Changes in SAPI modules ======================================== +- CLI: + . 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 2f6ea7d9ea84..1c57072e289e 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -45,6 +45,7 @@ PHP 8.7 INTERNALS UPGRADE NOTES - The INIT_CLASS_ENTRY_INIT_METHODS() macro, which was previously used as part of handling disabled classes, was removed following the removal of the `disable_classes` INI setting in PHP 8.5. +- Added do_php_fpm(), the former main() of php-fpm. ======================== 2. Build system changes @@ -57,6 +58,10 @@ PHP 8.7 INTERNALS UPGRADE NOTES - Unix build system changes: . Autoconf minimum required version upgraded to 2.71. + . 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 3702da89f9f0..f5604b96f64d 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -490,6 +490,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 3d35e942a06e..1c294c01c6ef 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 */ }; @@ -1530,8 +1531,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; @@ -1667,6 +1668,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/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); +} From 26fcf3896cace0d71ae0dea26221d38d4cfaea5d Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Mon, 5 Oct 2026 12:21:32 +0200 Subject: [PATCH 2/4] Parse FPM options after --fpm instead of accepting it as an FPM option --- sapi/cli/php_cli_main.c | 5 +++-- sapi/fpm/fpm/fpm.h | 6 ++++-- sapi/fpm/fpm/fpm_main.c | 7 ++----- sapi/fpm/php_fpm_main.c | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/sapi/cli/php_cli_main.c b/sapi/cli/php_cli_main.c index f91b882ca296..5b23d978853e 100644 --- a/sapi/cli/php_cli_main.c +++ b/sapi/cli/php_cli_main.c @@ -19,9 +19,10 @@ int main(int argc, char *argv[]) { #ifdef PHP_CLI_WITH_FPM - /* "php --fpm [args]" runs the FPM SAPI, which ignores the "--fpm" option. */ + /* "php --fpm [args]" runs the FPM SAPI. argv is passed as is because FPM + * re-executes it on reload; FPM options start after "--fpm". */ if (argc > 1 && strcmp(argv[1], "--fpm") == 0) { - return do_php_fpm(argc, argv); + return do_php_fpm(argc, argv, 2); } #endif return do_php_cli(argc, argv); diff --git a/sapi/fpm/fpm/fpm.h b/sapi/fpm/fpm/fpm.h index 90b06962a1ff..7d8b6c01c42a 100644 --- a/sapi/fpm/fpm/fpm.h +++ b/sapi/fpm/fpm/fpm.h @@ -41,8 +41,10 @@ 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[]); +/* this performs full fpm-SAPI boot: the former main() of php-fpm. + * argv must be the process' original argv (it is re-executed on reload), + * FPM options are parsed starting at argv[first_arg]. */ +int do_php_fpm(int argc, char *argv[], int first_arg); 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 1c294c01c6ef..d1813dd60f7c 100644 --- a/sapi/fpm/fpm/fpm_main.c +++ b/sapi/fpm/fpm/fpm_main.c @@ -131,7 +131,6 @@ 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 */ }; @@ -1532,7 +1531,7 @@ static zend_module_entry cgi_module_entry = { }; /* {{{ do_php_fpm */ -int do_php_fpm(int argc, char *argv[]) +int do_php_fpm(int argc, char *argv[], int first_arg) { int exit_status = FPM_EXIT_OK; int c, use_extended_info = 0; @@ -1593,6 +1592,7 @@ int do_php_fpm(int argc, char *argv[]) php_ini_builder_init(&ini_builder); + php_optind = first_arg; while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) { switch (c) { case 'c': @@ -1668,9 +1668,6 @@ int do_php_fpm(int argc, char *argv[]) force_stderr = 1; break; - case 20: /* php --fpm */ - break; - default: case 'h': case '?': diff --git a/sapi/fpm/php_fpm_main.c b/sapi/fpm/php_fpm_main.c index d7a3f0bca369..997fbd5961da 100644 --- a/sapi/fpm/php_fpm_main.c +++ b/sapi/fpm/php_fpm_main.c @@ -15,5 +15,5 @@ int main(int argc, char *argv[]) { - return do_php_fpm(argc, argv); + return do_php_fpm(argc, argv, 1); } From 485296b23008d5e47f6084c1c82055b9cc445e58 Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Mon, 5 Oct 2026 12:27:38 +0200 Subject: [PATCH 3/4] Rename do_php_fpm() to fpm_main() --- UPGRADING.INTERNALS | 2 +- sapi/cli/php_cli_main.c | 2 +- sapi/fpm/config.m4 | 2 +- sapi/fpm/fpm/fpm.h | 2 +- sapi/fpm/fpm/fpm_main.c | 4 ++-- sapi/fpm/php_fpm_main.c | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 1c57072e289e..032328af21be 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -45,7 +45,7 @@ PHP 8.7 INTERNALS UPGRADE NOTES - The INIT_CLASS_ENTRY_INIT_METHODS() macro, which was previously used as part of handling disabled classes, was removed following the removal of the `disable_classes` INI setting in PHP 8.5. -- Added do_php_fpm(), the former main() of php-fpm. +- Added fpm_main(), the former main() of php-fpm. ======================== 2. Build system changes diff --git a/sapi/cli/php_cli_main.c b/sapi/cli/php_cli_main.c index 5b23d978853e..e582066bc695 100644 --- a/sapi/cli/php_cli_main.c +++ b/sapi/cli/php_cli_main.c @@ -22,7 +22,7 @@ int main(int argc, char *argv[]) /* "php --fpm [args]" runs the FPM SAPI. argv is passed as is because FPM * re-executes it on reload; FPM options start after "--fpm". */ if (argc > 1 && strcmp(argv[1], "--fpm") == 0) { - return do_php_fpm(argc, argv, 2); + return fpm_main(argc, argv, 2); } #endif return do_php_cli(argc, argv); diff --git a/sapi/fpm/config.m4 b/sapi/fpm/config.m4 index ad5ec8f26c02..b2c293833965 100644 --- a/sapi/fpm/config.m4 +++ b/sapi/fpm/config.m4 @@ -470,7 +470,7 @@ if test "$PHP_FPM" != "no"; then [-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). + dnl the same objects for fpm_main() (--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], diff --git a/sapi/fpm/fpm/fpm.h b/sapi/fpm/fpm/fpm.h index 7d8b6c01c42a..a018948c8bd2 100644 --- a/sapi/fpm/fpm/fpm.h +++ b/sapi/fpm/fpm/fpm.h @@ -44,7 +44,7 @@ int fpm_run(int *max_requests); /* this performs full fpm-SAPI boot: the former main() of php-fpm. * argv must be the process' original argv (it is re-executed on reload), * FPM options are parsed starting at argv[first_arg]. */ -int do_php_fpm(int argc, char *argv[], int first_arg); +int fpm_main(int argc, char *argv[], int first_arg); 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 d1813dd60f7c..d08ad1a640c2 100644 --- a/sapi/fpm/fpm/fpm_main.c +++ b/sapi/fpm/fpm/fpm_main.c @@ -1530,8 +1530,8 @@ static zend_module_entry cgi_module_entry = { STANDARD_MODULE_PROPERTIES }; -/* {{{ do_php_fpm */ -int do_php_fpm(int argc, char *argv[], int first_arg) +/* {{{ fpm_main */ +int fpm_main(int argc, char *argv[], int first_arg) { int exit_status = FPM_EXIT_OK; int c, use_extended_info = 0; diff --git a/sapi/fpm/php_fpm_main.c b/sapi/fpm/php_fpm_main.c index 997fbd5961da..ab06cb4347d0 100644 --- a/sapi/fpm/php_fpm_main.c +++ b/sapi/fpm/php_fpm_main.c @@ -15,5 +15,5 @@ int main(int argc, char *argv[]) { - return do_php_fpm(argc, argv, 1); + return fpm_main(argc, argv, 1); } From d0b32261e197698ec5ea34330474760498e5ca06 Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Mon, 5 Oct 2026 12:50:40 +0200 Subject: [PATCH 4/4] Run FPM when the combined binary is invoked under a php-fpm name --- NEWS | 3 ++- UPGRADING | 4 +++- sapi/cli/php.1.in | 3 +++ sapi/cli/php_cli_main.c | 10 +++++++++- sapi/cli/tests/cli_fpm.phpt | 14 ++++++++++++++ 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index a987d073f18f..576a965c4afc 100644 --- a/NEWS +++ b/NEWS @@ -4,7 +4,8 @@ PHP NEWS - 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) + the php binary so that "php --fpm" (or a php-fpm symlink to php) runs + php-fpm. (Matthieu Napoli) - Curl: . curl_multi_exec() now throws a TypeError when still_running is not an int. diff --git a/UPGRADING b/UPGRADING index 1e0aa1ce0eee..a761c922894b 100644 --- a/UPGRADING +++ b/UPGRADING @@ -55,7 +55,9 @@ PHP 8.7 UPGRADE NOTES - CLI: . 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. + is only recognized as the first argument. The binary also behaves like + php-fpm when invoked under a name starting with "php-fpm" (e.g. through a + php-fpm symlink). The default build is unchanged. ======================================== 4. Deprecated Functionality diff --git a/sapi/cli/php.1.in b/sapi/cli/php.1.in index a9f2fc79af87..1972b1f87b4c 100644 --- a/sapi/cli/php.1.in +++ b/sapi/cli/php.1.in @@ -371,6 +371,9 @@ 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. +Such a binary also runs as +.BR php\-fpm (8) +when invoked under a name starting with php\-fpm, e.g. through a symbolic link. .SH FILES .TP 15 .B php\-cli.ini diff --git a/sapi/cli/php_cli_main.c b/sapi/cli/php_cli_main.c index e582066bc695..b13066610f85 100644 --- a/sapi/cli/php_cli_main.c +++ b/sapi/cli/php_cli_main.c @@ -19,8 +19,16 @@ int main(int argc, char *argv[]) { #ifdef PHP_CLI_WITH_FPM - /* "php --fpm [args]" runs the FPM SAPI. argv is passed as is because FPM + /* Running as "php-fpm*" (e.g. a php-fpm symlink to php) or as + * "php --fpm [args]" runs the FPM SAPI. argv is passed as is because FPM * re-executes it on reload; FPM options start after "--fpm". */ + if (argc > 0 && argv[0]) { + const char *name = strrchr(argv[0], '/'); + name = name ? name + 1 : argv[0]; + if (strncmp(name, "php-fpm", strlen("php-fpm")) == 0) { + return fpm_main(argc, argv, 1); + } + } if (argc > 1 && strcmp(argv[1], "--fpm") == 0) { return fpm_main(argc, argv, 2); } diff --git a/sapi/cli/tests/cli_fpm.phpt b/sapi/cli/tests/cli_fpm.phpt index 750129338353..0e15bc1e8eeb 100644 --- a/sapi/cli/tests/cli_fpm.phpt +++ b/sapi/cli/tests/cli_fpm.phpt @@ -18,7 +18,21 @@ var_dump($m[0]); // Anywhere else it is an ordinary argument. var_dump(shell_exec("$php -n -r 'echo \$argv[1];' -- --fpm")); + +// Running under a name starting with "php-fpm" also switches to the FPM SAPI. +$dir = __DIR__ . '/cli_fpm'; +@mkdir($dir); +symlink(realpath(getenv('TEST_PHP_EXECUTABLE')), "$dir/php-fpm8"); +preg_match('/^Server API => .*$/m', shell_exec(escapeshellarg("$dir/php-fpm8") . " -n -i"), $m); +var_dump($m[0]); +?> +--CLEAN-- + --EXPECT-- string(25) "Server API => FPM/FastCGI" string(5) "--fpm" +string(25) "Server API => FPM/FastCGI"