From 5771faf62c1ff410b070fb03a0a4af5144fde3d6 Mon Sep 17 00:00:00 2001 From: Greg Harmon <9166584+gregharmon@users.noreply.github.com> Date: Sat, 14 Feb 2026 23:40:43 -0500 Subject: [PATCH 1/2] Fix GH-17770: allow file_cache writes with JIT enabled --- .../tests/gh17770_jit_file_cache_write.phpt | 95 +++++++++++++++++++ ext/opcache/zend_file_cache.c | 31 +++++- 2 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 ext/opcache/tests/gh17770_jit_file_cache_write.phpt diff --git a/ext/opcache/tests/gh17770_jit_file_cache_write.phpt b/ext/opcache/tests/gh17770_jit_file_cache_write.phpt new file mode 100644 index 0000000000000..d491dc673e442 --- /dev/null +++ b/ext/opcache/tests/gh17770_jit_file_cache_write.phpt @@ -0,0 +1,95 @@ +--TEST-- +GH-17770: opcache.file_cache writes with JIT enabled +--EXTENSIONS-- +opcache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit=tracing +opcache.jit_buffer_size=16M +opcache.file_cache={TMP}/gh17770-opcache-file-cache +opcache.file_cache_consistency_checks=0 +--SKIPIF-- + +--FILE-- +isDir()) { + @rmdir($entry->getRealPath()); + } else { + @unlink($entry->getRealPath()); + } + } + + @rmdir($dir); +} + +$cacheDir = ini_get('opcache.file_cache'); +removeDirRecursive($cacheDir); +@mkdir($cacheDir, 0777, true); + +$tmpPhpFile = dirname($cacheDir) . '/gh17770-opcache-target.php'; +file_put_contents($tmpPhpFile, "isFile()) { + $hasCacheFile = true; + break; + } +} + +echo $hasCacheFile ? "OK\n" : "FAIL: opcache file cache is empty\n"; +?> +--CLEAN-- +isDir()) { + @rmdir($entry->getRealPath()); + } else { + @unlink($entry->getRealPath()); + } + } + + @rmdir($dir); +} + +$cacheDir = ini_get('opcache.file_cache'); +removeDirRecursive($cacheDir); +@unlink(dirname($cacheDir) . '/gh17770-opcache-target.php'); +?> +--EXPECT-- +OK diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 3114e5b92712c..4c92c7723a5b9 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -637,6 +637,10 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra /* relative extended_value don't have to be changed */ break; } +#endif +#ifdef HAVE_JIT + /* Re-normalize handlers before serializing to avoid storing JIT runtime handlers. */ + ZEND_VM_SET_OPCODE_HANDLER(opline); #endif zend_serialize_opcode_handler(opline); opline++; @@ -1152,15 +1156,19 @@ int zend_file_cache_script_store(zend_persistent_script *script, bool in_shm) char *filename; zend_file_cache_metainfo info; void *mem, *buf; - #ifdef HAVE_JIT - /* FIXME: dump jited codes out to file cache? */ - if (JIT_G(on)) { - return FAILURE; + bool orig_jit_on = JIT_G(on); + + /* File cache stores bytecode/metadata only; JIT code is rebuilt at runtime. */ + if (orig_jit_on) { + JIT_G(on) = 0; } #endif if (ZCG(accel_directives).file_cache_read_only) { +#ifdef HAVE_JIT + JIT_G(on) = orig_jit_on; +#endif return FAILURE; } @@ -1169,6 +1177,9 @@ int zend_file_cache_script_store(zend_persistent_script *script, bool in_shm) if (zend_file_cache_mkdir(filename, strlen(ZCG(accel_directives).file_cache)) != SUCCESS) { zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot create directory for file '%s', %s\n", filename, strerror(errno)); efree(filename); +#ifdef HAVE_JIT + JIT_G(on) = orig_jit_on; +#endif return FAILURE; } @@ -1178,12 +1189,18 @@ int zend_file_cache_script_store(zend_persistent_script *script, bool in_shm) zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot create file '%s', %s\n", filename, strerror(errno)); } efree(filename); +#ifdef HAVE_JIT + JIT_G(on) = orig_jit_on; +#endif return FAILURE; } if (zend_file_cache_flock(fd, LOCK_EX) != 0) { close(fd); efree(filename); +#ifdef HAVE_JIT + JIT_G(on) = orig_jit_on; +#endif return FAILURE; } @@ -1227,6 +1244,9 @@ int zend_file_cache_script_store(zend_persistent_script *script, bool in_shm) efree(mem); zend_file_cache_unlink(filename); efree(filename); +#ifdef HAVE_JIT + JIT_G(on) = orig_jit_on; +#endif return FAILURE; } @@ -1237,6 +1257,9 @@ int zend_file_cache_script_store(zend_persistent_script *script, bool in_shm) } close(fd); efree(filename); +#ifdef HAVE_JIT + JIT_G(on) = orig_jit_on; +#endif return SUCCESS; } From 3edc2b7087a1a717f6aa2895c3c875fc9e8c8724 Mon Sep 17 00:00:00 2001 From: Greg Harmon <9166584+gregharmon@users.noreply.github.com> Date: Sun, 15 Feb 2026 13:27:18 -0500 Subject: [PATCH 2/2] Fix GH-17770 test file_cache path handling on Windows --- .../tests/gh17770_jit_file_cache_write.phpt | 36 +++++++------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/ext/opcache/tests/gh17770_jit_file_cache_write.phpt b/ext/opcache/tests/gh17770_jit_file_cache_write.phpt index d491dc673e442..f372d98468076 100644 --- a/ext/opcache/tests/gh17770_jit_file_cache_write.phpt +++ b/ext/opcache/tests/gh17770_jit_file_cache_write.phpt @@ -1,5 +1,12 @@ --TEST-- GH-17770: opcache.file_cache writes with JIT enabled +--SKIPIF-- + --EXTENSIONS-- opcache --INI-- @@ -7,17 +14,8 @@ opcache.enable=1 opcache.enable_cli=1 opcache.jit=tracing opcache.jit_buffer_size=16M -opcache.file_cache={TMP}/gh17770-opcache-file-cache +opcache.file_cache="{PWD}/gh17770_cache" opcache.file_cache_consistency_checks=0 ---SKIPIF-- - --FILE-- isFile()) { - $hasCacheFile = true; - break; - } -} +$hasCacheFile = opcache_is_script_cached_in_file_cache($tmpPhpFile); echo $hasCacheFile ? "OK\n" : "FAIL: opcache file cache is empty\n"; ?> @@ -87,9 +76,8 @@ function removeDirRecursive(string $dir): void { @rmdir($dir); } -$cacheDir = ini_get('opcache.file_cache'); -removeDirRecursive($cacheDir); -@unlink(dirname($cacheDir) . '/gh17770-opcache-target.php'); +removeDirRecursive(__DIR__ . '/gh17770_cache'); +@unlink(__DIR__ . '/gh17770-opcache-target.php'); ?> --EXPECT-- OK