From c5f4cf36650a2c8015f8f3a053035451b1f4ce91 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Wed, 3 Jun 2026 17:21:49 +0200 Subject: [PATCH] Relax optimizer/JIT cross-file boundary restriction for CLI In CLI (but not cli-server) it's safe to assume script structures are fully immutable, given the structures will not be freed until the process ends. Hence, we can make stronger assumptions during optimization and jitting. This may be beneficial for web servers implemented in PHP, or worker mode. --- Zend/Optimizer/zend_optimizer.c | 11 +++++++++++ main/SAPI.c | 8 ++++++++ main/SAPI.h | 6 +++++- sapi/cli/php_cli.c | 12 ++++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index d10b4d83fc3e..4ce4a2ba2731 100644 --- a/Zend/Optimizer/zend_optimizer.c +++ b/Zend/Optimizer/zend_optimizer.c @@ -29,6 +29,7 @@ #include "zend_inference.h" #include "zend_dump.h" #include "php.h" +#include "SAPI.h" #ifndef ZEND_OPTIMIZER_MAX_REGISTERED_PASSES # define ZEND_OPTIMIZER_MAX_REGISTERED_PASSES 32 @@ -786,6 +787,9 @@ static bool zend_optimizer_ignore_class(zval *ce_zv, const zend_string *filename return false; } } + if (sapi_is_single_request()) { + return false; + } return ce->type == ZEND_USER_CLASS && (!ce->info.user.filename || ce->info.user.filename != filename); } @@ -807,6 +811,9 @@ static bool zend_optimizer_ignore_function(zval *fbc_zv, const zend_string *file return false; } } + if (sapi_is_single_request()) { + return false; + } return !fbc->op_array.filename || fbc->op_array.filename != filename; } else { ZEND_ASSERT(fbc->type == ZEND_EVAL_CODE); @@ -899,6 +906,10 @@ const zend_class_constant *zend_fetch_class_const_info( *is_prototype = is_static_reference && !(const_info->ce->ce_flags & ZEND_ACC_FINAL) && !(ZEND_CLASS_CONST_FLAGS(const_info) & ZEND_ACC_FINAL); + if (Z_TYPE(const_info->value) > IS_ARRAY) { + return NULL; + } + return const_info; } diff --git a/main/SAPI.c b/main/SAPI.c index 62e1c89e4bb9..c7e2f269d7bf 100644 --- a/main/SAPI.c +++ b/main/SAPI.c @@ -1173,3 +1173,11 @@ SAPI_API void sapi_add_request_header(const char *var, unsigned int var_len, cha } } /* }}} */ + +SAPI_API bool sapi_is_single_request(void) +{ + if (!sapi_module.is_single_request) { + return false; + } + return sapi_module.is_single_request(); +} diff --git a/main/SAPI.h b/main/SAPI.h index e62f686603c4..980802291715 100644 --- a/main/SAPI.h +++ b/main/SAPI.h @@ -167,6 +167,7 @@ SAPI_API void sapi_deactivate_destroy(void); SAPI_API void sapi_deactivate(void); SAPI_API void sapi_initialize_empty_request(void); SAPI_API void sapi_add_request_header(const char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg); +SAPI_API bool sapi_is_single_request(void); END_EXTERN_C() /* @@ -290,6 +291,8 @@ struct _sapi_module_struct { unsigned int (*input_filter_init)(void); int (*pre_request_init)(void); /* called before activate and before the post data read - used for .user.ini */ + + bool (*is_single_request)(void); /* Whether the SAPI will only handle a single request. This implies all script structures are immutable. */ }; struct _sapi_post_entry { @@ -341,6 +344,7 @@ END_EXTERN_C() NULL, /* ini_entries; */ \ NULL, /* additional_functions */ \ NULL, /* input_filter_init */ \ - NULL /* pre_request_init */ + NULL, /* pre_request_init */ \ + NULL /* is_single_request */ #endif /* SAPI_H */ diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index 9a05f8e68547..78ae7e458811 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -1189,6 +1189,11 @@ static int do_cli(int argc, char **argv) /* {{{ */ } /* }}} */ +static bool is_single_request(void) +{ + return true; +} + /* {{{ main */ #ifdef PHP_CLI_WIN32_NO_CONSOLE int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) @@ -1320,6 +1325,13 @@ int main(int argc, char *argv[]) sapi_module_ptr->php_ini_path_override = ini_path_override; sapi_module_ptr->phpinfo_as_text = 1; sapi_module_ptr->php_ini_ignore_cwd = 1; +#ifndef PHP_CLI_WIN32_NO_CONSOLE + if (sapi_module_ptr != &cli_server_sapi_module) { + sapi_module_ptr->is_single_request = is_single_request; + } +#else + sapi_module_ptr->is_single_request = is_single_request; +#endif sapi_startup(sapi_module_ptr); sapi_started = 1;