From 746dc23680bf9a4e07a0293811d61d593b486ed9 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 5 Mar 2026 14:54:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(core):=20=E6=B7=BB=E5=8A=A0=E8=BF=9B?= =?UTF-8?q?=E7=A8=8B=E6=A0=87=E9=A2=98=E8=AE=BE=E7=BD=AE=E5=92=8C=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在编译器中添加了 php_cli_process_title.c 和 ps_title.c 源文件 - 在扩展模板中添加了头文件包含和函数注册 - 实现了 cli_set_process_title 和 cli_get_process_title 函数 - 集成了跨平台的进程标题修改功能 - 添加了命令行参数保存和清理机制 - 提供了完整的错误处理和状态码返回 --- bin/compiler.php | 2 + src/cpp/main.cc | 3 + src/cpp/php_cli_process_title.c | 63 ++++ src/cpp/php_cli_process_title.h | 23 ++ src/cpp/php_cli_process_title.stub.php | 5 + src/cpp/php_cli_process_title_arginfo.h | 10 + src/cpp/ps_title.c | 478 ++++++++++++++++++++++++ src/cpp/ps_title.h | 41 ++ src/template/extension.cc.php | 6 + 9 files changed, 631 insertions(+) create mode 100644 src/cpp/php_cli_process_title.c create mode 100644 src/cpp/php_cli_process_title.h create mode 100644 src/cpp/php_cli_process_title.stub.php create mode 100644 src/cpp/php_cli_process_title_arginfo.h create mode 100644 src/cpp/ps_title.c create mode 100644 src/cpp/ps_title.h diff --git a/bin/compiler.php b/bin/compiler.php index 52c7580d..b4b4a2c2 100755 --- a/bin/compiler.php +++ b/bin/compiler.php @@ -70,6 +70,8 @@ $sourceFiles[] = $extensionSourceFile; // 添加 main.cc 文件 if ($translator->getBuildMode() == 'bin') { $sourceFiles[] = ROOT_PATH . '/src/cpp/main.cc'; + $sourceFiles[] = ROOT_PATH . '/src/cpp/php_cli_process_title.c'; + $sourceFiles[] = ROOT_PATH . '/src/cpp/ps_title.c'; } // 编译所有 C++ 文件 diff --git a/src/cpp/main.cc b/src/cpp/main.cc index 3e099294..8d57cca9 100644 --- a/src/cpp/main.cc +++ b/src/cpp/main.cc @@ -4,6 +4,7 @@ #include #include "sapi/embed/php_embed.h" +#include "ps_title.h" extern zend_module_entry *php_embed_get_module(); @@ -54,6 +55,8 @@ int main(int cpp_argc, char **cpp_argv) { zend_module_entry *module = php_embed_get_module(); module_init(module); + save_ps_args(cpp_argc, cpp_argv); + int rc = 0; #if PPROF_ON ProfilerStart("profile.out"); diff --git a/src/cpp/php_cli_process_title.c b/src/cpp/php_cli_process_title.c new file mode 100644 index 00000000..a786de2b --- /dev/null +++ b/src/cpp/php_cli_process_title.c @@ -0,0 +1,63 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Keyur Govande (kgovande@gmail.com) | + +----------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "php.h" +#include "php_cli_process_title.h" +#include "ps_title.h" + +/* {{{ Return a boolean to confirm if the process title was successfully changed or not */ +PHP_FUNCTION(cli_set_process_title) +{ + char *title = NULL; + size_t title_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &title, &title_len) == FAILURE) { + RETURN_THROWS(); + } + + ps_title_status rc = set_ps_title(title, title_len); + if (rc == PS_TITLE_SUCCESS) { + RETURN_TRUE; + } + + php_error_docref(NULL, E_WARNING, "cli_set_process_title had an error: %s", ps_title_errno(rc)); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ Return a string with the current process title. NULL if error. */ +PHP_FUNCTION(cli_get_process_title) +{ + size_t length = 0; + const char* title = NULL; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + ps_title_status rc = get_ps_title(&length, &title); + if (rc != PS_TITLE_SUCCESS) { + php_error_docref(NULL, E_WARNING, "cli_get_process_title had an error: %s", ps_title_errno(rc)); + RETURN_NULL(); + } + + RETURN_STRINGL(title, length); +} +/* }}} */ diff --git a/src/cpp/php_cli_process_title.h b/src/cpp/php_cli_process_title.h new file mode 100644 index 00000000..9527b4b4 --- /dev/null +++ b/src/cpp/php_cli_process_title.h @@ -0,0 +1,23 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Keyur Govande (kgovande@gmail.com) | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_PS_TITLE_HEADER +#define PHP_PS_TITLE_HEADER + +PHP_FUNCTION(cli_set_process_title); +PHP_FUNCTION(cli_get_process_title); + +#endif diff --git a/src/cpp/php_cli_process_title.stub.php b/src/cpp/php_cli_process_title.stub.php new file mode 100644 index 00000000..97dc8b27 --- /dev/null +++ b/src/cpp/php_cli_process_title.stub.php @@ -0,0 +1,5 @@ + +#ifdef PHP_WIN32 +#include "config.w32.h" +#include +#include +#include "win32/codepage.h" +#else +#include +extern char** environ; +#endif + +#include "ps_title.h" +#include + +#ifdef HAVE_UNISTD_H +#include +#include +#endif + +#include +#include +#include + +#ifdef HAVE_SYS_PSTAT_H +#include /* for HP-UX */ +#endif +#ifdef HAVE_PS_STRINGS +#include /* for old BSD */ +#include +#endif +#if defined(__APPLE__) +#include +#endif + +/* + * Ways of updating ps display: + * + * PS_USE_SETPROCTITLE + * use the function setproctitle(const char *, ...) + * (newer BSD systems) + * PS_USE_PSTAT + * use the pstat(PSTAT_SETCMD, ) + * (HPUX) + * PS_USE_PS_STRINGS + * assign PS_STRINGS->ps_argvstr = "string" + * (some BSD systems) + * PS_USE_CHANGE_ARGV + * assign argv[0] = "string" + * (some other BSD systems) + * PS_USE_CLOBBER_ARGV + * write over the argv and environment area + * (Linux and most SysV-like systems) + * PS_USE_WIN32 + * push the string out as the name of a Windows event + * PS_USE_NONE + * don't update ps display + * (This is the default, as it is safest.) + */ +#if defined(HAVE_SETPROCTITLE) +#define PS_USE_SETPROCTITLE +#elif defined(HAVE_SYS_PSTAT_H) && defined(PSTAT_SETCMD) +#define PS_USE_PSTAT +#elif defined(HAVE_PS_STRINGS) +#define PS_USE_PS_STRINGS +#elif defined(BSD) && !defined(__APPLE__) +#define PS_USE_CHANGE_ARGV +#elif defined(__linux__) || defined(_AIX) || defined(__sgi) || (defined(sun) && !defined(BSD)) || defined(ultrix) || defined(__osf__) || defined(__APPLE__) +#define PS_USE_CLOBBER_ARGV +#elif defined(PHP_WIN32) +#define PS_USE_WIN32 +#else +#define PS_USE_NONE +#endif + +/* Different systems want the buffer padded differently */ +#if defined(_AIX) || defined(__linux__) || defined(__APPLE__) +#define PS_PADDING '\0' +#else +#define PS_PADDING ' ' +#endif + +#ifdef PS_USE_WIN32 +static char windows_error_details[64]; +static char ps_buffer[MAX_PATH]; +static const size_t ps_buffer_size = MAX_PATH; +#elif defined(PS_USE_CLOBBER_ARGV) +static char *ps_buffer; /* will point to argv area */ +static size_t ps_buffer_size; /* space determined at run time */ +static char *empty_environ[] = {0}; /* empty environment */ +#else +#define PS_BUFFER_SIZE 256 +static char ps_buffer[PS_BUFFER_SIZE]; +static const size_t ps_buffer_size = PS_BUFFER_SIZE; +#endif + +static size_t ps_buffer_cur_len; /* actual string length in ps_buffer */ + +/* save the original argv[] location here */ +static int save_argc; +static char** save_argv; + +/* + * This holds the 'locally' allocated environ from the save_ps_args method. + * This is subsequently free'd at exit. + */ +#if defined(PS_USE_CLOBBER_ARGV) +static char** frozen_environ, **new_environ; +#endif + +/* + * Call this method early, before any code has used the original argv passed in + * from main(). + * If needed, this code will make deep copies of argv and environ and return + * these to the caller for further use. The original argv is then 'clobbered' + * to store the process title. + */ +char** save_ps_args(int argc, char** argv) +{ + save_argc = argc; + save_argv = argv; + +#if defined(PS_USE_CLOBBER_ARGV) + /* + * If we're going to overwrite the argv area, count the available space. + * Also move the environment to make additional room. + */ + { + char* end_of_area = NULL; + bool is_contiguous_area = true; + int i; + + /* + * check for contiguous argv strings + */ + for (i = 0; is_contiguous_area && (i < argc); i++) + { + if (i != 0 && end_of_area + 1 != argv[i]) { + is_contiguous_area = false; + } + end_of_area = argv[i] + strlen(argv[i]); + } + + if (!is_contiguous_area) { + goto clobber_error; + } + + /* + * check for contiguous environ strings following argv + */ + for (i = 0; environ[i] != NULL; i++) + { + if (end_of_area + 1 == environ[i]) { + end_of_area = environ[i] + strlen(environ[i]); + } + } + + ps_buffer = argv[0]; + ps_buffer_size = end_of_area - argv[0]; + + /* + * move the environment out of the way + */ + new_environ = (char **) malloc((i + 1) * sizeof(char *)); + frozen_environ = (char **) malloc((i + 1) * sizeof(char *)); + if (!new_environ || !frozen_environ) { + goto clobber_error; + } + for (i = 0; environ[i] != NULL; i++) + { + new_environ[i] = strdup(environ[i]); + if (!new_environ[i]) + goto clobber_error; + } + new_environ[i] = NULL; + environ = new_environ; + memcpy((char *)frozen_environ, (char *)new_environ, sizeof(char *) * (i + 1)); + + } +#endif /* PS_USE_CLOBBER_ARGV */ + +#if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV) + /* + * If we're going to change the original argv[] then make a copy for + * argument parsing purposes. + * + * (NB: do NOT think to remove the copying of argv[]! + * On some platforms, getopt() keeps pointers into the argv array, and + * will get horribly confused when it is re-called to analyze a subprocess' + * argument string if the argv storage has been clobbered meanwhile. + * Other platforms have other dependencies on argv[].) + */ + { + char** new_argv; + int i; + + new_argv = (char **) malloc((argc + 1) * sizeof(char *)); + if (!new_argv) + goto clobber_error; + for (i = 0; i < argc; i++) + { + new_argv[i] = strdup(argv[i]); + if (!new_argv[i]) { + free(new_argv); + goto clobber_error; + } + } + new_argv[argc] = NULL; + +#if defined(__APPLE__) + /* + * Darwin (and perhaps other NeXT-derived platforms?) has a static + * copy of the argv pointer, which we may fix like so: + */ + *_NSGetArgv() = new_argv; +#endif + + argv = new_argv; + + } +#endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */ + +#if defined(PS_USE_CLOBBER_ARGV) + { + /* make extra argv slots point at end_of_area (a NUL) */ + int i; + for (i = 1; i < save_argc; i++) + save_argv[i] = ps_buffer + ps_buffer_size; + } +#endif /* PS_USE_CLOBBER_ARGV */ + +#ifdef PS_USE_CHANGE_ARGV + save_argv[0] = ps_buffer; /* ps_buffer here is a static const array of size PS_BUFFER_SIZE */ + save_argv[1] = NULL; +#endif /* PS_USE_CHANGE_ARGV */ + + return argv; + +#if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV) +clobber_error: + /* probably can't happen?! + * if we ever get here, argv still points to originally passed + * in argument + */ + save_argv = NULL; + save_argc = 0; + ps_buffer = NULL; + ps_buffer_size = 0; + return argv; +#endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */ +} + +/* + * Returns PS_TITLE_SUCCESS if the OS supports this functionality + * and the init function was called. + * Otherwise returns NOT_AVAILABLE or NOT_INITIALIZED + */ +ps_title_status is_ps_title_available(void) +{ +#ifdef PS_USE_NONE + return PS_TITLE_NOT_AVAILABLE; /* disabled functionality */ +#endif + + if (!save_argv) + return PS_TITLE_NOT_INITIALIZED; + +#ifdef PS_USE_CLOBBER_ARGV + if (!ps_buffer) + return PS_TITLE_BUFFER_NOT_AVAILABLE; +#endif /* PS_USE_CLOBBER_ARGV */ + + return PS_TITLE_SUCCESS; +} + +/* + * Convert error codes into error strings + */ +const char* ps_title_errno(ps_title_status rc) +{ + switch(rc) + { + case PS_TITLE_SUCCESS: + return "Success"; + + case PS_TITLE_NOT_AVAILABLE: + return "Not available on this OS"; + + case PS_TITLE_NOT_INITIALIZED: + return "Not initialized correctly"; + + case PS_TITLE_BUFFER_NOT_AVAILABLE: + return "Buffer not contiguous"; + + case PS_TITLE_TOO_LONG: + // TODO Indicate max length? + return "Too long"; + + case PS_TITLE_WINDOWS_ERROR: +#ifdef PS_USE_WIN32 + snprintf(windows_error_details, sizeof(windows_error_details), "Windows error code: %lu", GetLastError()); + return windows_error_details; +#endif + return "Windows error"; + } + + return "Unknown error code"; +} + +/* + * Set a new process title. + * Returns the appropriate error code if if there's an error + * (like the functionality is compile time disabled, or the + * save_ps_args() was not called. + * Else returns 0 on success. + */ +ps_title_status set_ps_title(const char *title, size_t title_len) +{ + if (title_len >= ps_buffer_size) { + return PS_TITLE_TOO_LONG; + } + + ps_title_status rc = is_ps_title_available(); + if (rc != PS_TITLE_SUCCESS) + return rc; + + /* Include final null byte */ + memcpy(ps_buffer, title, title_len+1); + ps_buffer_cur_len = title_len; + +#ifdef PS_USE_SETPROCTITLE + setproctitle("%s", ps_buffer); +#endif + +#ifdef PS_USE_PSTAT + { + union pstun pst; + + pst.pst_command = ps_buffer; + pstat(PSTAT_SETCMD, pst, ps_buffer_cur_len, 0, 0); + } +#endif /* PS_USE_PSTAT */ + +#ifdef PS_USE_PS_STRINGS + PS_STRINGS->ps_nargvstr = 1; + PS_STRINGS->ps_argvstr = ps_buffer; +#endif /* PS_USE_PS_STRINGS */ + +#ifdef PS_USE_CLOBBER_ARGV + /* pad unused memory */ + if (ps_buffer_cur_len < ps_buffer_size) + { + memset(ps_buffer + ps_buffer_cur_len, PS_PADDING, + ps_buffer_size - ps_buffer_cur_len); + } +#endif /* PS_USE_CLOBBER_ARGV */ + +#ifdef PS_USE_WIN32 + { + wchar_t *ps_buffer_w = php_win32_cp_any_to_w(ps_buffer); + + if (!ps_buffer_w || !SetConsoleTitleW(ps_buffer_w)) { + return PS_TITLE_WINDOWS_ERROR; + } + + free(ps_buffer_w); + } +#endif /* PS_USE_WIN32 */ + + return PS_TITLE_SUCCESS; +} + +/* + * Returns the current ps_buffer value into string. On some platforms + * the string will not be null-terminated, so return the effective + * length into *displen. + * The return code indicates the error. + */ +ps_title_status get_ps_title(size_t *displen, const char** string) +{ + ps_title_status rc = is_ps_title_available(); + if (rc != PS_TITLE_SUCCESS) + return rc; + +#ifdef PS_USE_WIN32 + { + wchar_t ps_buffer_w[MAX_PATH]; + char *tmp; + + if (!(ps_buffer_cur_len = GetConsoleTitleW(ps_buffer_w, (DWORD)sizeof(ps_buffer_w)))) { + return PS_TITLE_WINDOWS_ERROR; + } + + tmp = php_win32_cp_conv_w_to_any(ps_buffer_w, PHP_WIN32_CP_IGNORE_LEN, &ps_buffer_cur_len); + if (!tmp) { + return PS_TITLE_WINDOWS_ERROR; + } + + ps_buffer_cur_len = ps_buffer_cur_len > sizeof(ps_buffer)-1 ? sizeof(ps_buffer)-1 : ps_buffer_cur_len; + + memmove(ps_buffer, tmp, ps_buffer_cur_len); + free(tmp); + } +#endif + *displen = ps_buffer_cur_len; + *string = ps_buffer; + return PS_TITLE_SUCCESS; +} + +/* + * Clean up the allocated argv and environ if applicable. Only call + * this right before exiting. + * This isn't needed per-se because the OS will clean-up anyway, but + * having and calling this will ensure Valgrind doesn't output 'false + * positives'. + */ +void cleanup_ps_args(char **argv) +{ +#ifndef PS_USE_NONE + if (save_argv) + { + save_argv = NULL; + save_argc = 0; + +#ifdef PS_USE_CLOBBER_ARGV + { + int i; + for (i = 0; frozen_environ[i] != NULL; i++) + free(frozen_environ[i]); + free(frozen_environ); + free(new_environ); + /* leave a sane environment behind since some atexit() handlers + call getenv(). */ + environ = empty_environ; + } +#endif /* PS_USE_CLOBBER_ARGV */ + +#if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV) + { + int i; + for (i=0; argv[i] != NULL; i++) + free(argv[i]); + free(argv); + } +#endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */ + } +#endif /* PS_USE_NONE */ + + return; +} diff --git a/src/cpp/ps_title.h b/src/cpp/ps_title.h new file mode 100644 index 00000000..7436c2b2 --- /dev/null +++ b/src/cpp/ps_title.h @@ -0,0 +1,41 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Keyur Govande | + +----------------------------------------------------------------------+ + */ + +#ifndef PS_TITLE_HEADER +#define PS_TITLE_HEADER + +typedef enum { + PS_TITLE_SUCCESS = 0, + PS_TITLE_NOT_AVAILABLE = 1, + PS_TITLE_NOT_INITIALIZED = 2, + PS_TITLE_BUFFER_NOT_AVAILABLE = 3, + PS_TITLE_WINDOWS_ERROR = 4, + PS_TITLE_TOO_LONG = 5, +} ps_title_status; + +extern char** save_ps_args(int argc, char** argv); + +extern ps_title_status set_ps_title(const char *title, size_t title_len); + +extern ps_title_status get_ps_title(size_t* displen, const char** string); + +extern const char* ps_title_errno(ps_title_status rc); + +extern ps_title_status is_ps_title_available(void); + +extern void cleanup_ps_args(char **argv); + +#endif // PS_TITLE_HEADER diff --git a/src/template/extension.cc.php b/src/template/extension.cc.php index d0886f14..0fc95e5e 100644 --- a/src/template/extension.cc.php +++ b/src/template/extension.cc.php @@ -6,6 +6,10 @@ use PhpAot\Php\Translator; echo $this->genIncludeHeaderFiles(); ?> +#include "ps_title.h" +#include "php_cli_process_title.h" +#include "php_cli_process_title_arginfo.h" + // global vars functions as $functionDef): if ($this->buildMode === 'ext' and $functionDef->name === 'main') {