--- a/main/main.c 2025-11-21 11:19:17.088614216 +0100 +++ b/main/main.c 2025-11-21 11:19:47.226411520 +0100 @@ -278,6 +278,17 @@ } else { value = Z_L(1)<<30; /* effectively, no limit */ } + + /* If memory_limit exceeds max_memory_limit, set to max_memory_limit instead. */ + if (value > PG(max_memory_limit)) { + zend_ini_entry *max_mem_limit_ini = zend_hash_str_find_ptr(EG(ini_directives), ZEND_STRL("max_memory_limit")); + entry->value = zend_string_init(ZSTR_VAL(max_mem_limit_ini->value), ZSTR_LEN(max_mem_limit_ini->value), true); + GC_MAKE_PERSISTENT_LOCAL(entry->value); + PG(memory_limit) = PG(max_memory_limit); + + return SUCCESS; + } + if (zend_set_memory_limit(value) == FAILURE) { /* When the memory limit is reset to the original level during deactivation, we may be * using more memory than the original limit while shutdown is still in progress. @@ -293,6 +304,26 @@ } /* }}} */ +static PHP_INI_MH(OnChangeMaxMemoryLimit) +{ + size_t value; + if (new_value) { + value = zend_ini_parse_uquantity_warn(new_value, entry->name); + } else { + value = Z_L(1) << 30; /* effectively, no limit */ + } + + if (zend_set_memory_limit(value) == FAILURE) { + zend_error(E_ERROR, "Failed to set memory limit to %zd bytes (Current memory usage is %zd bytes)", value, zend_memory_usage(true)); + return FAILURE; + } + + PG(max_memory_limit) = value; + zend_alter_ini_entry(ZSTR_KNOWN(ZEND_STR_MEMORY_LIMIT), new_value, PHP_INI_ALL, stage); + + return SUCCESS; +} + /* {{{ PHP_INI_MH */ static PHP_INI_MH(OnSetLogFilter) { @@ -753,7 +784,10 @@ STD_PHP_INI_BOOLEAN("mail.mixed_lf_and_crlf", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, mail_mixed_lf_and_crlf, php_core_globals, core_globals) STD_PHP_INI_ENTRY("mail.log", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateMailLog, mail_log, php_core_globals, core_globals) PHP_INI_ENTRY("browscap", NULL, PHP_INI_SYSTEM, OnChangeBrowscap) - PHP_INI_ENTRY("memory_limit", "128M", PHP_INI_ALL, OnChangeMemoryLimit) + + PHP_INI_ENTRY("max_memory_limit", "-1", PHP_INI_SYSTEM, OnChangeMaxMemoryLimit) + PHP_INI_ENTRY("memory_limit", "128M", PHP_INI_ALL, OnChangeMemoryLimit) + PHP_INI_ENTRY("precision", "14", PHP_INI_ALL, OnSetPrecision) PHP_INI_ENTRY("sendmail_from", NULL, PHP_INI_ALL, NULL) PHP_INI_ENTRY("sendmail_path", DEFAULT_SENDMAIL_PATH, PHP_INI_SYSTEM, NULL) --- a/main/php_globals.h 2025-11-21 11:19:16.948615157 +0100 +++ b/main/php_globals.h 2025-11-21 11:19:47.227411514 +0100 @@ -72,6 +72,7 @@ zend_long serialize_precision; zend_long memory_limit; + zend_long max_memory_limit; zend_long max_input_time; char *error_log; --- a/php.ini-development 2025-11-21 11:19:16.948615157 +0100 +++ b/php.ini-development 2025-11-21 11:19:47.227411514 +0100 @@ -445,6 +445,7 @@ ; Maximum amount of memory a script may consume ; https://php.net/memory-limit memory_limit = 128M +max_memory_limit = -1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Error handling and logging ; --- a/php.ini-production 2025-11-21 11:19:16.949615151 +0100 +++ b/php.ini-production 2025-11-21 11:19:47.228411507 +0100 @@ -447,6 +447,7 @@ ; Maximum amount of memory a script may consume ; https://php.net/memory-limit memory_limit = 128M +max_memory_limit = -1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Error handling and logging ; --- a/Zend/zend_ini.c 2025-11-18 23:17:16.000000000 +0100 +++ b/Zend/zend_ini.c 2025-11-21 11:19:47.228411507 +0100 @@ -247,10 +247,16 @@ zend_unregister_ini_entries_ex(module_number, module_type); return FAILURE; } + + zend_string *prev_value = p->value; + if (((default_value = zend_get_configuration_directive(p->name)) != NULL) && (!p->on_modify || p->on_modify(p, Z_STR_P(default_value), p->mh_arg1, p->mh_arg2, p->mh_arg3, ZEND_INI_STAGE_STARTUP) == SUCCESS)) { - p->value = zend_new_interned_string(zend_string_copy(Z_STR_P(default_value))); + /* Skip assigning the value if the handler has already done so. */ + if (p->value == prev_value) { + p->value = zend_new_interned_string(zend_string_copy(Z_STR_P(default_value))); + } } else { p->value = ini_entry->value ? zend_string_init_interned(ini_entry->value, ini_entry->value_length, 1) : NULL; @@ -388,14 +394,20 @@ zend_hash_add_ptr(EG(modified_ini_directives), ini_entry->name, ini_entry); } + zend_string *prev_value = ini_entry->value; duplicate = zend_string_copy(new_value); if (!ini_entry->on_modify || ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) { - if (modified && ini_entry->orig_value != ini_entry->value) { /* we already changed the value, free the changed value */ - zend_string_release(ini_entry->value); + if (modified && ini_entry->orig_value != prev_value) { /* we already changed the value, free the changed value */ + zend_string_release(prev_value); + } + /* Skip assigning the value if the handler has already done so. */ + if (ini_entry->value == prev_value) { + ini_entry->value = duplicate; + } else { + zend_string_release(duplicate); } - ini_entry->value = duplicate; } else { zend_string_release(duplicate); return FAILURE; --- a/Zend/zend_string.h 2025-11-18 23:17:16.000000000 +0100 +++ b/Zend/zend_string.h 2025-11-21 11:20:30.430120949 +0100 @@ -631,6 +631,7 @@ _(ZEND_STR_COUNT, "count") \ _(ZEND_STR_SENSITIVEPARAMETER, "SensitiveParameter") \ _(ZEND_STR_CONST_EXPR_PLACEHOLDER, "[constant expression]") \ + _(ZEND_STR_MEMORY_LIMIT, "memory_limit") \ typedef enum _zend_known_string_id {