Commit 4fc487f6d9c for php.net

commit 4fc487f6d9c6876bdc328914d7887d933eee3d84
Author: Marc <m@pyc.ac>
Date:   Tue Jul 28 16:27:20 2026 +0700

    Make php-cli functionality available in embed build (#21385)

diff --git a/NEWS b/NEWS
index a7aa29cd904..48864929c22 100644
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,9 @@ PHP                                                                        NEWS
   . Fixed bug GH-22825 (DOMElement::setAttribute() fails silently when the DTD
     declares a default value for the attribute). (iliaal)

+- Embed:
+  . Made php-cli functionality available in embed builds. (henderkes)
+
 - GMP:
   . Added gmp_prevprime(). (Weilin Du, David Carlier)
   . Fixed GMP power and shift operators to reject GMP right operands outside
diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS
index 39f5d72e270..8b4b5912317 100644
--- a/UPGRADING.INTERNALS
+++ b/UPGRADING.INTERNALS
@@ -191,6 +191,7 @@ PHP 8.6 INTERNALS UPGRADE NOTES
     strings from one or more buffers.
   . Added zend_string_ends_with() and related variants.
   . Added trait support for internal classes.
+  . Added do_php_cli().

 ========================
 2. Build system changes
@@ -227,6 +228,10 @@ PHP 8.6 INTERNALS UPGRADE NOTES
   . Added a new function CHECK_HEADER() which is intended to be used instead of
     the CHECK_HEADER_ADD_INCLUDE().

+- Embed:
+  . The CLI SAPI can not be disabled when building the embed SAPI
+    (--enable-embed is incompatible with --disable-cli).
+
 ========================
 3. Module changes
 ========================
@@ -307,3 +312,6 @@ PHP 8.6 INTERNALS UPGRADE NOTES

 - AG and SCNG are now allocated with ts_allocate_tls_id() and live in native
   __thread storage on ZTS builds.
+
+- php-cli functionality is now available in embed builds via the do_php_cli()
+  function.
diff --git a/sapi/cli/cli.h b/sapi/cli/cli.h
index 2f6ef95052c..9502a6726fe 100644
--- a/sapi/cli/cli.h
+++ b/sapi/cli/cli.h
@@ -54,4 +54,7 @@ typedef struct php_cli_server_context {
 	php_cli_mode mode;
 } php_cli_server_context;

+/* this performs full cli-SAPI boot, loads modules, sets up TSRM and co. */
+extern PHP_CLI_API int do_php_cli(int argc, char *argv[]);
+
 #endif /* CLI_H */
diff --git a/sapi/cli/cli_win32.c b/sapi/cli/cli_win32.c
index 4407fd088f6..de7d0a0282f 100644
--- a/sapi/cli/cli_win32.c
+++ b/sapi/cli/cli_win32.c
@@ -1,2 +1,7 @@
 #define PHP_CLI_WIN32_NO_CONSOLE 1
 #include "php_cli.c"
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
+{
+	return do_php_cli(__argc, __argv);
+}
diff --git a/sapi/cli/config.m4 b/sapi/cli/config.m4
index 76c2d64e8c8..6abaa88df41 100644
--- a/sapi/cli/config.m4
+++ b/sapi/cli/config.m4
@@ -5,6 +5,11 @@ PHP_ARG_ENABLE([cli],
   [yes],
   [no])

+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])
+fi
+
 if test "$PHP_CLI" != "no"; then
   AC_CHECK_FUNCS([setproctitle])

@@ -32,9 +37,17 @@ if test "$PHP_CLI" != "no"; then
   dnl Select SAPI.
   PHP_SELECT_SAPI([cli],
     [program],
-    [php_cli.c php_http_parser.c php_cli_server.c ps_title.c php_cli_process_title.c],
+    [php_cli_main.c],
     [-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1])

+  dnl Everything except the main() entry point, so that the embed SAPI can link
+  dnl the same objects into libphp for do_php_cli().
+  PHP_ADD_SOURCES_X([sapi/cli],
+    [php_cli.c php_http_parser.c php_cli_server.c ps_title.c php_cli_process_title.c],
+    [-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1],
+    [PHP_CLI_SHARED_OBJS])
+  PHP_CLI_OBJS="$PHP_CLI_OBJS $PHP_CLI_SHARED_OBJS"
+
   AS_CASE([$host_alias],
     [*aix*], [
       AS_VAR_IF([php_sapi_module], [shared], [
diff --git a/sapi/cli/config.w32 b/sapi/cli/config.w32
index 5dff4054b69..695a5b46ec1 100644
--- a/sapi/cli/config.w32
+++ b/sapi/cli/config.w32
@@ -3,8 +3,13 @@
 ARG_ENABLE('cli', 'Build CLI version of PHP', 'yes');
 ARG_ENABLE('cli-win32', 'Build console-less CLI version of PHP', 'no');

+// The embed SAPI requires the CLI sources for do_php_cli().
+if (PHP_EMBED != "no" && PHP_CLI != "yes") {
+	ERROR("--enable-embed requires the CLI SAPI, do not use --disable-cli with --enable-embed");
+}
+
 if (PHP_CLI == "yes") {
-	SAPI('cli', 'php_cli.c php_http_parser.c php_cli_server.c', 'php.exe', '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1');
+	SAPI('cli', 'php_cli.c php_cli_main.c php_http_parser.c php_cli_server.c', 'php.exe', '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1');
 	ADD_SOURCES(configure_module_dirname, 'php_cli_process_title.c ps_title.c', 'cli');
 	ADD_FLAG("LIBS_CLI", "ws2_32.lib");
 	ADD_FLAG("LIBS_CLI", "shell32.lib");
diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c
index 9095fb10dd4..296b7cd521f 100644
--- a/sapi/cli/php_cli.c
+++ b/sapi/cli/php_cli.c
@@ -1173,18 +1173,10 @@ static int do_cli(int argc, char **argv) /* {{{ */
 }
 /* }}} */

-/* {{{ main */
-#ifdef PHP_CLI_WIN32_NO_CONSOLE
-int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
-#else
-int main(int argc, char *argv[])
-#endif
+/* {{{ do_php_cli */
+PHP_CLI_API int do_php_cli(int argc, char *argv[])
 {
 #if defined(PHP_WIN32)
-# ifdef PHP_CLI_WIN32_NO_CONSOLE
-	int argc = __argc;
-	char **argv = __argv;
-# endif
 	int num_args;
 	wchar_t **argv_wide;
 	char **argv_save = argv;
@@ -1387,6 +1379,6 @@ int main(int argc, char *argv[])
 	 * exiting.
 	 */
 	cleanup_ps_args(argv);
-	exit(exit_status);
+	return exit_status;
 }
 /* }}} */
diff --git a/sapi/cli/php_cli_main.c b/sapi/cli/php_cli_main.c
new file mode 100644
index 00000000000..1030b5feb92
--- /dev/null
+++ b/sapi/cli/php_cli_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 <https://www.php.net/license/>.        |
+   |                                                                      |
+   | SPDX-License-Identifier: BSD-3-Clause                                |
+   +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "cli.h"
+
+int main(int argc, char *argv[])
+{
+	return do_php_cli(argc, argv);
+}
diff --git a/sapi/embed/config.m4 b/sapi/embed/config.m4
index 2bb5ed4df4d..e0051ecf66d 100644
--- a/sapi/embed/config.m4
+++ b/sapi/embed/config.m4
@@ -1,10 +1,3 @@
-PHP_ARG_ENABLE([embed],,
-  [AS_HELP_STRING([[--enable-embed[=TYPE]]],
-    [Enable building of embedded SAPI library TYPE is either
-    'shared' or 'static'. [TYPE=shared]])],
-  [no],
-  [no])
-
 AC_MSG_CHECKING([for embedded SAPI library support])

 if test "$PHP_EMBED" != "no"; then
@@ -33,6 +26,9 @@ if test "$PHP_EMBED" != "no"; then
       [php_embed.c],
       [-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1])
     PHP_INSTALL_HEADERS([sapi/embed], [php_embed.h])
+
+    dnl Link the CLI objects into libphp for do_php_cli().
+    PHP_SAPI_OBJS="$PHP_SAPI_OBJS $PHP_CLI_SHARED_OBJS"
   ])
 else
   AC_MSG_RESULT([no])
diff --git a/sapi/embed/config.w32 b/sapi/embed/config.w32
index 394982126ca..3ac4668bce8 100644
--- a/sapi/embed/config.w32
+++ b/sapi/embed/config.w32
@@ -6,5 +6,8 @@ var PHP_EMBED_PGO = false;

 if (PHP_EMBED != "no") {
 	SAPI('embed', 'php_embed.c', 'php' + PHP_VERSION + 'embed.lib', '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1');
+	ADD_SOURCES("sapi/cli", "php_cli.c php_http_parser.c php_cli_server.c ps_title.c php_cli_process_title.c", "embed", undefined, true);
+	ADD_FLAG("LIBS_EMBED", "ws2_32.lib");
+	ADD_FLAG("LIBS_EMBED", "shell32.lib");
 	PHP_INSTALL_HEADERS("sapi/embed", "php_embed.h");
 }
diff --git a/sapi/embed/config0.m4 b/sapi/embed/config0.m4
new file mode 100644
index 00000000000..f1c05b8ea01
--- /dev/null
+++ b/sapi/embed/config0.m4
@@ -0,0 +1,6 @@
+PHP_ARG_ENABLE([embed],,
+  [AS_HELP_STRING([[--enable-embed[=TYPE]]],
+    [Enable building of embedded SAPI library TYPE is either
+    'shared' or 'static'. [TYPE=shared]])],
+  [no],
+  [no])
diff --git a/sapi/embed/php_embed.c b/sapi/embed/php_embed.c
index 75249f97fc8..96534c18a69 100644
--- a/sapi/embed/php_embed.c
+++ b/sapi/embed/php_embed.c
@@ -28,10 +28,6 @@ static const char HARDCODED_INI[] =
 	"max_execution_time=0\n"
 	"max_input_time=-1\n\0";

-#if defined(PHP_WIN32) && defined(ZTS)
-ZEND_TSRMLS_CACHE_DEFINE()
-#endif
-
 static char* php_embed_read_cookies(void)
 {
 	return NULL;