Skip to content

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

Open
mnapoli wants to merge 1 commit into
php:masterfrom
mnapoli:php-fpm-single-binary
Open

mnapoli wants to merge 1 commit 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.

The binary behaves exactly like the CLI unless its first argument is --fpm, in which case it runs the php-fpm master with the remaining arguments:

php -v                                     # PHP 8.6.0-dev (cli)
php --fpm -v                               # PHP 8.6.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)

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 do_php_fpm(), 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. 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, dispatch to do_php_fpm(argc, argv) when argv[1] is --fpm.
  • FPM's option table accepts and ignores --fpm, so argv is passed through unchanged. Shifting argv instead breaks FPM's process titles (fpm_env_init_main requires the argv strings to be contiguous), which I verified on Linux. Side effect: php-fpm --fpm is silently accepted.
  • 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_apache_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. A new sapi/cli/tests/cli_fpm.phpt skips unless the flag is built in.

Backward compatibility

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

Note: I am new here, so please let me know if I've got things backwards, I've made mistakes, I haven't followed the right workflow, etc. I'm opening this tentatively to get the discussion started.

And the main question I see: does this require a RFC or not?

@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
/* }}} */

PHP_FUNCTION(apache_request_headers) /* {{{ */
PHP_FUNCTION(fpm_apache_request_headers) /* {{{ */

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.

Not sure why apache should stay in it...

Suggested change
PHP_FUNCTION(fpm_apache_request_headers) /* {{{ */
PHP_FUNCTION(fpm_request_headers) /* {{{ */

Comment thread sapi/fpm/fpm/fpm_main.c
{'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?

Comment thread sapi/fpm/fpm/fpm.h

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_ .

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?

@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.

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