diff --git a/NEWS b/NEWS index d9709800d8ac2..2bc7d6131aa81 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,7 @@ PHP NEWS - OPcache: . Fixed ZTS OPcache build on Cygwin. (cmb) + . Added opcache.file_cache_read_only. (Samuel Melrose) - Output: . Fixed calculation of aligned buffer size. (cmb) diff --git a/UPGRADING b/UPGRADING index f379c46cb5931..9daf6cca22603 100644 --- a/UPGRADING +++ b/UPGRADING @@ -143,6 +143,17 @@ PHP 8.5 UPGRADE NOTES 11. Changes to INI File Handling ======================================== +- Opcache: + . Added opcache.file_cache_read_only to support a read-only + opcache.file_cache directory, for use with read-only file systems + (e.g. read-only Docker containers). + Best used with opcache.validate_timestamps=0, + opcache.enable_file_override=1, + and opcache.file_cache_consistency_checks=0. + Note: A cache generated with a different build of PHP, a different file + path, or different settings (including which extensions are loaded), may be + ignored. + ======================================== 12. Windows Support ======================================== diff --git a/Zend/tests/lazy_objects/setRawValueWithoutLazyInitialization_initialized.phpt b/Zend/tests/lazy_objects/setRawValueWithoutLazyInitialization_initialized.phpt index caa5211f371a5..25580b32ae2cc 100644 --- a/Zend/tests/lazy_objects/setRawValueWithoutLazyInitialization_initialized.phpt +++ b/Zend/tests/lazy_objects/setRawValueWithoutLazyInitialization_initialized.phpt @@ -17,6 +17,7 @@ function test(string $name, object $obj) { $reflector->initializeLazyObject($obj); $reflector->getProperty('a')->setRawValueWithoutLazyInitialization($obj, 'test'); + var_dump($obj->a); var_dump($obj); } @@ -33,9 +34,22 @@ $obj = $reflector->newLazyProxy(function () { test('Proxy', $obj); +$real = new C('foo'); +$obj = $reflector->newLazyProxy(function () use ($real) { + return $real; +}); +$reflector->initializeLazyObject($obj); +$reflector->resetAsLazyProxy($real, function () { + return new C('bar'); +}); +$reflector->initializeLazyObject($real); + +test('Nested Proxy', $obj); + ?> --EXPECTF-- # Ghost +string(4) "test" object(C)#%d (2) { ["a"]=> string(4) "test" @@ -43,12 +57,27 @@ object(C)#%d (2) { NULL } # Proxy +string(4) "test" lazy proxy object(C)#%d (1) { ["instance"]=> object(C)#%d (2) { ["a"]=> - NULL + string(4) "test" ["b"]=> NULL } } +# Nested Proxy +string(4) "test" +lazy proxy object(C)#%d (1) { + ["instance"]=> + lazy proxy object(C)#%d (1) { + ["instance"]=> + object(C)#%d (2) { + ["a"]=> + string(4) "test" + ["b"]=> + NULL + } + } +} diff --git a/Zend/tests/lazy_objects/skipLazyInitialization_initialized_object.phpt b/Zend/tests/lazy_objects/skipLazyInitialization_initialized_object.phpt index bf8ff2094ca15..8c2a52de53511 100644 --- a/Zend/tests/lazy_objects/skipLazyInitialization_initialized_object.phpt +++ b/Zend/tests/lazy_objects/skipLazyInitialization_initialized_object.phpt @@ -36,6 +36,19 @@ $obj = $reflector->newLazyProxy(function () { test('Proxy', $obj); +$real = new C('foo'); +$obj = $reflector->newLazyProxy(function () use ($real) { + return $real; +}); +$reflector->initializeLazyObject($obj); +$reflector->resetAsLazyProxy($real, function () { + var_dump("initializer"); + return new C('bar'); +}); +$reflector->initializeLazyObject($real); + +test('Nested Proxy', $obj); + ?> --EXPECTF-- # Ghost @@ -48,7 +61,7 @@ object(C)#%d (2) { NULL } # Proxy -int(1) +int(2) bool(true) lazy proxy object(C)#%d (1) { ["instance"]=> @@ -59,3 +72,19 @@ lazy proxy object(C)#%d (1) { NULL } } +string(11) "initializer" +# Nested Proxy +int(2) +bool(true) +lazy proxy object(C)#%d (1) { + ["instance"]=> + lazy proxy object(C)#%d (1) { + ["instance"]=> + object(C)#%d (2) { + ["a"]=> + int(2) + ["b"]=> + NULL + } + } +} diff --git a/build/gen_stub.php b/build/gen_stub.php index c32f53678d1e2..1468a0b68b164 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -2086,6 +2086,13 @@ public function getMethodSynopsisElement(array $funcMap, array $aliasMap, DOMDoc $methodSynopsis->appendChild(new DOMText("\n ")); + foreach ($this->attributes as $attribute) { + $modifier = $doc->createElement("modifier", "#[\\" . $attribute->class . "]"); + $modifier->setAttribute("role", "attribute"); + $methodSynopsis->appendChild($modifier); + $methodSynopsis->appendChild(new DOMText("\n ")); + } + foreach ($this->getModifierNames() as $modifierString) { $modifierElement = $doc->createElement('modifier', $modifierString); $methodSynopsis->appendChild($modifierElement); @@ -3870,6 +3877,13 @@ private static function createOoElement( $ooElement->appendChild($doc->createElement('modifier', $modifierOverride)); $ooElement->appendChild(new DOMText("\n$indentation ")); } elseif ($withModifiers) { + foreach ($classInfo->attributes as $attribute) { + $modifier = $doc->createElement("modifier", "#[\\" . $attribute->class . "]"); + $modifier->setAttribute("role", "attribute"); + $ooElement->appendChild($modifier); + $ooElement->appendChild(new DOMText("\n$indentation ")); + } + if ($classInfo->flags & Modifiers::FINAL) { $ooElement->appendChild($doc->createElement('modifier', 'final')); $ooElement->appendChild(new DOMText("\n$indentation ")); diff --git a/docs/source/miscellaneous/stubs.rst b/docs/source/miscellaneous/stubs.rst index 481c26ebc4a0f..385eddc5be72f 100644 --- a/docs/source/miscellaneous/stubs.rst +++ b/docs/source/miscellaneous/stubs.rst @@ -226,7 +226,7 @@ Additional meta information can be attached to functions, with the following PHP - ``@deprecated``: Triggers the usual deprecation notice when the function/method is called. - ``@alias``: If a function/method is an alias of another function/method, then the aliased - function/method name has to be provided as value. E.g. the function ``sizeof()` has the ``@alias + function/method name has to be provided as value. E.g. the function ``sizeof()`` has the ``@alias count`` annotation. - ``@implementation-alias``: This is very similar to ``@alias`` with some semantic differences. @@ -255,7 +255,7 @@ Additional meta information can be attached to functions, with the following PHP In order to generate code which is necessary for registering constants, classes, properties, enums, and traits, use the ``@generate-class-entries`` file-level PHPDoc block. -``@generate-class-entries`` implies ``@generate-function-entries```, so the latter is then +``@generate-class-entries`` implies ``@generate-function-entries``, so the latter is then superfluous. Given the following stub: @@ -573,8 +573,8 @@ Then notice the ``#if (PHP_VERSION_ID >= ...)`` conditions in the generated argi return class_entry; } -The preprocessor conditions are necessary because ``enum``s, ``readonly`` properties, and the -``not-serializable`` flag, are PHP 8.1 features and don't exist in PHP 8.0. +The preprocessor conditions are necessary because enumerations (``enum``), ``readonly`` properties, +and the ``not-serializable`` flag, are PHP 8.1 features and don't exist in PHP 8.0. The registration of ``Number`` is therefore completely omitted, while the ``readonly`` flag is not added for``Elephpant::$name`` for PHP versions before 8.1. diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c index 81f44dafc3fda..f2125750bf4dc 100644 --- a/ext/filter/logical_filters.c +++ b/ext/filter/logical_filters.c @@ -925,13 +925,13 @@ void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ if (flags & FILTER_FLAG_GLOBAL_RANGE) { if ( - (ip[0] == 100 && ip[1] >= 64 && ip[1] <= 127 ) || - (ip[0] == 192 && ip[1] == 0 && ip[2] == 0 ) || - (ip[0] == 192 && ip[1] == 0 && ip[2] == 2 ) || - (ip[0] == 198 && ip[1] >= 18 && ip[1] <= 19 ) || - (ip[0] == 198 && ip[1] == 51 && ip[2] == 100 ) || - (ip[0] == 203 && ip[1] == 0 && ip[2] == 113 ) - ) { + (ip[0] == 100 && ip[1] >= 64 && ip[1] <= 127 ) || + (ip[0] == 192 && ip[1] == 0 && ip[2] == 0 ) || + (ip[0] == 192 && ip[1] == 0 && ip[2] == 2 ) || + (ip[0] == 198 && ip[1] >= 18 && ip[1] <= 19 ) || + (ip[0] == 198 && ip[1] == 51 && ip[2] == 100 ) || + (ip[0] == 203 && ip[1] == 0 && ip[2] == 113 ) + ) { RETURN_VALIDATION_FAILED } } @@ -952,23 +952,24 @@ void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ } } if (flags & FILTER_FLAG_NO_RES_RANGE || flags & FILTER_FLAG_GLOBAL_RANGE) { - if ((ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 - && ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && (ip[7] == 0 || ip[7] == 1)) - || (ip[0] == 0x5f) - || (ip[0] >= 0xfe80 && ip[0] <= 0xfebf) - || (ip[0] == 0x2001 && (ip[1] == 0x0db8 || (ip[1] >= 0x0010 && ip[1] <= 0x001f))) - || (ip[0] == 0x3ff3) - ) { - RETURN_VALIDATION_FAILED - } + if ( + (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && (ip[7] == 0 || ip[7] == 1)) || + (ip[0] == 0x5f) || + (ip[0] >= 0xfe80 && ip[0] <= 0xfebf) || + (ip[0] == 0x2001 && (ip[1] == 0x0db8 || (ip[1] >= 0x0010 && ip[1] <= 0x001f))) || + (ip[0] == 0x3ff3) + ) { + RETURN_VALIDATION_FAILED + } } if (flags & FILTER_FLAG_GLOBAL_RANGE) { - if ((ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0xffff) || - (ip[0] == 0x0100 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) || - (ip[0] == 0x2001 && ip[1] <= 0x01ff) || - (ip[0] == 0x2001 && ip[1] == 0x0002 && ip[2] == 0) || - (ip[0] >= 0xfc00 && ip[0] <= 0xfdff) - ) { + if ( + (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0xffff) || + (ip[0] == 0x0100 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) || + (ip[0] == 0x2001 && ip[1] <= 0x01ff) || + (ip[0] == 0x2001 && ip[1] == 0x0002 && ip[2] == 0) || + (ip[0] >= 0xfc00 && ip[0] <= 0xfdff) + ) { RETURN_VALIDATION_FAILED } } diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index d1be1e7ddcbd4..3cc5097234254 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -46,6 +46,7 @@ #include "zend_accelerator_util_funcs.h" #include "zend_accelerator_hash.h" #include "zend_file_cache.h" +#include "zend_system_id.h" #include "ext/pcre/php_pcre.h" #include "ext/standard/basic_functions.h" @@ -3301,6 +3302,54 @@ static zend_result accel_post_startup(void) #endif accel_shared_globals = calloc(1, sizeof(zend_accel_shared_globals)); } + + /* opcache.file_cache_read_only should only be enabled when all script files are read-only */ + int file_cache_access_mode = 0; + + if (ZCG(accel_directives).file_cache_read_only) { + zend_accel_error(ACCEL_LOG_INFO, "opcache.file_cache is in read-only mode"); + + if (!ZCG(accel_directives).file_cache) { + accel_startup_ok = false; + zend_accel_error_noreturn(ACCEL_LOG_FATAL, "opcache.file_cache_read_only is set without a proper setting of opcache.file_cache"); + return SUCCESS; + } + + /* opcache.file_cache is read only, so ensure the directory is readable */ +#ifndef ZEND_WIN32 + file_cache_access_mode = R_OK | X_OK; +#else + file_cache_access_mode = 04; // Read access +#endif + } else { + /* opcache.file_cache isn't read only, so ensure the directory is writable */ +#ifndef ZEND_WIN32 + file_cache_access_mode = R_OK | W_OK | X_OK; +#else + file_cache_access_mode = 06; // Read and write access +#endif + } + + if ( ZCG(accel_directives).file_cache ) { + zend_accel_error(ACCEL_LOG_INFO, "opcache.file_cache running with PHP build ID: %s", zend_system_id); + + zend_stat_t buf = {0}; + + if (!IS_ABSOLUTE_PATH(ZCG(accel_directives).file_cache, strlen(ZCG(accel_directives).file_cache)) || + zend_stat(ZCG(accel_directives).file_cache, &buf) != 0 || + !S_ISDIR(buf.st_mode) || +#ifndef ZEND_WIN32 + access(ZCG(accel_directives).file_cache, file_cache_access_mode) != 0 +#else + _access(ZCG(accel_directives).file_cache, file_cache_access_mode) != 0 +#endif + ) { + accel_startup_ok = false; + zend_accel_error_noreturn(ACCEL_LOG_FATAL, "opcache.file_cache must be a full path of an accessible directory"); + return SUCCESS; + } + } + #if ENABLE_FILE_CACHE_FALLBACK file_cache_fallback: #endif diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h index 162892bf2c279..3f7eb3bdf008f 100644 --- a/ext/opcache/ZendAccelerator.h +++ b/ext/opcache/ZendAccelerator.h @@ -177,6 +177,7 @@ typedef struct _zend_accel_directives { char *lockfile_path; #endif char *file_cache; + bool file_cache_read_only; bool file_cache_only; bool file_cache_consistency_checks; #if ENABLE_FILE_CACHE_FALLBACK diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 5fe33cb07ef84..da4c4a55d1107 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -11720,6 +11720,32 @@ static int zend_jit_rope(zend_jit_ctx *jit, const zend_op *opline, uint32_t op2_ return 1; } +static int zend_jit_zval_copy_deref_reg(zend_jit_ctx *jit, zend_jit_addr res_addr, uint32_t res_info, zend_jit_addr val_addr, ir_ref type, ir_ref *values) +{ + ir_ref if_type, val; + + if (res_info == MAY_BE_LONG) { + if_type = ir_IF(ir_EQ(type, ir_CONST_U32(IS_LONG))); + ir_IF_TRUE(if_type); + val = jit_ZVAL_ADDR(jit, val_addr); + ir_END_PHI_list(*values, val); + ir_IF_FALSE(if_type); + val = ir_ADD_OFFSET(jit_Z_PTR(jit, val_addr), offsetof(zend_reference, val)); + ir_END_PHI_list(*values, val); + } else if (res_info == MAY_BE_DOUBLE) { + if_type = ir_IF(ir_EQ(type, ir_CONST_U32(IS_DOUBLE))); + ir_IF_TRUE(if_type); + val = jit_ZVAL_ADDR(jit, val_addr); + ir_END_PHI_list(*values, val); + ir_IF_FALSE(if_type); + val = ir_ADD_OFFSET(jit_Z_PTR(jit, val_addr), offsetof(zend_reference, val)); + ir_END_PHI_list(*values, val); + } else { + ZEND_UNREACHABLE(); + } + return 1; +} + static int zend_jit_zval_copy_deref(zend_jit_ctx *jit, zend_jit_addr res_addr, zend_jit_addr val_addr, ir_ref type) { ir_ref if_refcounted, if_reference, if_refcounted2, ptr, val2, ptr2, type2; @@ -14463,9 +14489,16 @@ static int zend_jit_fetch_obj(zend_jit_ctx *jit, } ir_END_list(end_inputs); } else { - if (((res_info & MAY_BE_GUARD) && JIT_G(current_frame) && prop_info) - || Z_MODE(res_addr) == IS_REG) { + if ((res_info & MAY_BE_GUARD) && JIT_G(current_frame) && prop_info) { ir_END_PHI_list(end_values, jit_ZVAL_ADDR(jit, prop_addr)); + } else if ((res_info & MAY_BE_GUARD) && Z_MODE(res_addr) == IS_REG) { + ir_END_PHI_list(end_values, jit_ZVAL_ADDR(jit, prop_addr)); + } else if (Z_MODE(res_addr) == IS_REG) { + prop_type_ref = jit_Z_TYPE_INFO(jit, prop_addr); + + if (!zend_jit_zval_copy_deref_reg(jit, res_addr, res_info & ~MAY_BE_GUARD, prop_addr, prop_type_ref, &end_values)) { + return 0; + } } else { prop_type_ref = jit_Z_TYPE_INFO(jit, prop_addr); diff --git a/ext/opcache/tests/zzz_basic_logging.phpt b/ext/opcache/tests/zzz_basic_logging.phpt index 7490fadc62ef6..a53c6a8db9de1 100644 --- a/ext/opcache/tests/zzz_basic_logging.phpt +++ b/ext/opcache/tests/zzz_basic_logging.phpt @@ -6,6 +6,7 @@ outputs the correct logging at the highest log_verbosity_level --INI-- opcache.enable=1 opcache.enable_cli=1 +opcache.file_cache= opcache.file_cache_only=0 opcache.error_log= opcache.log_verbosity_level=4 diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c index 2ed8155eff773..1b62b757b87d2 100644 --- a/ext/opcache/zend_accelerator_module.c +++ b/ext/opcache/zend_accelerator_module.c @@ -165,20 +165,6 @@ static ZEND_INI_MH(OnUpdateFileCache) if (new_value) { if (!ZSTR_LEN(new_value)) { new_value = NULL; - } else { - zend_stat_t buf = {0}; - - if (!IS_ABSOLUTE_PATH(ZSTR_VAL(new_value), ZSTR_LEN(new_value)) || - zend_stat(ZSTR_VAL(new_value), &buf) != 0 || - !S_ISDIR(buf.st_mode) || -#ifndef ZEND_WIN32 - access(ZSTR_VAL(new_value), R_OK | W_OK | X_OK) != 0) { -#else - _access(ZSTR_VAL(new_value), 06) != 0) { -#endif - zend_accel_error(ACCEL_LOG_WARNING, "opcache.file_cache must be a full path of accessible directory.\n"); - new_value = NULL; - } } } OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); @@ -312,6 +298,7 @@ ZEND_INI_BEGIN() #endif STD_PHP_INI_ENTRY("opcache.file_cache" , NULL , PHP_INI_SYSTEM, OnUpdateFileCache, accel_directives.file_cache, zend_accel_globals, accel_globals) + STD_PHP_INI_BOOLEAN("opcache.file_cache_read_only" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_read_only, zend_accel_globals, accel_globals) STD_PHP_INI_BOOLEAN("opcache.file_cache_only" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_only, zend_accel_globals, accel_globals) STD_PHP_INI_BOOLEAN("opcache.file_cache_consistency_checks" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_consistency_checks, zend_accel_globals, accel_globals) #if ENABLE_FILE_CACHE_FALLBACK @@ -823,6 +810,7 @@ ZEND_FUNCTION(opcache_get_configuration) #endif add_assoc_string(&directives, "opcache.file_cache", ZCG(accel_directives).file_cache ? ZCG(accel_directives).file_cache : ""); + add_assoc_bool(&directives, "opcache.file_cache_read_only", ZCG(accel_directives).file_cache_read_only); add_assoc_bool(&directives, "opcache.file_cache_only", ZCG(accel_directives).file_cache_only); add_assoc_bool(&directives, "opcache.file_cache_consistency_checks", ZCG(accel_directives).file_cache_consistency_checks); #if ENABLE_FILE_CACHE_FALLBACK diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 736d644516a04..fe54c477cea77 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -1100,6 +1100,10 @@ int zend_file_cache_script_store(zend_persistent_script *script, bool in_shm) } #endif + if (ZCG(accel_directives).file_cache_read_only) { + return FAILURE; + } + filename = zend_file_cache_get_bin_file_path(script->script.filename); if (zend_file_cache_mkdir(filename, strlen(ZCG(accel_directives).file_cache)) != SUCCESS) { @@ -1843,7 +1847,9 @@ zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handl zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (info)\n", filename); zend_file_cache_flock(fd, LOCK_UN); close(fd); - zend_file_cache_unlink(filename); + if (!ZCG(accel_directives).file_cache_read_only) { + zend_file_cache_unlink(filename); + } efree(filename); return NULL; } @@ -1853,7 +1859,9 @@ zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handl zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (wrong header)\n", filename); zend_file_cache_flock(fd, LOCK_UN); close(fd); - zend_file_cache_unlink(filename); + if (!ZCG(accel_directives).file_cache_read_only) { + zend_file_cache_unlink(filename); + } efree(filename); return NULL; } @@ -1861,7 +1869,9 @@ zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handl zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (wrong \"system_id\")\n", filename); zend_file_cache_flock(fd, LOCK_UN); close(fd); - zend_file_cache_unlink(filename); + if (!ZCG(accel_directives).file_cache_read_only) { + zend_file_cache_unlink(filename); + } efree(filename); return NULL; } @@ -1873,7 +1883,9 @@ zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handl zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename); } close(fd); - zend_file_cache_unlink(filename); + if (!ZCG(accel_directives).file_cache_read_only) { + zend_file_cache_unlink(filename); + } efree(filename); return NULL; } @@ -1891,7 +1903,9 @@ zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handl zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (mem)\n", filename); zend_file_cache_flock(fd, LOCK_UN); close(fd); - zend_file_cache_unlink(filename); + if (!ZCG(accel_directives).file_cache_read_only) { + zend_file_cache_unlink(filename); + } zend_arena_release(&CG(arena), checkpoint); efree(filename); return NULL; @@ -1905,7 +1919,9 @@ zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handl if (ZCG(accel_directives).file_cache_consistency_checks && (actual_checksum = zend_adler32(ADLER32_INIT, mem, info.mem_size + info.str_size)) != info.checksum) { zend_accel_error(ACCEL_LOG_WARNING, "corrupted file '%s' excepted checksum: 0x%08x actual checksum: 0x%08x\n", filename, info.checksum, actual_checksum); - zend_file_cache_unlink(filename); + if (!ZCG(accel_directives).file_cache_read_only) { + zend_file_cache_unlink(filename); + } zend_arena_release(&CG(arena), checkpoint); efree(filename); return NULL; @@ -1997,6 +2013,10 @@ zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handl void zend_file_cache_invalidate(zend_string *full_path) { + if (ZCG(accel_directives).file_cache_read_only) { + return; + } + char *filename; filename = zend_file_cache_get_bin_file_path(full_path); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index ecfa20fe0c0c8..f5e463699b1b5 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6005,6 +6005,11 @@ ZEND_METHOD(ReflectionProperty, setRawValueWithoutLazyInitialization) RETURN_THROWS(); } + while (zend_object_is_lazy_proxy(object) + && zend_lazy_object_initialized(object)) { + object = zend_lazy_object_get_instance(object); + } + zval *var_ptr = OBJ_PROP(object, ref->prop->offset); bool prop_was_lazy = Z_PROP_FLAG_P(var_ptr) & IS_PROP_LAZY; @@ -6048,7 +6053,10 @@ ZEND_METHOD(ReflectionProperty, skipLazyInitialization) RETURN_THROWS(); } - bool prop_was_lazy = (Z_PROP_FLAG_P(OBJ_PROP(object, ref->prop->offset)) & IS_PROP_LAZY); + while (zend_object_is_lazy_proxy(object) + && zend_lazy_object_initialized(object)) { + object = zend_lazy_object_get_instance(object); + } zval *src = &object->ce->default_properties_table[OBJ_PROP_TO_NUM(ref->prop->offset)]; zval *dst = OBJ_PROP(object, ref->prop->offset); @@ -6063,7 +6071,7 @@ ZEND_METHOD(ReflectionProperty, skipLazyInitialization) ZVAL_COPY_PROP(dst, src); /* Object becomes non-lazy if this was the last lazy prop */ - if (prop_was_lazy && zend_object_is_lazy(object) + if (zend_object_is_lazy(object) && !zend_lazy_object_initialized(object)) { if (zend_lazy_object_decr_lazy_props(object)) { zend_lazy_object_realize(object); diff --git a/ext/standard/info.c b/ext/standard/info.c index aa03be761c052..216bf9d62e45e 100644 --- a/ext/standard/info.c +++ b/ext/standard/info.c @@ -271,7 +271,9 @@ static char* php_get_windows_name() major = "Windows 10"; } } else { - if (osvi.dwBuildNumber >= 20348) { + if (osvi.dwBuildNumber >= 26100) { + major = "Windows Server 2025"; + } else if (osvi.dwBuildNumber >= 20348) { major = "Windows Server 2022"; } else if (osvi.dwBuildNumber >= 19042) { major = "Windows Server, version 20H2"; diff --git a/php.ini-development b/php.ini-development index 5adb11d215697..31353facdb394 100644 --- a/php.ini-development +++ b/php.ini-development @@ -1766,6 +1766,15 @@ ldap.max_links = -1 ; SHM reset. The default "" disables file based caching. ;opcache.file_cache= +; Enables or disables read-only mode for the second level cache directory. +; It should improve performance for read-only containers, +; when the cache is pre-warmed and packaged alongside the application. +; Best used with `opcache.validate_timestamps=0`, `opcache.enable_file_override=1` +; and `opcache.file_cache_consistency_checks=0`. +; Note: A cache generated with a different build of PHP, a different file path, +; or different settings (including which extensions are loaded), may be ignored. +;opcache.file_cache_read_only=0 + ; Enables or disables opcode caching in shared memory. ;opcache.file_cache_only=0 diff --git a/php.ini-production b/php.ini-production index e4ffc1084b13a..e4fbd6e785f86 100644 --- a/php.ini-production +++ b/php.ini-production @@ -1768,6 +1768,15 @@ ldap.max_links = -1 ; SHM reset. The default "" disables file based caching. ;opcache.file_cache= +; Enables or disables read-only mode for the second level cache directory. +; It should improve performance for read-only containers, +; when the cache is pre-warmed and packaged alongside the application. +; Best used with `opcache.validate_timestamps=0`, `opcache.enable_file_override=1` +; and `opcache.file_cache_consistency_checks=0`. +; Note: A cache generated with a different build of PHP, a different file path, +; or different settings (including which extensions are loaded), may be ignored. +;opcache.file_cache_read_only=0 + ; Enables or disables opcode caching in shared memory. ;opcache.file_cache_only=0