镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Add --enable-cli-fpm to link the FPM SAPI into the CLI binary (php --fpm) - #23558

Open
mnapoli wants to merge 5 commits into
php:masterfrom
mnapoli:php-fpm-single-binary
Open

mnapoli wants to merge 5 commits into
php:masterfrom
mnapoli:php-fpm-single-binary

Conversation

@mnapoli

@mnapoli mnapoli commented Sep 3, 2026 •

Copy link
Copy Markdown

This adds an opt-in configure option, --enable-cli-fpm, that links the FPM SAPI into the php binary.

RFC

Intro

The binary behaves exactly like the CLI unless its first argument is --fpm, or it is invoked under a name starting with php-fpm (e.g. a php-fpm symlink to php). In both cases it runs the php-fpm master with the remaining arguments:

php -v                                     # PHP 8.7.0-dev (cli)
php --fpm -v                               # PHP 8.7.0-dev (fpm-fcgi)
php --fpm --nodaemonize -y /etc/php-fpm.conf
php script.php --fpm                       # "--fpm" is a script argument here (i.e. no changes)

ln -s php php-fpm
./php-fpm -v                               # PHP 8.7.0-dev (fpm-fcgi)

The default build is unchanged: without the flag, php and php-fpm are built exactly as before.

Goal

In some environments, the size of the runtime matters. Having php and php-fpm binaries when they are ~99% the same code can be wasteful.

That's the case for example on AWS Lambda with Bref, where a second ~24 MB binary on disk increases the cold start duration (because that's more data to load in the container/micro-VM when it starts).

Design

This follows the shape of #21385 (do_php_cli() / PHP_CLI_SHARED_OBJS for embed):

  • sapi/fpm/fpm/fpm_main.c: main() becomes fpm_main(argc, argv, first_arg), declared in fpm.h. A new sapi/fpm/php_fpm_main.c holds the one-line main() for the standalone php-fpm binary.
  • sapi/fpm/config.m4: PHP_SELECT_SAPI now only takes php_fpm_main.c; all other FPM sources go through PHP_ADD_SOURCES_X into PHP_FPM_SHARED_OBJS, which is appended to PHP_FPM_OBJS. These are plain objects, no shared library is built, and the BUILD_FPM link lines are untouched.
  • sapi/fpm/config0.m4 (new): PHP_ARG_ENABLE([fpm]) moves here so $PHP_FPM is set before sapi/cli/config.m4 runs (same reason embed has a config0.m4).
  • sapi/cli/config.m4: PHP_ARG_ENABLE([cli-fpm]), default off; errors out unless both the CLI and FPM SAPIs are enabled; defines PHP_CLI_WITH_FPM and appends $(PHP_FASTCGI_OBJS) $(PHP_FPM_SHARED_OBJS) to PHP_CLI_OBJS. The BUILD_CLI lines are untouched. FPM_EXTRA_LIBS (systemd, acl, apparmor, selinux) is appended to EXTRA_LIBS when the flag is on.
  • sapi/cli/php_cli_main.c: under #ifdef PHP_CLI_WITH_FPM, calls fpm_main() when basename(argv[0]) starts with php-fpm (FPM options start at argv[1]), or when argv[1] is --fpm (FPM options start at argv[2]).
  • argv is passed to FPM unchanged: on reload FPM re-executes its saved argv (so it must still start with php --fpm or the php-fpm name), and the process title is written over the original argv area. FPM's option table is untouched, so php-fpm --fpm is still rejected.
  • Both SAPIs define PHP_FUNCTION(apache_request_headers), which is a duplicate-symbol link error when linked together. FPM's C symbol is renamed to fpm_request_headers; the stub uses @implementation-alias on both apache_request_headers() and getallheaders() and the arginfo header is regenerated. Userland names and behaviour are unchanged.
  • php --help, php.1, NEWS, UPGRADING and UPGRADING.INTERNALS are updated. sapi/cli/tests/cli_fpm.phpt skips unless the flag is built in. The whole sapi/fpm/tests suite also passes when run through php --fpm and through a php-fpm symlink to php (including the reload tests).

Backward compatibility

  • Default builds: same binaries. The FPM objects are simply listed in two make variables.
  • Internals: main() of php-fpm is now fpm_main(); --enable-fpm is declared in config0.m4. Both noted in UPGRADING.INTERNALS.
  • Windows: untouched (FPM does not build there).

@henderkes

Copy link
Copy Markdown
Contributor

Cross-posting from mailing list to get a reminder on discussion here:

Hello Matthieu,

If the primary concern is with size, it may make sense to instead look into creating a reusable shared library on unix, much like on Windows.

That being said, while I'm also in favour of shipping only one binary for both, I strongly believe that it should not become an opt-in that nobody ends up shipping. It should become default at the very least, with an option to explicitly disable cli/fpm sources to save a few kb in a 9mb+++ binary at most.

Have you had any thoughts about the embed shared library if fpm should also be exposed there?

Marc

@bukka bukka left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about that splitting - we should for sure check with packagers to see what they think.

Comment thread sapi/fpm/fpm/fpm_main.c Outdated
Comment thread sapi/fpm/fpm/fpm_main.c Outdated
{'D', 0, "daemonize"},
{'F', 0, "nodaemonize"},
{'O', 0, "force-stderr"},
{20, 0, "fpm"}, /* "php --fpm", see sapi/cli/php_cli_main.c */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this - those are options for php-fpm binary so why is it added here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's there because php --fpm hands its argv to FPM unchanged, so FPM's getopt sees --fpm. The argv has to stay intact: on reload fpm_pctl_exec() re-executes the saved argv, so it must still start with php --fpm, and the process title is written over the original argv area.

But you're right that FPM's option table shouldn't know about this. The caller now tells FPM where its options start, so the entry is gone and php-fpm --fpm is rejected again.

Comment thread sapi/fpm/fpm/fpm.h Outdated

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[]);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fpm function should be prefix with fpm_

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't agree here, unless we also rename do_php_cli to cli_...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah cli should rename as well - it doesn't match our convention. Especially in fpm the convention is quite strict to prefix all functions with fpm_ .

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to fpm_main(). For do_php_cli(): since it's exported for embed and ships in 8.6, I'm leaving it out of this PR (LMK if you want me to take any action).

Comment thread sapi/fpm/config.m4
Comment on lines +472 to +479
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])

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you I'm not sure this is a good idea. Does this mean that it will always created shared lib for php-fpm so it will no longer be a single binary?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No shared library is built. "SHARED" means shared between two SAPIs, following PHP_CLI_SHARED_OBJS naming from #21385. Happy to rename it (e.g. PHP_FPM_COMMON_OBJS) if you find it confusing.

@henderkes

Copy link
Copy Markdown
Contributor

I'm not sure about that splitting - we should for sure check with packagers to see what they think.

As a build maintainer, especially with largely static linkage, I think it's a great idea. The different functionality could also be exposed directly if the binary is invoked under the php-fpm name (e.g. as a symlink). We're doing the same in frankenphp.

@mnapoli
mnapoli force-pushed the php-fpm-single-binary branch from 47a27e7 to 5c50562 Compare October 5, 2026 13:35
@mnapoli

mnapoli commented Oct 5, 2026

Copy link
Copy Markdown
Author

Thanks for the review!

Have you had any thoughts about the embed shared library if fpm should also be exposed there?

I don't see a direct use case for putting FPM in the embed library (from my POV) so I won't push for it in this change. But it could be a follow-up PR.

I'm not sure about that splitting - we should for sure check with packagers to see what they think.

If "splitting" refers to the config.m4 change, see my reply above: no shared library is built, php-fpm is still a single binary. If it's about the idea of putting FPM in the embed library or something else let me know.

The different functionality could also be exposed directly if the binary is invoked under the php-fpm name (e.g. as a symlink).

Good idea, done: in a combined build, running the binary under a name starting with php-fpm (e.g. a php-fpm or php-fpm8.7 symlink to php) starts FPM, same as php --fpm. That makes the combined binary a drop-in replacement for packagers and existing service definitions.

It should become default at the very least

I'd love to make this the default. I might include that as a vote in the RFC? (let me know if that's a decision that can instead be made here in this PR)

@mnapoli

mnapoli commented Oct 6, 2026

Copy link
Copy Markdown
Author

RFC published: https://wiki.php.net/rfc/single-binary-cli-fpm

I also started a discussion thread in the mailing list.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants